C宏,用于通过回调定义测试方法

时间:2019-03-20 04:17:31

标签: c macros

因此,您可以使用自己的名称动态定义一个方法,如下所示:

#define test(name) void name() { print("#name"); }

然后您可以这样称呼它:

test(foo);
foo();

我想知道是否可以制作“回调”样式的表单,如下所示:

#define test(name, body) void name() { print(#name); body(); }

在其中调用这样定义为“块”的主体的方式:

test(dosomething, {
  int a = add(1, 1);
  assert(a == 2);
})

但除此之外,我想为异步函数传递一个回调,以说它们是完整的,像这样:

test(dosomething, { (done)
  int a = add(1, 1);
  assert(a == 2);
  done();
})

此外,我在main之外定义了这些,因此将在与普通函数相同的范围内进行定义。因此,测试不会自动运行。需要重复。因此,可能需要将它们放入某种数组中。因此想知道如何做到这一点,如果宏允许您将捕获的内容整理到一个数组中,还是一次建立一个枚举#define

#define test(name, body) void name() { \
  print(#name); \
} \
\
TESTS[CURRENT_TEST++] = &name \ // push the test into a TESTS array.

因此,您可以在main中对其进行迭代:

int
main() {
  iterate over TESTS...
}

总而言之,我想知道如何#define在文件正文级别(即不是在main级别,而是在功能级别):

void afunction() { printf("Foo"); }

test(dosomething, { (done)
  int a = add(1, 1);
  assert(a == 2);
  done();
})

void anotherfunction() { printf("Bar"); }

这样我就可以遍历main中的测试。

  • This建议在宏中使用块。

1 个答案:

答案 0 :(得分:2)

好像您正在使用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); }。如果要执行此操作,则可能要涵盖以下情况...有一些基本策略:(a)将身体放在括号中(可以展开展开),或(b)使用可变参数宏。

需要对其进行迭代。

听起来像x-macros的工作(下面我将使用x宏的参数化宏样式)。

但除此之外,我还想传递一个异步函数回调,以说它们是完整的,像这样:

...您不能在中间添加参数,但是括号不必是其中的一部分(无论如何它们都无济于事,因为预处理程序会忽略它们)。因此,对于以上内容,我们可能希望选择“拥抱”选项。这样您的调用看起来像这样:

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宏布局允许您在预处理器中进行迭代,因此您可以执行诸如在每次测试中生成线程之类的操作。如果想将测试提供给线程池,您也可以 使用相同的方法来构建测试函数数组。