我们如何在 va_list 上应用非 vararg 函数? [英] How can we apply a non-vararg function over a va_list?

查看:27
本文介绍了我们如何在 va_list 上应用非 vararg 函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将 QuickCheck 单元测试框架移植到 C(参见工作GitHub 上的代码).语法将是:

I'm porting the QuickCheck unit test framework to C (see the working code at GitHub). The syntax will be:

for_all(property, gen1, gen2, gen3 ...);

其中 property 是要测试的函数,例如 bool is_odd(int).gen1gen2 等是为 property 生成输入值的函数.有些生成整数,有些生成字符,有些生成字符串,等等.

Where property is a function to test, for example bool is_odd(int). gen1, gen2, etc. are functions that generate input values for property. Some generate integers, some generate chars, some generate strings, and so on.

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

for_all will accept a function with arbitrary inputs (any number of arguments, any types of arguments). for_all will run the generators, creating test values to pass to the property function. For example, the property is_odd is a function with type bool f(int). for_all will use the generates to create 100 test cases. If the property returns false for any of them, for_all will print the offending test case values. Otherwise, for_all will print "SUCCESS".

因此 for_all 应该使用 va_list 来访问生成器.一旦我们调用了生成器函数,我们如何将它们传递给属性函数?

Thus for_all should use a va_list to access the generators. Once we call the generator functions, how do we pass them to the property function?

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

If is_odd has the type bool f(int), how would we implement a function apply() that has this syntax:

apply(is_odd, generated_values);

次要问题

请参阅 SO.

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

How can we intelligently print the arbitrary values of a failing test case? A test case may be a single integer, or two characters, or a string, or some combination of the above? We won't know ahead of time whether to use:

  • printf("%d %d %d ", some_int, some_int, some_int);
  • printf("%c " a_character);
  • printf("%s%s ", a_string, a_struct_requiring_its_own_printf_function);
  • printf("%d %d %d ", some_int, some_int, some_int);
  • printf("%c " a_character);
  • printf("%s%s ", a_string, a_struct_requiring_its_own_printf_function);

推荐答案

C语言是一种静态类型语言.它不具备其他语言所具备的运行时反射功能.它也不提供从运行时提供的类型构建任意函数调用的方法.您需要有一些方法知道 is_odd 的函数签名是什么,它接受多少个参数以及这些参数的类型是什么.它甚至不知道它何时到达 ... 参数列表的末尾;你需要一个显式的终止符.

The C language is a statically-typed language. It does not have the powers of runtime reflection that other languages do. It also does not provide ways to build arbitrary function calls from runtime-provided types. You need to have some way of knowing what the function signature of is_odd is and how many parameter it accepts and what the types of those parameters is. It doesn't even know when it has reached the end of the ... argument list; you need an explicit terminator.

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 中缺少一些东西.

Your problem is that QuickCheck was designed to take advantage of JavaScripts high dynamic programmability, something missing from C.

更新如果您允许任意函数签名,那么您需要一种方法使其再次成为静态,例如让调用者提供适当的适配器.

Update If you allow arbitrary function signatures, then you need a way to make it static again, say, by making the caller provide the appropriate adapters.

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);



}

这篇关于我们如何在 va_list 上应用非 vararg 函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆