Variadi c макрооболочка, которая расширяется для форматирования строки с символами, соответствующими количеству аргументов - PullRequest
2 голосов
/ 01 апреля 2020

Вопрос

Я ищу макрос препроцессора c C, который передает свой аргумент и соответствующую строку формата функции, повторяя символ в зависимости от количества аргументов. Например, я хотел бы макрос FOO, который расширяется следующим образом (или эквивалентным кодом C):

  • FOO(1)bar("d",1)
  • FOO(1,2)bar("dd",1,2),
  • FOO(1,2,3)bar("ddd",1,2,3)
  • бонус: FOO()bar("")

Пока я могу объединить решения в C макрос препроцессора для возврата строки, повторенной определенное число раз и препроцессор C ++ __VA_ARGS__ количество аргументов (или аналогичные вопросы) или использование variadi c макросов , у них есть несколько недостатков, таких как:

  • , требующие специальных библиотек, таких как Boost (что было бы проблемой для меня),
  • зависит от компилятора,
  • работает только во время выполнения,
  • чрезвычайно сложно.

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

Справочная информация

Я хочу отозвать Python функций в C расширении Python в автоматически генерировать д код. Так, например, мне нужно, чтобы foo(1,2,3) расширилось до:

PyObject_CallObject( callback_foo, Py_Build_Value("(Oddd)",Y,1,2,3) )

Я знаю, что все аргументы foo являются двойными, но я не знаю их числа. (Приведенный выше пример несколько упрощен. Мне известно, что ему не хватает нескольких Py_DECREF с.)

Ответы [ 3 ]

3 голосов
/ 01 апреля 2020

При 100% стандарте C вы можете сделать это:

#define COUNT_ARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#define STRTABLE (const char*[]){ "", "d", "dd", "ddd", "ddddd" } // and so on
#define FOO(...) bar(STRTABLE[COUNT_ARGS(__VA_ARGS__)], __VA_ARGS__)

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

Полный пример:

#include <stdio.h>

#define COUNT_ARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#define STRTABLE (const char*[]){ "", "d", "dd", "ddd", "ddddd" } // and so on
#define FOO(...) bar(STRTABLE[COUNT_ARGS(__VA_ARGS__)], __VA_ARGS__)

void bar(const char* fmt, ...)
{
  puts(fmt);
}

int main (void)
{
  FOO(1);
  FOO(1,2);
  FOO(1,2,3);
}
0 голосов
/ 01 апреля 2020

Всего 2 макроса:

#define GET_MACRO(_0,_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) bar(GET_MACRO(0,##__VA_ARGS__,"dddd","ddd","dd","d",""), ##__VA_ARGS__)
0 голосов
/ 01 апреля 2020

Лучшее, что я мог придумать, это взять этот ответ и упростить его:

# define EXPAND(x) x

# define FORMATSTRING(...) EXPAND(ELEVENTHARG1(__VA_ARGS__ __VA_OPT__(,) RSEQ()))
# define ELEVENTHARG1(...) EXPAND(ELEVENTHARG2(__VA_ARGS__))
# define ELEVENTHARG2(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
# define RSEQ() "dddddddddd","ddddddddd","dddddddd", \
    "ddddddd","dddddd","ddddd","dddd","ddd","dd","d",""

# define FOO(...) bar( FORMATSTRING(__VA_ARGS__) __VA_OPT__(,) __VA_ARGS__ )

FOO()      // expands to: bar( "" )
FOO(1)     // expands to: bar( "d" , 1 )
FOO(1,2,3) // expands to: bar( "ddd" , 1,2,3 )

Это работает с G CC и Clang (с -std=c++2a ) и до десяти аргументов (но могут быть расширены).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...