It's possible to create a macro str(a)
that will use its argument (a
) and its stringified name (#a
), for example:
#include <iostream>
#define str(a) #a, " ", a
int main()
{
int i = 5;
float f = 4.5;
const char* s = "string";
auto l = [] (const auto&... p) { (std::cout << ... << p) << std::endl; };
l(str(i));
l(str(f));
l(str(s));
}
Example.
Is there a simple way to print variable number of arguments prepended with a name of each argument? i.e. implement PREPEND_EACH_ARG_WITH_HASH_ARG
from the following:
#include <iostream>
#include <tuple>
template <typename ... Ts>
void print_all(const Ts&... ts)
{
(std::cout << ... << ts) << std::endl;
}
#define PREPEND_EACH_ARG_WITH_HASH_ARG(...) // how to implement '#a, " ", a' here?
#define PRINT_ALL(...) print_all(PREPEND_EACH_ARG_WITH_HASH_ARG(__VA_ARGS__))
int main()
{
auto a = 10;
auto b = 20.1;
auto c = "string";
auto d = 'c';
PRINT_ALL(a, b, c, d);
}
Example.
Macros with arguments To create a macro with arguments, put them in parentheses separated by commas after the macro name, e.g. then BadSquare(3+4) would give 3+4*3+4, which evaluates to 19, which is probably not what we intended.
Function-like macros can take arguments, just like true functions. To define a macro that uses arguments, you insert parameters between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.
__VA_OPT__ is a new feature of variadic macros in C++20. It lets you optionally insert tokens depending on if a variadic macro is invoked with additional arguments. An example usage is comma elision in a standardized manner.
The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.
The Boost.Preprocessor library is definitely a great solution. If, however, you do not want to be dependent on external libraries, you could something like the following:
#include <iostream>
#define str(a) #a, " = ", a
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
#define CONCAT_IMPL( x, y ) x##y
#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
// to verify, run the preprocessor alone (g++ -E):
#define PREPEND_EACH_ARG_WITH_HASH_ARG_1(a) str(a)
#define PREPEND_EACH_ARG_WITH_HASH_ARG_2(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_1(__VA_ARGS__)
#define PREPEND_EACH_ARG_WITH_HASH_ARG_3(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_2(__VA_ARGS__)
#define PREPEND_EACH_ARG_WITH_HASH_ARG_4(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_3(__VA_ARGS__)
#define PREPEND_EACH_ARG_WITH_HASH_ARG_5(a, ...) str(a) , " ; " , PREPEND_EACH_ARG_WITH_HASH_ARG_4(__VA_ARGS__)
#define PREPEND_EACH_ARG_WITH_HASH_ARG(...) MACRO_CONCAT(PREPEND_EACH_ARG_WITH_HASH_ARG_, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define PRINT_ALL(...) print_all(PREPEND_EACH_ARG_WITH_HASH_ARG(__VA_ARGS__))
template<typename T>
void print_impl(const T& t)
{
std::cout << t;
}
template<typename T, typename... Ts>
void print_impl(const T& t, const Ts&... ts)
{
std::cout << t;
print_impl(ts...);
}
template <typename ... Ts>
void print_all(const Ts&... ts)
{
print_impl(ts...);
std::cout << std::endl;
}
int main()
{
auto a = 10;
auto b = 20.1;
auto c = "string";
auto d = 'c';
PRINT_ALL(a, b, c, d);
}
Here the idea is to count the number of arguments using VA_NUM_ARGS
and use the result of that to call the correct PREPEND_EACH_ARG_WITH_HASH_ARG_#
macro, which will "recursively" call the next PREPEND_EACH_ARG_WITH_HASH_ARG_#
for each argument in __VA_ARGS__
.
The small drawback is that it is limited in the number of arguments it can take, but on the other hand this is easily extend-able.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With