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

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

Hi Codeforces,

Here's a blog on easy IO I've meant to write for quite some time and was finally able to put together.

Motivation: For many contests, all code needs to be produced from scratch in contest, i.e. no templates unless you are willing to write them in contest. This means that if you are going to have a template, it needs to be planned out aptly to minimize the time it takes to write.

One of the best things about templates is the ability to have easy IO functions. This means writing re(a,b,c,d) instead of cin >> a >> b >> c >> d and similar (such as something with scanf and formatters). The problem is, creating such functions from scratch can take a very long time if done traditionally (blatant recursive calls), and this also loses some efficiency in runtime.

But I recently came upon a novel method that can streamline this very process called operator forwarding.

The process is extremely simple: you can write the entire set of functions in only four lines of code and no extra hassle!

The first thing you need to know is that functions like re and pr need to be based on variadic template arguments (i.e., a comma separated chain). We can make a reusable macro like the below to handle any desired function x. The semantics here are that the arguments are given as universal rvalue references for flexibility.

#define m1(x) template<class T, class... U> void x(T&& a, U&&... b) 

But how do we write the function bodies? This is where we notice that the arguments are in the form of a parameter pack, meaning that we can use perfect forwarding. This avoids recursion, not only speeding up runtime by avoiding recursive call overhead but even speeding up coding time (one line instead of many). In particular, we can make a second macro that hosts our forwarding pack:

#define m2(x) (int[]){(x forward<U>(b),0)...}

Now, based on just these two macros, we can write the generalized easy IO with surprising ease:

m1(pr) { cout << forward<T>(a);  m2(cout << " " <<); cout << "\n"; } 
m1(re) { cin >> forward<T>(a); m2(cin >>); }

Just like that, we have the capacity to write an IO template extremely quickly, in only four lines total! These lines are not only easy to memorize but also easy to understand.

If this wasn't enough, note that the generic nature of the macros m1 and m2 enables us to write simple functions that take comma-separated parameters for anything in C++ that is inconvenient when time is of essence (overloaded operators).

Hope this was helpful!

PassionUnlimited

  • Проголосовать: нравится
  • +31
  • Проголосовать: не нравится

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

This blog was basically a copy-paste from this blog.

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

Is there any way that modifies this code to initialize as well as cin the variable using m1(re) for some particular type of variable like int or string? like calling if we made m1 for int then it do

m1(a); ---->>> int a; cin>>a;

If yes then can anyone please provide its code?

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

    Variant 1 (works starting with C++11, predefined variables):

    void Read() {}
    
    template<typename First, typename ...Rest>
    void Read(First& first, Rest&... rest) {
        cin >> first;
        Read(rest...);
    }
    // Usage
    int a;
    string str;
    Read(a, str);
    

    Variant 2 (works starting with C++17, predefined variables):

    template<typename ...Args>
    void Read(Args&... args) {
        (cin >> ... >> args);
    }
    // Usage
    int a;
    string str;
    Read(a, str);
    

    Variant 3 — think of it as a joke (works starting with C++17, variables defined right there):

    template<typename T>
    T Read() {
        T t;
        cin >> t;
        return t;
    }
    
    template<typename First, typename Second, typename ...Args>
    tuple<First, Second, Args...> Read() {
        tuple<First, Second, Args...> res;
        apply(
            [](auto&... arg) { ((cin >> arg), ...); },
            res
        );
        return res;
    }
    
    //Usage
    auto [i, s] = Read<int, string>();
    auto j = Read<int>();
    

    Con: you can't use read variables within lambda :(

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

      Thanks a lot for reply, But unfortunately I was looking for something that can replace this code into any single command.

      #define i1(x)             int x;cin>>x;
      #define i2(x,y)           int x,y;cin>>x>>y;
      #define i3(x,y,z)         int x,y,z;cin>>x>>y>>z;
      #define i4(x,y,z,w)       int x,y,z,w;cin>>x>>y>>z>>w;
      

      ...and so on...

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

        You can combine Variant 2 with variadic macros:

        #define read_ints(...) int __VA_ARGS__; Read(__VA_ARGS__);