C 如何在va_列表上应用非vararg函数? 背景故事

C 如何在va_列表上应用非vararg函数? 背景故事,c,unit-testing,tuples,quickcheck,C,Unit Testing,Tuples,Quickcheck,我正在将单元测试框架移植到C(请参阅第页的工作代码)。语法将是: for_all(property, gen1, gen2, gen3 ...); 其中,property是一个要测试的函数,例如bool是奇数(int)gen1、gen2等是为属性生成输入值的函数。一些生成整数,一些生成字符,一些生成字符串,等等 for_all将接受具有任意输入(任意数量的参数、任意类型的参数)的函数for_all将运行生成器,创建要传递给属性函数的测试值。例如,属性是奇数的是类型为bool f(int)的函数

我正在将单元测试框架移植到C(请参阅第页的工作代码)。语法将是:

for_all(property, gen1, gen2, gen3 ...);
其中,
property
是一个要测试的函数,例如
bool是奇数(int)
gen1
gen2
等是为
属性生成输入值的函数。一些生成整数,一些生成字符,一些生成字符串,等等

for_all
将接受具有任意输入(任意数量的参数、任意类型的参数)的函数
for_all
将运行生成器,创建要传递给属性函数的测试值。例如,属性
是奇数的
是类型为
bool f(int)
的函数
for_all
将使用生成的代码创建100个测试用例。如果属性为其中任何一个返回false,
for_all
将打印有问题的测试用例值。否则,
for_all
将打印
“SUCCESS”

因此,
for_all
应使用
va_列表
访问发电机。调用生成器函数后,如何将它们传递给属性函数

例子 如果
的类型为
bool f(int)
,我们将如何实现具有以下语法的函数
apply()

apply(is_odd, generated_values);
次要问题 看

我们如何智能地打印失败测试用例的任意值?一个测试用例可以是一个整数,或者两个字符,或者一个字符串,或者上面的一些组合?我们无法提前知道是否使用:

  • printf(“%d%d%d\n”,一些int,一些int,一些int)
  • printf(“%c\n”一个字符)
  • printf(“%s%s\n”,一个字符串,一个需要自己的printf函数的结构)

    • C语言是一种静态类型语言。它没有其他语言所具有的运行时反射能力。它也不提供从运行时提供的类型生成任意函数调用的方法。您需要知道
      的函数签名是什么,它接受多少个参数,这些参数的类型是什么。它甚至不知道它什么时候到达终点。。。参数表;你需要一个明确的终结者

      enum function_signature {
          returns_bool_accepts_int,
          returns_bool_accepts_float,
          returns_bool_accepts_int_int,
      };
      
      typedef bool (*function_returning_bool_accepting_int)(int);
      typedef int (*function_generates_int)();
      
      void for_all(function_signature signature, ...)
      {
          va_list ap;
          va_start(ap, signature);
          switch (function_signature)
          {
          case returns_bool_accepts_int:
              {
                  function_returning_bool_accepting_int fn = va_arg(ap, function_returning_bool_accepting_int);
                  function_generates_int generator;
                  do {
                      generator = va_arg(ap, function_generates_int);
                      if (generator) fn(generator());
                  } while (generator);
              }
              break;
          ... etc ...
          }
      }
      
      您的问题是,QuickCheck是为了利用JavaScripts的高动态可编程性而设计的,这是C语言中缺少的

      更新如果允许任意函数签名,则需要一种方法使其再次保持静态,例如,让调用方提供适当的适配器

      typedef void (*function_pointer)();
      typedef bool (*function_applicator)(function_pointer, function_pointer);
      
      void for_all(function_applicator apply, ...)
      {
          va_list ap;
          va_start(ap, apply);
          function_pointer target = va_arg(ap, function_pointer);
          function_pointer generator;
          do {
              generator = va_arg(ap, function_pointer);
              if (generator) apply(target, generator);
          } while (generator);
      }
      
      // sample caller
      typedef bool (*function_returning_bool_accepting_int)(int);
      typedef int (*function_returning_int)();
      bool apply_one_int(function_pointer target_, function_pointer generator_)
      {
          function_returning_bool_accepting_int target = (function_returning_bool_accepting_int)target_;
          function_returning_int generator = (function_returning_int)generator_;
          return target(generator());
      }
      
      for_all(apply_one_int, is_odd, generated_values1, generated_values2, (function_pointer)0);
      
      
      
      }
      

      我不认为我遵循-你应该调用每个生成器一次并立即接收生成的值的整个集合,还是
      b)
      在循环中调用每个生成器函数以获得后续的值进行测试?好的,我已经阅读了源代码,所以我知道它实际上是
      c)
      每个生成器为特定函数参数提供后续值。示例:
      testme(int,char,char)
      需要一个随机整数和两个随机字符。一旦我们弄清楚了如何做,我们将让
      for_all
      在一个大循环中运行一个测试用例,而不是100个测试用例。对于您的第二个问题,您需要生成参数,并将它们放置在一个特殊的日志函数中,您也可以通过,或者启动一个
      va_列表
      ,然后将启动的列表传递给某种类型的打印或处理函数(您可以使用
      va_列表
      类型作为函数的参数,允许您包装或使用vararg函数)。@peachykeen是的,我想我必须将API全部更改为
      (property,gen1,print1,gen2,print2,…)
      来处理任意复杂的数据类型(想想红黑树)。是的,C不能动态地确定函数的类型签名。这就是为什么必须提供显式生成器函数的原因。而且我们不能硬编码
      enum
      ,因为用户可能想要在自定义类型上测试函数(结构、联合等)。然后你必须将键入的内容推入调用方。请参阅更新。(填补我留下的空白作为练习。我不能为你做所有事情…)嘿,不,我不希望你这么做。:)在您的代码中,似乎对生成的值1和生成的值2分别调用了is_odd。请记住,每个生成器表示属性输入的一部分。
      is_odd(int)
      具有arity 1,因此它应该具有单个生成器(
      gen_int
      )。
      等于(int a,int b)
      有arity 2,因此它将有两个生成器(
      gen_int,gen_int
      )。欢迎您扩展设计以支持arity大于1的函数。该设计要求为每个可能的函数签名创建辅助函数。我将把无限组合的枚举留给其他人作为练习。