Похоже, вы строите какой-то каркас мини-теста, используя препроцессор C.
Есть предостережение для тел;для препроцессора C фигурные скобки и квадратные скобки являются просто токенами.Выражения в скобках распознаются (т. Е. Круглые скобки совпадают), а запятые распознаются как разделители.Так, например, этот вызов макроса:
test(dosomething, { int a = add(1, 1); assert(a == 2); })
... имеет два аргумента, несмотря на наличие двух запятых (потому что вторая запятая «обнята» в наборе в скобках), но это немного вводит в заблуждение.Этот вызов:
test(dosomething, { enum { red, green, blue }; assert(red+1==green); })
... имеет четыре аргумента: 1: dosomething
, 2: { enum { red
, 3: green
и 4: blue }; assert(red+1==green); }
.Если вы собираетесь это сделать, вы, вероятно, захотите охватить такие случаи, как это ... есть базовые стратегии: (а) обнять тело в скобках (вы можете развернуть его в расширении) или (б) использовать макросы с переменным числом аргументов.
Их нужно повторить.
Похоже на задание для x-macros (ниже я буду использовать вариант параметризованного макроса x-macros).
Но более того, я хотел бы передать обратный вызов для асинхронных функций, чтобы сказать, что они завершены, например:
... вы не можете добавить аргумент в середине, но скобки не должны быть частью этого (они все равно не помогают, так как препроцессор их игнорирует).Так что для вышеизложенного мы, вероятно, хотим выбрать вариант объятия.В результате ваши вызовы выглядят так:
test(dosomething, (int a=add(1,1); assert(a==2);), done)
Однако, поскольку мы вырываем фигурные скобки, мы можем поместить их в произвольные места в нашем расширении и делать произвольные промежуточные вещи.Так как я предполагаю, что вы хотите, чтобы происходила такая же асинхронная вещь, мы могли бы просто поместить эту вещь в расширение, которое генерирует определение, а не аргумент.
Вот примерно так это выглядело бы, используяпараметризованная версия макроса x-macros и применение асинхронного расширения (используя семафоры, чтобы продемонстрировать, насколько это может быть произвольно):
#define APPLY_TEST_MACROS(macro) \
macro(test_add, (int a=add(1,1); assert(a==2); )) \
macro(test_sub, (int a=sub(5,2); assert(a==3); )) \
macro(test_mul, (int a=mul(3,4); assert(a==12); ))
#define UNWRAP(...) __VA_ARGS__
#define MAKE_ASYNC_SEM(NAME_, BODY_) \
void NAME_() { \
sem_wait(&test_sem_ctl); print(#NAME_); sem_post(&test_sem_ctl); \
UNWRAP BODY_ \
sem_wait(&test_sem_ctl); \
if (0==--tests_remaining) sem_post(&test_sem_done); \
sem_post(&test_sem_ctl); \
}
#define COUNT_TESTS(NAME_, BODY_) +1
sem_t test_sem_ctl;
sem_t test_sem_done;
void init_semaphores() {
sem_init(&test_sem_ctl, 0, 1);
sem_init(&test_sem_done, 0, 0);
}
// iterate over tests to count them
unsigned int tests_remaining = APPLY_TEST_MACROS(COUNT_TESTS);
// define the tests
APPLY_TEST_MACROS(MAKE_ASYNC_SEM)
... и так далее (я остановлюсь здесь, потому что идеяэто передать идею, а не код для вас).Макет x-macro позволяет вам выполнять итерации в препроцессоре, так что вы можете сделать что-то вроде порождения потока за тест; также можно просто использовать этот же подход для построения массива тестовых функций, если, скажем, вы хотите передать свои тесты в пул потоков.