Блог пользователя dendi239

Автор dendi239, 4 года назад, По-английски

Hello, codeforces!

I would like to tell you about tips'n'tricks I use in C++ sources. Some of them require a strong C++ background, some not, but all of them are aimed to reduce the time you spend on basic things like reading input/debugging/etc, so it might be useful for your next template.

After a few hours of working on this post, I decided to split it into several more focused articles -- it's so hard to write about everything I planned, cover all nuances, and don't make it super-big.

For simple tasks, you may need to write very little code solving the problem and the biggest part of the source will be about reading values. Thus, declaring and reading variables seem like things that need to be simplified. Is there a way to shorten the following?

int n, m, k, t, q;
cin >> n >> m >> k >> t >> q;

There're a few ways, more or less flexible, I'll show one of them, you can find another, for example, here:

int READ(n, m, k, t, q);
// here variables n, m, k, t, q are declared and read from stdin.

In the following section we will use variadic number of arguments for functions and macros. It might be a bit hard to understand, but you still can copy/paste the result and skip this section.

How to achieve this? Well, we need to do two things: declare variables and read them. Let's start from scratch:

#define READ(...) __VA_ARGS__; read(__VA_ARGS__)

int READ(x, y, z);
// expands to:
int x, y, z; read(x, y, z);

Where read is a function that reads all arguments given to it. But how to do it? We need a function that takes as many arguments as you want and reads every single one by reference from stdin. However, we can't simply iterate over them through a cycle:

for (auto &arg : args) cin >> arg;

We can't since there's no collection or smth else. But for simple and arguably short versions we could write the following:

void read() {}

template <class T> 
void read(T &value) {
  cin >> value;
}

template <class T1, class T2>
void read(T1 &x1, T2 &x2) {
  cin >> x1 >> x2;
}

// and so on

It seems a little messy, isn't it? But what if we need to read 13 arguments and have a macro that works fine with only up to 10? Let's use variadic template parameters. We can use the simple trick with recursion: if there're parameters, take one of them, read it and repeat with the rest:

template <class ...Args>
void read(Args &...args);

template <>
void read() {}

template <class Arg, class ...Args>
void read(Arg &arg, Args &...args) {
  cin >> arg;
  read(args...);
}

Since C++17 we can do it slightly shorter and prettier with parameter pack:

template <class ...Args>
void read(Args &...args) {
  (cin >> ... >> args);
}

Let's combine it all together:

template <class ...Args>
auto &read(Args &...args) { return (cin >> ... >> args); }

#define READ(...) __VA_ARGS__; read(__VA_ARGS__)

Note return type: it will be deduced as istream & so you can read additional values:

vector<int> ws(m);
for (int e = 0; e < m; ++e) {
  int READ(u, v) >> ws[e];
  connect(--u, --v);
}

Types with no defined input operator, vector

To work with other types, you can define operator>>(istream &is, YourType &your_variable) to use it with cin or READ macro. For example, you can define operator>> for vector. Often in problems, you need to read the size of a vector, followed by its elements. I could suggest using READ_CTOR macro for this:

template <class T> istream &operator>>(istream &is, vector<T> &vec);

#define READ_CTOR(value, ...) value(__VA_ARGS__); cin >> value

// usage
int READ(n);
vector<int> READ_CTOR(xs, n);

READ_CTOR can be used to initialize a variable with a constructor and then read it.

Example

You could look at all these tips in action here.

Credits

If you have any thoughts to extend or update this code, feel free to leave a comment below. What tricks do you use for reading values?

I would like to thank:

  • viskonsin for help in editing this article
  • Golovanov399 and HosseinYousefi for inspiring me to write this article (more will come)
  • MikeMirzayanov for great Codeforces platform (haven't tried Polygon yet:)
  • Whole codeforces community for sharing their great code -- it made me try to improve my template.
  • Проголосовать: нравится
  • +88
  • Проголосовать: не нравится

»
4 года назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

Auto comment: topic has been updated by dendi239 (previous revision, new revision, compare).

»
4 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

How would you do write instead of read?

  • »
    »
    4 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    You could simply use similar to read function:

    template <class ...Args>
    auto &write(const Args &...args) {
      return ((cout << " " << args), ...);
    }
    
»
4 года назад, # |
Rev. 8   Проголосовать: нравится 0 Проголосовать: не нравится

I am trying to compile your solution to 1367B using the following : g++ -std=c++17 ToBeOrange.cpp

I am getting the following error :

ToBeOrange.cpp: In function ‘auto& read(As& ...)’:
ToBeOrange.cpp:5:63: error: expected primary-expression before ‘...’ token
 template <class ...As> auto &read(As &...as) { return (cin >> ... >> as); }
                                                               ^
ToBeOrange.cpp:5:63: error: expected ‘)’ before ‘...’ token
ToBeOrange.cpp: In instantiation of ‘auto& read(As& ...) [with As = {int}]’:
ToBeOrange.cpp:15:9:   required from here
ToBeOrange.cpp:5:75: error: no return statements in function returning ‘auto&’
 template <class ...As> auto &read(As &...as) { return (cin >> ... >> as); }
                                                                           ^
ToBeOrange.cpp:5:75: note: only plain ‘auto’ return type can be deduced to ‘void’

Any ideas how should I fix this ?