Introduction#
In C++
, it is common to print variables for debugging code, but both printf
and cout
can be cumbersome:
-
printf
int a = 1; float b = 2.0; char c = 'c'; printf("a = %d, b = %f, c = %c", a, b, c);
-
cout
int a = 1; float b = 2.0; char c = 'c'; std::cout << "a = " << a << ", b = " << b << ", c = " << c << '\n';
Variadic Macros#
Variadic macros are a feature introduced in C99
and supported since C++11
.
#define def_name(...) def_body(__VA_ARGS__)
Variadic Templates#
C++11
allows template definitions with any number of template parameters of any type (including $0$ template parameters).
template<typename... Values> class tuple;
If you do not want to allow $0$ template arguments, you can declare it as follows:
template<typename First, typename... Rest> class tuple;
Variadic templates are applicable to function templates, such as the implementation of the printf
function:
template<typename... Args>
void printf(const std::string &str_format, Args... args);
Expansion of Parameter Packs#
- For template functions
- Recursive expansion
// Recursive termination function void print() {} template<typename T, typename... Args> void print(T head, Args... args) { std::cout << head << (sizeof...(args) > 0 ? ", " : ""); print(args...); }
- Comma expression expansion: This approach is a bit cumbersome and not easy to understand, not recommended
- For template classes
- Recursive expansion
- Inheritance expansion
Macro Operators#
-
#
: Converts a macro parameter to a string#include <stdio.h> #define stringer( x ) printf( #x "\n" ) int main() { stringer( In quotes in the printf function call ); stringer( "In quotes when printed to the screen" ); stringer( "This: \" prints an escaped double quote" ); }
After preprocessing with
gcc -E
, the following result is obtained:int main() { printf("In quotes in the printf function call" "\n"); printf("\"In quotes when printed to the screen\"" "\n"); printf("\"This: \\\" prints an escaped double quote\"" "\n"); }
-
#@
: Encloses the argument in single quotes, treating it as a character#define makechar(x) #@x c = makechar(b);
After preprocessing:
c = 'b';
-
##
: Concatenates two independent tokens together#define paster(n) printf("token" #n " = %d", token##n) int token9 = 9; paster(9);
After preprocessing:
printf("token9 = %d", token9);
Implementing the DBG
Macro#
With the above skills, we can implement a simple debugging macro:
#include <iostream>
#include <string>
template <typename T>
void _DBG(const char *s, T t) {
std::cout << s << "=" << t << '\n';
}
template <typename T, typename... Args>
void _DBG(const char *s, T t, Args... args) {
while (*s != ',')
std::cout << *s++;
std::cout << "=" << t << ",";
_DBG(s + 1, args...);
}
#define DBG(...) _DBG(#__VA_ARGS__, __VA_ARGS__)
int main() {
int a = 1;
float b = 2.0;
char c = 'c';
std::string d = "def";
DBG(a, b, c, d);
return 0;
}
The above code outputs:
a=1, b=2, c=c, d=def