Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get name for each argument in variadic macros?

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.

like image 237
Dev Null Avatar asked Oct 13 '17 08:10

Dev Null


People also ask

What is the correct format for naming a macro with multiple arguments?

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.

How do you use macros in arguments?

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.

What is __ Va_opt __?

__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.

What does ## mean in macro?

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.


1 Answers

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.

like image 56
Banan Avatar answered Sep 28 '22 10:09

Banan