背景故事
我正在移植 QuickCheck C 的单元测试框架(参见 GitHub 处的工作代码)。语法将是:
for_all(property, gen1, gen2, gen3 ...);
其中 property
是要测试的函数,例如 bool is_odd(int)
。 gen1
、gen2
等是为property
生成输入值的函数。一些生成整数,一些生成字符,一些生成字符串,等等。
for_all
将接受具有任意输入(任意数量的参数、任意类型的参数)的函数。 for_all
将运行生成器,创建测试值以传递给属性函数。例如,属性 is_odd
是一个类型为 bool f(int)
的函数。 for_all
将使用生成器创建 100 个测试用例。如果属性为其中任何一个返回 false,for_all
将打印有问题的测试用例值。否则,for_all
将打印 "SUCCESS"
。
因此 for_all
应该使用 va_list
来访问生成器。一旦我们调用了生成器函数,我们如何将它们传递给属性函数?
例子
如果 is_odd
具有 bool f(int)
类型,我们将如何实现具有以下语法的函数 apply()
:
apply(is_odd, generated_values);
次要问题
参见 SO .
我们如何智能地打印失败测试用例的任意值?一个测试用例可能是一个整数,或者两个字符,或者一个字符串,或者以上的某种组合?我们不会提前知道是否使用:
printf("%d %d %d\n", some_int, some_int, some_int);
printf("%c\n"a_character);
printf("%s%s\n", a_string, a_struct_requiring_its_own_printf_function);
最佳答案
C 语言是静态类型语言。它没有其他语言所具有的运行时反射能力。它也不提供从运行时提供的类型构建任意函数调用的方法。您需要通过某种方式了解 is_odd
的函数签名是什么,它接受多少参数以及这些参数的类型是什么。它甚至不知道它何时到达 ... 参数列表的末尾;你需要一个明确的终止符。
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 旨在利用 JavaScript 的高动态可编程性,这是 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);
}
关于c - 我们如何在 va_list 上应用非可变参数函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7522954/