Реализация форматированной печати с возможностью ничего не делать, когда она не получает аргументов - PullRequest
0 голосов
/ 14 ноября 2018

Я хочу реализовать макрос с именем PRINT, который получает ноль или более параметров и выполняет следующие действия:

  1. Если он получает ноль параметров - ничего не делать.
  2. Если он получает один или несколько аргументов - действуйте как printf.

Мне удалось реализовать это, как вы можете видеть в моем коде ниже, но только за счет вызова printf с пустой строкой в ​​случае, если мы получим нулевые аргументы.

Есть ли способ, которым я могу обработать случай с нулевыми аргументами без вызова printf (неэффективно печатать что-то, когда вы просто хотите ничего не делать)?

#include <stdio.h>

#define PRINT(...) printf("" __VA_ARGS__);

int main(){
    PRINT();
    PRINT("print\n");
    PRINT("print number: %d\n", 7);
    return 0;
}   

вывод:

print
print number: 7  

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Если вы хотите передать проверку пустого списка аргументов макросу, то, возможно, что-то вроде этого:

#define HAS_ARGS(...) (sizeof( (char[]){#__VA_ARGS__} ) > 1)

#define PRINT(...) (HAS_ARGS(__VA_ARGS__) ? printf("" __VA_ARGS__) : (void)0)

Это зависит от составного литерала, получающего размер 1 в случае пустой строки =только нулевой терминатор.Пример:

#include <stdio.h>

#define HAS_ARGS(...) (sizeof( (char[]){#__VA_ARGS__} ) > 1)

#define PRINT(...) (HAS_ARGS(__VA_ARGS__) ? printf("" __VA_ARGS__) : (void)0)

int main (void)
{
  int i = 5;
  PRINT("%d\n", i);
  PRINT();
}
0 голосов
/ 15 ноября 2018

Решение:

#define Argument1(a,...)    Argument2(a
#define Argument2(a, b,...) b
#define TestEmpty()         ,
#define PRINT(...)          Argument1(TestEmpty __VA_ARGS__ (),), printf(__VA_ARGS__);,)

Для этого исходного текста:

Test 0: PRINT()
Test 1: PRINT("Hello, world.\n")
Test 2: PRINT("Result is %d.\n", result)
Test 3: PRINT("%d = %g.\n", 3, 3.)

результат предварительной обработки:

Test 0:
Test 1: printf("Hello, world.\n");
Test 2: printf("Result is %d.\n", result);
Test 3: printf("%d = %g.\n", 3, 3.);

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

Объяснение:

  • При подготовке к замене PRINT, __VA_ARGS__ заменяется.TestEmpty в настоящее время не заменяется, так как за ним не следуют скобки.
  • Затем заменяется PRINT, и результат повторно сканируется.
  • Argument1 идентифицируется для замены.Готовясь к этому, его аргументы обрабатываются.
  • На данный момент, если __VA_ARGS__ пусто, у нас есть токены TestEmpty (), которые заменяются на ,.В противном случае TestEmpty <some tokens> () остается.Обратите внимание, что если присутствует TestEmpty (), то , расширяется до и является первым аргументом до Argument1.Это не разделитель аргументов, потому что аргументы для Argument1 уже определены.
  • Затем Argument1 заменяется, производя либо Argument2(, (если __VA_ARGS было пусто), либо, возможно, Argument2(TestEmptyс последующими дополнительными токенами (в противном случае).
  • Результат будет Argument2(,, printf();,) или Argument2(TestEmpty <some tokens>, printf(<some tokens>);,), в зависимости от того, был ли __VA_ARGS__ пуст или нет.
  • Теперь токены повторно сканируются для дальнейшегозамену, поэтому , теперь будет распознаваться как разделитель аргументов.
  • Наконец, Argument2 заменяется пустым списком токенов или printf(<some tokens>);.
0 голосов
/ 14 ноября 2018

И gcc, и clang полностью исключат вызов printf в случае, если ему передана пустая строка формата. Вероятно, такая оптимизация достаточно распространена.

См. здесь и здесь для разборки на gcc.godbolt.org.

Короче, не беспокойся об этом.

...