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

Автор RNR, история, 5 лет назад, По-английски

Many times while debugging in contests, to trace the elements I use multiple functions like following,

Code

It's good if I can trace all using one method, I was looking for one single function/method for printing variables, vectors, etc.

Watching Errichto streams and this tweet during ICPC live stream, I got interested in this template but couldn't understand much of this, I wanted to use this template, know how to use, but don't understand why it works, and I try not to put things that I don’t understand in my template, Can you please help me in understanding this

Original code

We can generally use this like this

debug() << imie(c * 2) imie(min(a, b)) imie(a != b);
debug() << "add" imie(i) imie(A) imie(diff);
debug() << imie(V); // V is a vector
debug() << imie(a) imie(h1) imie(h2) imie(my_dist) imie(path) imie(vertices); // int a,h1,h2; vector path;
debug() << "Zlaczacz::Ogarnij(" imie(a) imie(b) ") = " << res; // pair<vector<int>, vector<int>> res; int a,b;

So I have elaborated code and it resulted in the following

#define sim template < class c
#define ris return * this
#define dor > debug & operator <<
#define eni(x) sim > typename \
  enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(c i) {

/*sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);*/

template < class c > struct rge { c b, e; };
template < class c > rge<c> range(c i, c j) { return rge<c>{i, j}; }
template < class c > auto dud(c* x) -> decltype(cerr << *x, 0);
template < class c > char dud(...);

struct debug {
	~debug() { cerr << endl; }
	
	//eni(!=) cerr << boolalpha << i; ris; }        Part 1
	
	template < class c > typename \
	enable_if<sizeof dud<c>(0) != 1, debug&>::type operator<<(c i) {
		cerr << boolalpha << i;
		return * this;
	}
	
	//eni(==) ris << range(begin(i), end(i)); }     Part 2
	
	template < class c > typename \
	enable_if<sizeof dud<c>(0) == 1, debug&>::type operator<<(c i) {
		return * this << range(begin(i), end(i)); 
	}
	
	/*sim, class b dor(pair < b, c > d) {           Part 3
		ris << "(" << d.first << ", " << d.second << ")";
	}*/
	
	template < class c, class b > debug & operator << (pair < b, c > d) {
		return * this << "(" << d.first << ", " << d.second << ")";
	}
	
	/*sim dor(rge<c> d) {                           Part 4
		*this << "[";
		for (auto it = d.b; it != d.e; ++it)
			*this << ", " + 2 * (it == d.b) << *it;
		ris << "]";
	}*/
	
	template < class c > debug & operator <<(rge<c> d) {
		*this << "[";
		for (auto it = d.b; it != d.e; ++it)
			*this << ", " + 2 * (it == d.b) << *it;
		return * this << "]";
	}
	
}
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "

std::boolalpha is an I/O manipulator, it causes bool values to display as true or false. decltype inspects the declared type of an entity or the type and value category of expression.

When the macro is invoked, all the tokens in its argument list after the last named argument (this macro has none), including any commas, become the variable argument. This sequence of tokens replaces the identifier __VA_ARGS__ in the macro body wherever it appears. You may use the # and ## operators to stringify the variable argument or to paste its leading or trailing token with another token — Ref

template< bool B, class T = void >
struct enable_if;
/* If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef. */
template <typename T>
struct enable_if<true, T> {
  typedef T type;
};

Can someone please explain what eni(x), dud does? and how does eni relate to Part 1 and Part 2? I didn't understand eni(x) and dud.

How rge is used in range?

I understand that Part 4 is to iterate through containers, is Part 3 for pairs?

Thank you in advance.

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

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

You don't have to write imie everywhere. It's just that imie changes a variable into its name and value. Plus it's a class so you can create it, print some things, and then a newline character will be printed in the destructor.

range allows printing arrays. Try debug() << range(a, a+n) or debug() << imie(range(a, a+n)).

is Part 3 for pairs?

yes

Summoning mareksom in case of more questions, and tnowak because maybe he would like to explain his shorter/improved templates.

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

    Which flags do we have to use locally to compile the code in the given template?

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

      These are flags which Errichto uses g++ -std=c++17 -Wshadow -Wall -o a a.cpp -fsanitize=address -fsanitize=undefined -D_GLIBCXX_DEBUG -g

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

      The gcc option -DNAME defines a preprocessor macro NAME from the command line.

      So you should use -DLOCAL

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

    eni(x) and dud are sfinae tricks to specify when to use which function (which operator<<). The "part 1" tells the compiler to use this operator<< for any objects that can use cout, and "part 2" for other objects — it assumes they are containers with iterators. rge stores those begin/end iterators, and the "part 4" prints the container.

»
5 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
  • How does *this << ", " + 2 * (it == d.b) << *it; play role in excluding ',' at start of printing?
  • What does decltype(cerr << *x, 0); doing? What will be sizeof dud<c>(0) when we pass different objects to print? What is the role of 0 in decltype(cerr << *x, 0); ?
  • »
    »
    5 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    I have the following:

    // ii is pair<int,int>
    debug()<<imie(sizeof dud<ii>(0));
    [sizeof dud<ii>(0): 1]
    
    // vi is vector<int>
    debug()<<imie(sizeof dud<vi>(0));
    [sizeof dud<vi>(0): 1]
    

    Basically for printable objects, we have other than 1, But can't understand how dud is returning those values using dud(c* x) -> decltype(cerr << *x, 0); ?

    Any help would be greatly appreciated.

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

    Don't analyze so hacky code if you don't know C++ well.

    cout << "abcd" + x prints "abcd" starting from x-th character.

    a, b runs a and then runs b and returns its value.

    And I won't answer all your questions because I don't understand our templates. mareksom does.

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

      There is a warning if we use "abcd" + x

      BINFLIP.cpp:100:19: warning: adding 'int' to a string does not append to the string [-Wstring-plus-int]
          *this << ", " + 2 * (it == d.b) << *it;
                   ~~~~~^~~~~~~~~~~~~~~~~
      BINFLIP.cpp:93:13: note: in instantiation of function template specialization 'debug::operator<<<std::__wrap_iter<int *>>' requested here
      eni(==) ris << range(begin(i), end(i)); }
                  ^
      BINFLIP.cpp:160:39: note: in instantiation of function template specialization 'debug::operator<<<std::vector<int>>' requested here
              debug() << imie(i) imie(last) imie(V);
                                            ^
      BINFLIP.cpp:107:45: note: expanded from macro 'imie'
      #define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "
                                                  ^
      BINFLIP.cpp:100:19: note: use array indexing to silence this warning
          *this << ", " + 2 * (it == d.b) << *it;
                        ^
                   &    [                ]
      1 warning generated.
      

      To fix this, I've changed *this << ", " + 2 * (it == d.b) << *it; to *this << string(", ").substr(2 * (it == d.b)) << *it;

      Or just use -Wno-string-plus-int flag while compiling

      Do you have any suggestions?

  • »
    »
    5 лет назад, # ^ |
      Проголосовать: нравится +17 Проголосовать: не нравится
    template < class c > auto dud(c* x) -> decltype(cerr << *x, 0);
    template < class c > char dud(...);
    

    The first line is a function template declaration (there is no definition), which takes as an argument a pointer to any type c and returns an integer (decltype(0) is int, so decltype(anything at all, 0) is int).

    The second line is a function template declaration (again, there is no definition), which takes any number of arguments of any type (like printf) and returns a character (char).

    When you write dud<c>, the compiler will try to resolve this template to one of these two functions, based on the type c. The "ellipsis thing" has the lowest priority when it comes to resolving template functions. So, if the first declaration is well-formed, then it will be chosen, and if not, then the second declaration will be chosen. The cerr << *x part is responsible for this resolution: the first template with type c is well-formed if and only if the type c can be printed to cerr. To better understand the details, you should read about SFINAE.

    When you write sizeof dud<c>(0) (or better sizeof dud<c>(nullptr)), you get a size of 1 if dud<c> returns characters, or a size greater than 1 (usually 4) if dud<c> returns integers. So, you get 1 if objects of type c cannot be printed to cerr or something greater than 1 if objects of type c can be printed to cerr.

    The function dud<c> takes as an argument a pointer to type c, not an object of type c, so that you can easily call it this way: dud<c>(nullptr) or dud<c>(0). If you had to pass an object, you would need to call a constructor and assume that this constructor exists and is public.

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

      Thank you very much.

      To add:-

      The operands of the operators typeid, sizeof, noexcept, and decltype (since C++11) are expressions that are not evaluated since these operators only query the compile-time properties of their operands.

      The sizeof operator permits us to returns the size in bytes of a type or an expression at compilation time. sizeof operator does a "fake evaluation" of the expression that you pass to it.

      SFINAE on the return type, where the expression uses the comma operator, whose left subexpression is the one that is being examined (cast to void to ensure the user-defined operator comma on the returned type is not selected), and the right subexpression has the type that the function is supposed to return.

      Reference