Строит произвольное число переменных
Для одной переменной (или заданного числа переменных) легко использовать макросы для подкрепления переменных. Например. для 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