4

Я пытаюсь построить макрос M, который будет расширяться в одну из двух возможностей, depeding от того, имеет ли он один или более чем один, аргументы:макросы с Перегрузка переменным числом аргументов

M(x) 

должна расширяться

f(x) 

Хотя

M(x, "%d%d%d", 1, 2, 3) 

должен расширяться

g(x, "%d%d%d", 1, 2, 3) 

Если функция подписи

f(int x); 
g(int x, const char *fmt, ...); 

Есть variousanswers касательно «перегрузки» макрокоманд, если количество аргументов известно; однако их методы определения длины __VA_ARGS__ работают только с конечным выбранным числом.

Есть ли какой-либо трюк, который мог бы схожим образом работать для моего случая «один аргумент/более одного аргумента»?

Примечание:

Перегрузка функции не вариант, потому что в моем случае они на самом деле являются конструкторами для двух различных классов.

+3

Для меня, с использованием двух различных constructurs позади одного макроса на основе количества аргументов звучит неправильно. Если один класс не является производным от другого, то в этом случае возможен какой-либо перегруженный/вариационный аргумент фабрики или шаблона с переменной аргументами. –

+1

Особенно, если два класса не являются братьями и сестрами друг от друга (из общего базового класса), так как тогда вам нужно будет знать, каков тип возврата макроса, и в этот момент вы можете просто вызвать правильный макрос. Перегруженный завод звучит как намного лучший вариант в любом случае. Макросы действительно должны использоваться только (на C++), где предварительная компиляция текстовой части замены важна для функциональной корректности. – aruisdante

+0

Я согласен, что это выглядит плохой идеей. Почему вы хотите использовать макросы там? –

ответ

1

Простой. Мы просто делаем немного зондирования, чтобы узнать, является ли маркер 1:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) 
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ 

#define CHECK_N(x, n, ...) n 
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,) 
#define PROBE(x) x, 1, 

#define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x)) 
#define IS_1_1 PROBE(~) 

Так IS_1 расширяется до 1, если маркер является 1, в противном случае она расширяется до 0. Так дальше, подсчитывают число аргументов (до 8):

#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N 
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) 

Затем перегрузить на ли это равно 1 или нет:

#define M_1 f 
#define M_0 g 

#define M(...) CAT(M_, IS_1(NARGS(__VA_ARGS__)))(__VA_ARGS__) 

Итак, то вы можете позвонить M так:

M(x) // Expands to f(x) 
M(x, "%d%d%d", 1, 2, 3) // Expands to g(x, "%d%d%d", 1, 2, 3) 

Теперь вы можете считать до 64 аргументов (мой пример насчитывает до 8), для стандартного препроцессора C (gcc может считать до 32767 argumen ц). Если вам нужно больше аргументов, чем лучше использовать последовательность, которая не имеет ограничений.Таким образом, первым написать метод, чтобы преобразовать последовательность обратно аргументов, используя последовательность итераций:

#define TO_ARGS(seq) TO_ARGS_END(TO_ARGS_1 seq) 
#define TO_ARGS_END(...) TO_ARGS_END_I(__VA_ARGS__) 
#define TO_ARGS_END_I(...) __VA_ARGS__ ## _END 
#define TO_ARGS_1(x) x TO_ARGS_2 
#define TO_ARGS_2(x) , x TO_ARGS_3 
#define TO_ARGS_3(x) , x TO_ARGS_2 
#define TO_ARGS_1_END 
#define TO_ARGS_2_END 
#define TO_ARGS_3_END 

Затем определите M макрос перегружать ли есть один элемент в последовательности:

#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x) 
#define IS_PAREN_PROBE(...) PROBE(~) 

#define EAT(...) 

#define M_1(seq) g(TO_ARGS(seq)) 
#define M_0(seq) f(TO_ARGS(seq)) 

#define M(seq) CAT(M_, IS_PAREN(EAT seq))(seq) 

И тогда вы можно назвать так:

M((x)) // Expands to f(x) 
M((x)("%d%d%d")(1)(2)(3)) // Expands to g(x, "%d%d%d", 1, 2, 3) 

конечно, в C++ 14, если вы не нуждаетесь в информации источника, то вы можете использовать шаблоны variadiac вместо:

template<class T> 
auto M(T&& xs) -> decltype(f(std::forward<T>(x))) 
{ 
    return f(std::forward<T>(x)); 
} 

template<class T, class U, class... Ts> 
auto M(T&& x, U&& y, Ts&&... xs) -> decltype(g(std::forward<T>(x), std::forward<U>(y), std::forward<Ts>(xs)...)) 
{ 
    return g(std::forward<T>(x), std::forward<U>(y),std::forward<Ts>(xs)...); 
} 

Или конструкторами:

class M : f, g 
{ 
    template<class T> 
    M(T&& xs) : f(std::forward<T>(x)) 
    {} 

    template<class T, class U, class... Ts> 
    M(T&& x, U&& y, Ts&&... xs) : g(std::forward<T>(x), std::forward<U>(y), std::forward<Ts>(xs)...) 
    {} 
}; 
+0

К сожалению, 'M (1,1,1,1,1,1,1,1,1,1,1)' будет расширяться до 'f' ... –

+0

Кроме того,' M (0,1,2, 3,4,5,6,7, «foo») 'приведет к ошибке: вставка« IS_1_ »и« foo »« не дает действительного токена предварительной обработки » –

+0

Это потому, что у него есть предел до 8 аргументов. Вам нужно расширить 'NARGS_SEQ', чтобы обрабатывать больше (максимум 64). –

Смежные вопросы