Строит произвольное число переменных

Для одной переменной (или заданного числа переменных) легко использовать макросы для подкрепления переменных. Например. для 2 переменных я могу сделать:

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

Есть ли способ расширить описанные выше варианты с помощью нескольких вариационных макросов или какого-либо другого типа обмана времени компиляции, чтобы в конечном итоге получить функцию STRINGIFY, которая принимает произвольное количество аргументов?

Ответ 1

Я не уверен, что понял, что вы пытаетесь сделать. Код ниже tokenizes, во время компиляции, __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

Сгладить [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;
}

Run:

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

Ответ 3

Вы не можете рекурсировать в CPP, но вы можете #define много макросов (DO1, DO2.. 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"

Ответ 4

Вы можете использовать следующие (до жестко закодированных 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.

Демо

Ответ 5

Увы, вы не можете выполнять рекурсию в препроцессоре, но вы можете как-то сделать макрос "применить макрос ко всем аргументам к этому переменному макросу".

Полезная идея заключается в том, что при вызове макроса в стиле функции функция() может быть частью аргумента. Это позволяет вам делать некоторые напуганные вещи. В вашем конкретном примере...

Во-первых, макрос, который сообщает нам, сколько аргументов находится в пакете __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__ pack независимо:

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

Простой макрос "подстроить одну вещь" (что позволяет расширить его аргумент):

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

Надеюсь, вы сможете увидеть, как вы можете писать разные "лямбда-подобные" макросы для использования с FOREACH, чтобы делать всевозможные захватывающие вещи.

Ответ 6

Попробуйте __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