stringify arbitrary number of variables

时间:2016-03-04 17:51:43

标签: c++ c-preprocessor

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?

6 个答案:

答案 0 :(得分:1)

Stringify [an]任意数量的变量?

是一个问题,并且:

如何...获取一个接受任意数量参数的STRINGIFY函数?

是另一个问题。猜测第一个问题是重要的问题 对你来说,这就足够了:

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

一些无意义的用法:

<强>的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;
}

预处理:

$  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;
}

执行命令

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

答案 1 :(得分:1)

我不确定我是否明白你要做的事情。下面的代码在编译时标记__VA_ARGS__。它不检查语法:它盲目地用'\0'替换空格和逗号,将arg中的标识符的开头和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 :(得分:0)

您无法在CPP中递归,但您可以#define许多宏(DO1DO2 .. DO128),然后使用一个“通用”宏,使用适当的后缀扩展为宏。

P99是一个lib(实际上是一个头文件),它提供了这样做的样板文件。 P99_SER在每个参数上调用P99_STRINGIFY之后粘贴用空格分隔的参数。

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

扩展为

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

答案 3 :(得分:0)

您可以使用以下内容(最多6个硬编码参数):

#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)

因此names(var1, var2)会产生"var1", "var2"。 (你可以传递给任何功能) 您还可以自定义name_x

Demo

答案 4 :(得分:0)

唉,你不能在预处理器中进行递归,但是你可以对这个可变参数宏的所有参数做一个&#34; apply宏。宏。

一个有用的想法是,在调用函数式宏时,()可以是参数的一部分。这可以让你做一些时髦的事情。在您的具体示例中......

首先,一个宏告诉我们__VA_ARGS__包中有多少个参数:

#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)

接下来,一个宏将一个给定的宏独立应用于__VA_ARGS__包的每个元素:

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

一个简单的&#34;字符串化一个&#34;宏(确保其参数扩展):

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

现在让我们将所有部分组合在一起,制作一个构建字符串数组的好例子:

#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" };

希望您能看到如何编写不同的&#34; lambda-like&#34;与FOREACH一起使用的宏可以做各种令人兴奋的事情。

答案 5 :(得分:-2)

尝试使用__VA_ARGS__宏。

#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;
}

输出:

var1 var2 varN