Hi Community,
It's been a while I wrote a blog post here.
You can follow more such tricks on —
- Github: https://github.com/rachitiitr/modern-cpp-tricks
- Discord: https://bit.ly/discord-rachit
- YouTube: https://bit.ly/rachityoutube
How debug macros work?
Straight to the point, I have often used the debug
macro which stringifies the variable names and their values.
#define deb(x) cout << #x << " " << x
int ten = 10;
deb(ten); // prints "ten = 10"
This is often useful in debugging.
The Problem with this macro — its not scalable
However, when you have multiple variables to log, you end up with more deb2
and deb3
macros.
#define deb(x) cout << #x << " " << x
#define deb2(x) cout << #x << " " << x << " " << #y << " " << y
#define deb3(x, y, z) cout << #x << " " << x << " " << #y << " " << y << " " << #z << " " << z
This is not scalable.
Solution using a powerful macro
Here is the solution using variadic macros and fold expressions,
#define deb(...) logger(#__VA_ARGS__, __VA_ARGS__)
template<typename ...Args>
void logger(string vars, Args&&... values) {
cout << vars << " = ";
string delim = "";
(..., (cout << delim << values, delim = ", "));
}
int xx = 3, yy = 10, xxyy = 103;
deb(xx); // prints "xx = 3"
deb(xx, yy, xxyy); // prints "xx, yy, xxyy = 3, 10, 103"
Hope this helps, this is going to my template for sure
Bro, you can overload macro in c++ like this:
__#define GET_MACRO6(_1,_2,_3,_4,_5,_6,NAME,...) NAME__ __define deb_1(a)__ __define deb_2(a,b)__ __define deb_3(a,b,c)__ __define deb_4(a,b,c,d)__ __define deb_5(a,b,c,d,e)__ __define deb_6(a,b,c,d,e,f)__ __define deb(...) GET_MACRO6(__VA_ARGS__,deb_6,deb_5,deb_4,deb_3,deb_2,deb_1)(__VA_ARGS__)__
my full debug code: https://github.com/Wgmlgz/Random-code/blob/main/Cp/template.cpp
The issue with this is readability. Parameter packs and variadic templates seem like the future and easy to read (when you know how they work).
I use something like this
usage the same
I simply straight up copied tourist's debugging template
where can I find it?
Thanks but you still have to comment it out before submitting. Maybe use cerr ?
what is the benefit to use cerr?
You dont have to comment your debug line during submitting for judging, you can leave there as it is. cerr prints its contents to a separate error log.
cerr
statements are most likely to cause TLE.so you need to use this macro
I did some research and searched for very good debugging techniques and templates. So this is what I felt is the best method for debugging, and this works for several stl containers including
maps
and even policy based data structures.Here is the template:
Now, you might be feel that this would make my default template a lot more scary and confusing. So a solution to that is to use the
#ifndef ONLINE_JUDGE
preprocessor directive, and include the debug template as a .hpp file.Results:
note
: themyprettyprint.hpp
should be placed in a directory so that it is in the search path when compiling.in ubuntu, I placed it here:
/usr/include/c++/9/myprettyprint.hpp
. I hope this helps!original source: link1, link2
Thanks for this idea! I just want to know that where should I place this hpp file? Inside my compiler's folder (where "bits/stdc++.h" is placed?)
"bits/stdc++.h" if placed under ubuntu (/usr/include/x86_64-linux-gnu/c++/9/bits) and for arch linux (/usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits).
You should put "headerfile.hpp" in the above directory and use it like this
Got it.
One doubt though that #define dbg(...) what does this do? Do I need to have some other dbg macro defined above for it to work or is it self sufficient?
One doubt though that #define dbg(...) what does this do
This will make every dbg(..) statemets an EMPTY line. So, you dont have to comment out every dbg(..) statement in your code before submitting it.✌️
And it is self sufficient.
I simply use one existing pretty print header file
Update: put this in your stdc++.h
and put this in your source code:
I tried to compile and it threw several errors. Am I missing something?
[edit] fixed : I was not using c++17
use fmtlib and wait for C++20
This is what I use, It looks pretty lengthy and ugly but works like a charm for me.
I think most of the solutions for debugging here are quite non-generic in nature. I used to have such a debug template too (albeit with variadic macros and lots of overloads for printing STL containers). Then I came across Narut's awesome template for debugging that uses SFINAE in a pretty clever way, and I tinkered with it slightly to make it work with my old pretty printing template, to get the following result:
Note: This would work only with C++17 and later (because of
std::apply
andif constexpr
among others).Some advantages:
Doing
debug(a, b, c)
(or any number of parameters) pretty prints variablesa, b, c
of any sane type defined in the C++ standard or STL (sane as in something that makes sense to be printed). For instance, if it is a pair or an arbitrary tuple of printable types, or a container of printable types, it can be printed using this template, and is hence a printable type. No need to define printing functions for literally every single type. You can print stuff likevector<pair<string, vector<tuple<int, char>>>>
with this template too.It prints the line numbers as well, and takes care of printing containers in separate lines. In terms of formatting, it gives you a nice coloured output too.
For those curious, here's the explanation of how it works.
The
SFINAE
macro uses, very predictably, SFINAE (Substitution Failure Is Not An Error). For those unfamiliar with SFINAE, I'll try to explain it in an intuitive sense without going into too much detail. SFINAE tries to keep compiling when it gets a substitution failure according to the type system, and we can use the order of priority of compilation to our advantage. The usage here tries to perform a certain action and based on whether that action can be compiled or not, it stores the truth value of whether it failed or not in a struct's type (for instance, ifT
is an integral type, thenDefaultIO<T>::value
is true, whileIsTuple<T>::value
is false, and this will be useful when we implement ourWriter
class which can print everything that we care about.In the
Writer
class, using this type information, we determine at compile-time which kind of print we want to implement for each class. For a "trivially-printable" type, we can do stuff pretty easily. For a container, we just print everything recursively using ourWriter
and we win. For a tuple, usingstd::apply
, we can print stuff in the tuple just like what we would do for a container.Now coming to the debugging part, the
debug
macro takes the arguments, splits them into tokens, prints the line number, and then calls a function that iterates over all the variables and prints them alongside their name using ourWriter
.And, of course, it's always better to have this all neatly packed into a local header like
debug.hpp
which helps in keeping your submission clean!May you suggest other resources for further reading regarding
variadic macros and fold expressions
. Thanks !Great article! I'd like to suggest the following:
I mostly covered these topics in my article, feel free to check it out!
I am getting the below warning and my code isn't running.
The standard for the CPP set is already c++17 in my vs code.
Please help me in setting this debug code as my template.
Getting same error, have you found a solution yet?
I'm putting all my ugly debugging code inside "debug.h" file and include it only if I'm compiling the file locally, and also calling the debug function from an online judge will print nothing.