Могут ли макросы препроцессора C/С++ иметь значения параметров по умолчанию?

Можем ли мы указать значения параметров по умолчанию для параметров макроса?

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

Ответ 1

Вы ищете механизм перегрузки макросов, который представлен, например, Установки Boost.PP.

#define MACRO_2(a, b) std::cout << a << ' ' << b;

#define MACRO_1(a) MACRO_2(a, "test") // Supply default argument

// Magic happens here:

#define MACRO(...) BOOST_PP_OVERLOAD(MACRO_, __VA_ARGS__)(__VA_ARGS__)

Демо. Количество аргументов конкатенируется с именем макроса, которое может быть легко реализовано без Boost следующим образом:

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

#define CONCAT_(a, b) a##b
#define CONCAT(a, b) CONCAT_(a, b)

и

#define MACRO_2(a, b) std::cout << a << ' ' << b;

#define MACRO_1(a) MACRO_2(a, "test") // Supply default argument

#define MACRO(...) CONCAT(MACRO_, VARGS(__VA_ARGS__))(__VA_ARGS__)

Демо.

Ответ 2

Есть способ сделать это, если вы допустили необычный синтаксис:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

void TestInternal( int n )
{
    printf("%d\n" , n ) ;
}
#define TestGet( f , ... )  f
#define Test( ... ) TestInternal( TestGet( __VA_ARGS__ DEFAULT , DEFAULT ) )
#define DEFAULT 100

int main( void )
{ 
    Test( ) ;
    Test( 12345 , ) ;

return 0 ;
}

Это безопасный тип и может быть расширен до любого количества аргументов.

Обратите внимание, что запятая должна присутствовать, если вы передаете переменную. Вы можете рассматривать его как индикатор того, что эта функция/макрос имеет параметры по умолчанию.

Используя аналогичную версию, вы можете опустить запятую, но тогда ваши функции должны иметь по крайней мере один параметр, отличный от значения по умолчанию, которое должно быть первым.

Ответ 3

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

#define MACRO1( PARAM1 , PARAM2 ) &( PARAM1 + PARAM2 ) // or whatever logic

#define MACRO1_DEFAULT( PARAM1 ) MACRO1 ( PARAM1 , 12 ) // PARAM2 default of 12

Кроме того, как уже упоминалось 40, язык поддерживает использование многоточия для вариативных макросов, который также может быть жизнеспособным решением в некоторых случаях.

Содержимое связанной статьи:

Макросы Variadic являются функциональными макросами, которые содержат переменное количество аргументов.

Чтобы использовать переменные макросы, эллипсис может быть указан как окончательный формальный аргумент в определении макроса, а идентификатор замены __VA_ARGS__ может использоваться в определении для вставки дополнительных аргументов.
__VA_ARGS__ заменяется всеми аргументами, которые соответствуют многоточию, включая запятые между ними.

Стандарт C указывает, что по крайней мере один аргумент должен быть передан эллипсису, чтобы гарантировать, что макрос не разрешает выражение с конечной запятой.

<Microsoft specific>
Реализация Visual С++ будет подавлять конечную запятую, если аргументы не передаются многоточию.
</Microsoft Specific>

Пример

// variadic_macros.cpp
#include <stdio.h>
#define EMPTY

#define CHECK1(x, ...) if (!(x)) { printf(__VA_ARGS__); }
#define CHECK2(x, ...) if ((x)) { printf(__VA_ARGS__); }
#define CHECK3(...) { printf(__VA_ARGS__); }
#define MACRO(s, ...) printf(s, __VA_ARGS__)

int main() {
    CHECK1(0, "here %s %s %s", "are", "some", "varargs1(1)\n");
    CHECK1(1, "here %s %s %s", "are", "some", "varargs1(2)\n");   // won't print

    CHECK2(0, "here %s %s %s", "are", "some", "varargs2(3)\n");   // won't print
    CHECK2(1, "here %s %s %s", "are", "some", "varargs2(4)\n");

    // always invokes printf in the macro
    CHECK3("here %s %s %s", "are", "some", "varargs3(5)\n");

    MACRO("hello, world\n");

    MACRO("error\n", EMPTY); // would cause error C2059, except VC++ 
                             // suppresses the trailing comma
}

Выход

here are some varargs1(1)
here are some varargs2(4)
here are some varargs3(5)
hello, world
error