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

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

Today, I was seeing tourist's submissions. He uses a very cool way to print the variables for the purpose of debugging. The function debug_out lists all variable names, followed by their values. I found it very interesting and convenient.

However, when you would like to print a long list of variables, it might be hard to match variables and their corresponding values. I made a few changes, and I would like to share the code with you. Please note that you need to define XOX (-D XOX should do the job). Therefore, when you are submitting your code, you do not need to remove or comment out lines including debug. Hope you'll find it useful.

#include <bits/stdc++.h>
using namespace std;
vector<string> vec_splitter(string s) {
	s += ',';
	vector<string> res;
	while(!s.empty()) {
		res.push_back(s.substr(0, s.find(',')));
		s = s.substr(s.find(',') + 1);
	}
	return res;
}
void debug_out(
vector<string> __attribute__ ((unused)) args,
__attribute__ ((unused)) int idx, 
__attribute__ ((unused)) int LINE_NUM) { cerr << endl; } 
template <typename Head, typename... Tail>
void debug_out(vector<string> args, int idx, int LINE_NUM, Head H, Tail... T) {
	if(idx > 0) cerr << ", "; else cerr << "Line(" << LINE_NUM << ") ";
	stringstream ss; ss << H;
	cerr << args[idx] << " = " << ss.str();
	debug_out(args, idx + 1, LINE_NUM, T...);
}
#ifdef XOX
#define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)
#else
#define debug(...) 42
#endif
int main() {
	int x = -1, y = 10000;
	double z = 0.2;
	string s = "beginner1010";
	long long b = 1LL << 60;
	
	debug(x, y, z, s, b);
	
	double aux = 1010.0;
	string code = "code";
	
	debug(code + "forces",-aux / 10 * 2.3);
	return 0;
}

Results:

Line(34) x = -1,  y = 10000,  z = 0.2,  s = beginner1010,  b = 1152921504606846976
Line(39) code + "forces" = codeforces, -aux / 10 * 2.3 = -232.3

UPD 1: The code is further improved to handle the case when an operation is applied to variables in debug. The line number is added, and spookywooky's suggestion is applied. Please see the examples. Thank you all for your suggestions.

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

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

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

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

Not as cool, but I use simple

#define watch(x) cout << (#x) << " is " << (x) << endl

to debug single variables

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

void debug_out(vector<string> __attribute__ ((unused)) args, __attribute__ ((unused)) int idx) { cerr << endl; }

to get rid of the compiler warnings caused by -Wunused-parameter

»
4 года назад, # |
Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится
it's better, isn't it?
»
4 года назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

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

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

Can we do some modification in above template so that we can also print STl containers like map, vector, set, multiset (also map<pair<int,pair<int,int>>, int> type things).

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

    pretty sure you will have to use iterators there (so i dont think it will work by modifying these functions little bit)

    maybe use something like this to determine whether its an STL container: https://github.com/cpp-libraries/is_stl_container_type_trait/blob/master/is_stl_container.hpp

    then you have to decide if you have to iterate with pairs (for map/unordered_map) also some containers don't have iterators (like queue/deque/stack)

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

    I think the best way to do this is to define a custom operator<< for STL containers. The magic sauce here is weeding out std::string, but this is relatively easy if you know C++ template metaprogramming.

    Take a look at this code: 76534814. Notice that I have print(a); in main, and a is defined as std::vector<std::string>. This line prints something like this to stderr:

    [154873296, 386592714, 729641835, 863725149, 975314628, 412968357, 631457982, 598236471, 247189563]

    It works for any typical STL container, for example you can have something weird like std::unordered_map<std::pair<int, int>, std::vector<std::string>> b and printing it is still as easy as print(b);

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

This snippet is not printing anything on the console in my PC....No errors, no warnings although... What should I do??

Please help me out!!

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

Here is a function for print any stl container that has begin() and end():

Spoiler

Example:

vector<int> vec{1,2,3,4,5,6};
cout << vec << endl;
set<int> s{3,4,5,6};
cout << s << endl;
//Output
//[1, 2, 3, 4, 5, 6]
//[3, 4, 5, 6]

Work for map too if you overload operator<< for pair

Spoiler
//
map<int,string> m = {
 {1, "awesome"},
 {2, "c++"}
};
cout << m << endl;
//[{1, awesome}, {2, c++}]
map<pair<int,int>,pair<bool,float>> m2 = {
 {{1, 2}, {true, 3.0}},
 {{3, 4}, {false, 9.f}}
};
cout << m2 << endl;
//[{{1, 2}, {1, 3}}, {{3, 4}, {0, 9}}]


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

    We can get rid of vec_splitter() and debug_out() (they won't work if one ARG of #_VA_ARGS__ contain ',')_
    write less code and faster version by using a macro trick

    Spoiler
»
4 года назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится
#ifdef XOX
#define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)
#else
#define debug(...) 42
#endif

Here what is meant by

#define debug(...) 42 ???

Why we defined 42 as debug(...)?

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

    because we dont want to print anything in std err while submitting code.** XOX define at local pc (your pc , we need to define this)**

    int a = 10;
    debug(a);
    

    this will print 10 on std::err , but when you submit on cf XOX is not define so debug(a) replace with 42

    int a = 10;
    42;
    
  • »
    »
    4 года назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    Because 42 is the answer to Life, Universe, and everything, Duh!

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

Hello,

When I do:

int test[10] = {0};
debug(test);

He sends me back:

test = 0x7fffed0acd20

How can I display all my board on the line instead of its address?

Thank you

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

    you can't print this type of array becz size is unknown when it's passed through any function so always use vector

    code

    you have to compile it with LOCAl argument

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

To support STL containers and line number

code
output

just add -D LOCAL flag

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

    I have put #define LOCAL in the code and run it. What I have got is: [x: 10] [y: 11] [v: [1, 2, 3]] [mp: [(12, 13)]] [~ -->Line: 48] ~~ (~ is replacement for some strange symbol that I could not paste into here) Any idea how to fix it?

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

      It's from the backspace \b. I used it to make format the line number a little bit different. you can just remove them from line 34 and line 26.

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

    How to add -D LOCAL flag?

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

      code.cpp

      #include <bits/stdc++.h>
      using namespace std;
      int main()
      {
          #ifdef LOCAL
          {
              cout << "LOCAL defined.";
          }
          #endif
          return 0;
      }
      

      g++ -DLOCAL code.cpp && a.exe

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

    Kavaliro, Thanks a lot for the great debugging template. Yours is the best debugging template I have come across. What can I do to print the line number in the beginning instead of end of the dbg output ?

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

      Just swap arguments

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

how can I print the debug output to a file? currently it prints to terminal

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

Unfortunately this doesn't work when you debug a function call with more than one argument because the commas trick the vec splitter. Guess I'll go back to my not so cool

#define debug(x) cerr << "Line " << __LINE__ << ": " << #x << " = " << to_string(x) << endl
  • »
    »
    4 года назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    I recommend using this debugging template

    Code

    You can debug function calls, variables, STL containers, almost anything.

    Make sure to add -DLOCAL to your compiler arguments. When you write

    edbg(a, b);
    

    where

    a = 5;
    b = "Codeforces";
    

    It will print

    [a]: [5]
    [b]: [Codeforces]
    

    But for a general debug

    dbg(a, b);
    

    it will print

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

      This edbg macro is basically the same as the post. It dosen't work properly for something like this:

      *min_element(diff.begin() + 2, diff.end())
      

      because the commas separating the arguments of the function mess up the parsing algorithm. I think Tourist's way (which is essentially the same as your dbg function) is sufficient for me.

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

      Your code is not compile

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

I copy and paste your code but it doesn't print anything.

  • »
    »
    3 года назад, # ^ |
    Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится
    #ifdef XOX
    #define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)
    #else
    #define debug(...) 42
    #endif
    

    If you add the following line before the aforementioned block, It will work.

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

      I couldn't add XOX as compiler flag

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

        I guess you can replace

        #ifdef XOX
        #define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)
        #else
        #define debug(...) 42
        #endif
        

        with this

        #ifndef ONLINE_JUDGE
        #define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)
        #else
        #define debug(...) 42
        #endif
        
        • »
          »
          »
          »
          »
          3 года назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится

          Wow!Its work.Thanks a lot brother.I don't need to add any compiler flag with this.

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

Don't forget to use passing by reference (avoid unnecessary copying):

...
void debug_out(const vector<string>& args, int idx, int LINE_NUM, const Head& H, const Tail&... T) {
...
  • »
    »
    18 месяцев назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    The length of args is not usually so large, but also there is no harm to pass the parameters by the references...

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

One stop to debug template and its usage:

https://cs-mshah.github.io/getting_started_with_cp/#debugging

»
18 месяцев назад, # |
  Проголосовать: нравится +10 Проголосовать: не нравится

I used something similar myself, except one day I was bored so I decided to add the functionality to print stuff in different colors lol. It's hardly the most useful feature but does help sometimes with large implementations

The output —

(Don't judge my code too hard, it's a big steaming pile of mess but I don't have to look at it so I never bother to fix it lol)

If you choose to use it, I recommend you turn word wrap off

#define GREEN "\033[1;32m"
#define YELLOW "\033[1;33m"
#define ORANGE "\033[1;34m"
#define PURPLE "\033[1;35m"
#define BLUE "\033[1;36m"
#define trace(...) __f(__LINE__,YELLOW,#__VA_ARGS__, __VA_ARGS__)
#define Trace(color,...) __f(__LINE__,color,#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(int line, const char *color, const char* name, Arg1&& arg1){
        cerr << "\033[2;37m(Ln " << line << ")\033[0m " << color << name << ": \033[0;37m" << arg1 << "\033[0m " << endl;
    }
    template <typename Arg1, typename... Args>
    void __f(int line, const char *color, const char* names, Arg1&& arg1, Args&&... args){
        const char* comma = strchr(names + 1, ',');
        cerr << "\033[2;37m(Ln " << line << ")\033[0m " << color;
        cerr.write(names, comma - names) << ": \033[0;37m" << arg1 << "\033[0m " << endl;
        __f(line,color, comma+1, args...);
    }

//IO operators for vectors, pairs, sets, maps and __int128 so I can print just about anything
template <typename T>istream &operator>>(istream &in, vector<T> &v){for (auto &i : v)in >> i;return in;}
template <typename T>ostream &operator<<(ostream &ost, vector<T> v){if(&ost == &cerr){cerr <<"{";int cnt = v.size();for(auto x:v){cerr << x;cnt--;if(cnt) cerr << ", ";}cerr << "}";}else for(auto i:v)ost << i << " ";return ost;}
template <typename T>ostream &operator<<(ostream &ost, set<T> s){if(&ost == &cerr){cerr <<"{";int cnt = s.size();for(auto x:s){cerr << x;cnt--;if(cnt) cerr << ", ";}cerr << "}";}else for(auto i:s)ost << i << " ";return ost;}
template <typename T>ostream &operator<<(ostream &ost, multiset<T> s){if(&ost == &cerr){cerr <<"{";int cnt = s.size();for(auto x:s){cerr << x;cnt--;if(cnt) cerr << ", ";}cerr << "}";}else for(auto i:s)ost << i << " ";return ost;}
template <typename U, typename V>ostream &operator<<(ostream &ost, map<U,V> &m){if(&ost == &cerr){cerr <<"{";int cnt = m.size();for(auto x:m){cerr << x; cnt--;if(cnt) cerr << ", ";}cerr << "}";}else for(auto i:m)ost << i;return ost;}
template <typename U, typename V>istream &operator>>(istream &in, pair<U, V> &p){in>>p.first>>p.second;return in;}
template <typename U, typename V>ostream &operator<<(ostream &ost, pair<U, V> p){if (&ost == &cerr)ost << "{" << p.first << ": " << p.second << "}";else ost << p.first << " " << p.second << " ";return ost;}
ostream &operator<<(ostream &ost, __int128_t x){string output = "";while(x)output += '0'+x%10, x /= 10;reverse(all(output)); ost << output;  return ost;}
istream &operator>>(istream &in, __int128_t &x){x=0;string num;cin >> num;for(char c:num) x *= 10, x += c-'0';return in;}
//Variadic IO
template <typename... T>void read(T &...args){((cin >> args), ...);}
template <typename... T>void print(T... args){((cout << args << " "), ...);cout << endl;}

void solve()
{
    vector<int> v = {1,2,3};
    int a = 2;
    map<int,int> m;
    m[5] = 3;
    m[4] = 2;
    m[1] = 7;
    trace(v,a,m);
    Trace(ORANGE,v,a,m);
}
  • »
    »
    18 месяцев назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Sweet.. You may let the implementation decide the color based on the type of the variable. One question: are the color codes OS agnostic?

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

      Ohh, that's a good idea, I'll see if I can do that without too many lines of code.
      The codes do work on both windows and Linux but the colors themselves change according to your terminal's theme. For example I use Monokai Pro with some tweaks I made myself, but in Default Dark vscode theme the ORANGE color is printed in blue