stringify arbitrary number of variables

孤者浪人 提交于 2019-12-10 17:23:47

问题


For a single variable (or a given number of variables), it's easy to use macros to stringify variables. E.g. for 2 variables, I can do:

#define STRINGIFY(var1, var2) (std::string(#var1) + " " + #var2)

Is there a way to extend the above with either variadic macros or some other sort of compile-time trickery, to ultimately get a STRINGIFY function that accepts an arbitrary number of arguments?


回答1:


I'm not sure that I understood what you're trying to do. The code below tokenizes, at compile time, __VA_ARGS__. It does not check the syntax: it blindly replaces the white-space and commas with '\0', stores the start of identifiers in arg and the number of arguments in argc.

#include <iostream>

template < unsigned N > constexpr
unsigned countarg( const char( &s )[N], unsigned i = 0, unsigned c = 0 )
{
  return
    s[i] == '\0'
    ? i == 0
    ? 0
    : c + 1
    : s[i] == ','
    ? countarg( s, i + 1, c + 1 )
    : countarg( s, i + 1, c );
}

template < unsigned N > constexpr
unsigned skipid( char( &s )[N], unsigned i = 0 )
{
  return s[i] == '\0' || s[i] == ' ' || s[i] == '\t' || s[i] == ','
    ? i
    : skipid( s, i + 1 );
}

template < unsigned N, unsigned M > constexpr
unsigned tokenize( char( &s )[N], const char*(&a)[M], unsigned i = 0, unsigned j = 0 )
{
  return s[i] == '\0'
    ? i
    : s[i] == ' ' || s[i] == '\t' || s[i] == ','
    ? ((s[i] = '\0'),
      tokenize( s, a, ++i, j ))
    : ((a[j] = s + i),
      i = skipid( s, i ),
      tokenize( s, a, i, ++j ));
}

#define TOKENIZEVA( ... ) char orig[] = #__VA_ARGS__; const unsigned argc = countarg(#__VA_ARGS__); const char* arg[argc]; tokenize( orig, arg );

#define PRINT( ... ) { TOKENIZEVA(__VA_ARGS__) for ( auto s : arg ) std::cout << s << std::endl; }

int main()
{
  PRINT( first, second, third, fourth );
  return 0;
}



回答2:


You may use the following (up to hard coded 6 arguments):

#define NARGS_(_1, _2, _3, _4, _5 , _6, N, ...) N
#define NARGS(args...) NARGS_(args..., 6, 5, 4, 3, 2, 1)

#define CAT_(a, b) a ## b
#define CAT(a, b) CAT_(a, b)

#define name_1(x)                     #x
#define name_2(x, x2)                 #x , #x2
#define name_3(x, x2, x3)             #x , #x2, #x3
#define name_4(x, x2, x3, x4)         #x , #x2, #x3, #x4
#define name_5(x, x2, x3, x4, x5)     #x , #x2, #x3, #x4, #x5
#define name_6(x, x2, x3, x4, x5, x6) #x , #x2, #x3, #x4, #x5, #x6

#define names(args...) CAT(name_, NARGS(args))(args)

So names(var1, var2) results in "var1", "var2". (That you can pass to any function).
You can also customize name_x.

Demo




回答3:


You can't recurse in CPP, but you can #define a lot of macros (DO1, DO2 .. DO128) and then use one "generic" Macro that expands to the macro with the appropriate suffix.

P99 is one lib (a header file actually) that provides the boilerplate to do this. P99_SER pastes arguments delimited with spaces after calling P99_STRINGIFY on each of them.

#include "p99_for.h"
P99_SER(P99_STRINGIFY, first,second,third,fourth,fifth,6)

expands to

$ gcc -E test.c | tail -n 1
    "first" "second" "third" "fourth" "fifth" "6"



回答4:


Stringify [an] arbitrary number of variables?

is one question and:

Way to ... get a STRINGIFY function that accepts an arbitrary number of arguments?

is another question. Guessing that the first question is the one that matters to you, this is enough:

#define STRINGIFY(tokz) (std::string(#tokz))

Some nonsense usage:

main.cpp

#include <iostream>

#define STRINGIFY(tokz) (std::string(#tokz))

#define FOO(x,y) \
    {   int x = 1 ; int y = 2 ; std::string s = STRINGIFY(x y); \
        cout << '[' << s  << ']' << " -> " << x << ' ' << y  << '\n'; \
    }
#define BAR(x,y,z) \
    {   char x = 'x'; char y = 'y'; char z = 'z'; \
        std::string s = STRINGIFY([ x y z ]); \
        cout << s << " -> " << x << ' ' << y << ' ' << z << '\n'; \
    } 

using namespace std;
int main()
{
    cout << STRINGIFY(p q) << '\n' << STRINGIFY(r s t) << '\n';
    FOO(a,b);
    BAR(c,d,e);
    return 0;
}

Preprocessed prettily:

$  g++ -E main.cpp | astyle 
...
...
using namespace std;
int main()
{
    cout << (std::string("p q")) << '\n' << (std::string("r s t")) << '\n';
    {
        int a = 1 ;
        int b = 2 ;
        std::string s = (std::string("a b"));
        cout << '[' << s << ']' << " -> " << a << ' ' << b << '\n';
    };
    {
        char c = 'x';
        char d = 'y';
        char e = 'z';
        std::string s = (std::string("[ c d e ]"));
        cout << s << " -> " << c << ' ' << d << ' ' << e << '\n';
    };
    return 0;
}

Run:

$ g++ main.cpp && ./a.out
p q
r s t
[a b] -> 1 2
[ c d e ] -> x y z



回答5:


Alas, you can't do recursion in the preprocessor, but you can kinda-sorta make a "apply macro to all arguments to this variadic macro" macro.

A useful idea is that when calling a function-style macro, the () can be part of the argument. This allows you to do some funky things. In your specific example...

First, a macro that tells us how many arguments are in a __VA_ARGS__ pack:

#define NUM_ARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

Next, a macro that applies a given macro to every element of a __VA_ARGS__ pack independently:

#define FOREACH(MACRO, ...) FOREACH_(NUM_ARGS(__VA_ARGS__), MACRO, __VA_ARGS__)
#define FOREACH_(N, M, ...) FOREACH__(N, M, __VA_ARGS__)
#define FOREACH__(N, M, ...) FOREACH_##N(M, __VA_ARGS__)
#define FOREACH_1(M, A) M(A)
#define FOREACH_2(M, A, ...) M(A)  FOREACH_1(M, __VA_ARGS__)
#define FOREACH_3(M, A, ...) M(A) FOREACH_2(M, __VA_ARGS__)
#define FOREACH_4(M, A, ...) M(A) FOREACH_3(M, __VA_ARGS__)
#define FOREACH_5(M, A, ...) M(A) FOREACH_4(M, __VA_ARGS__)
#define FOREACH_6(M, A, ...) M(A) FOREACH_5(M, __VA_ARGS__)
#define FOREACH_7(M, A, ...) M(A) FOREACH_6(M, __VA_ARGS__)
#define FOREACH_8(M, A, ...) M(A) FOREACH_7(M, __VA_ARGS__)
// Extend in the obvious way for as many iterations as needed.

A simple "stringify one thing" macro (that makes sure its argument expands):

#define STRINGIFY_(X) #X
#define STRINGIFY(X) STRINGIFY_(X)

And now let's put all the pieces together to make a nice example that builds a string array:

#define STRINGIFY_ALL(...) FOREACH(STRINGIFY, __VA_ARGS__)

#define COMMA(X) X,
#define COMMA_STRINGIFY(X) COMMA(STRINGIFY(X))

#define STRING_LITERAL_ARRAY(...) const char* STUFF[ NUM_ARGS(__VA_ARGS__) ] = { FOREACH(COMMA_STRINGIFY, __VA_ARGS__) };

STRING_LITERAL_ARRAY(I, AM, A, POTATO);

// Will yield:
const char* STUFF[ 4 ] { "I", "AM", "A", "POTATO" };

Hopefully you can see how you can write different "lambda-like" macros to use with FOREACH to do all sorts of exciting things.




回答6:


Try __VA_ARGS__ macro.

#include <stdio.h>

#define STRINGIFY(...) #__VA_ARGS__

int main()
{
    int var1;
    int var2;
    int varN;

    printf(STRINGIFY(var1 var2 varN)); // or STRINGIFY(var1, var2, varN)

    return 0;
}

Output:

var1 var2 varN


来源:https://stackoverflow.com/questions/35802830/stringify-arbitrary-number-of-variables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!