如何在va_list上应用非vararg函数?

时间:2022-05-06 00:31:06

Backstory

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

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

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

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.

其中属性是要测试的函数,例如bool is_odd(int)。gen1、gen2等是为属性生成输入值的函数。有些生成整数,有些生成字符,有些生成字符串,等等。

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将接受一个具有任意输入(任意数量的参数,任意类型的参数)的函数。for_all将运行生成器,创建测试值以传递给属性函数。例如,属性is_odd是一个类型为bool f(int)的函数。for_all将使用生成来创建100个测试用例。如果其中任何一个属性返回false,则for_all将打印出错的测试用例值。否则,for_all将打印“SUCCESS”。

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?

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

Example

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

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

apply(is_odd, generated_values);

Secondary Issue

See 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\n", some_int, some_int, some_int);
  • printf(“%d %d %d %d\n”、some_int、some_int、some_int);
  • printf("%c\n" a_character);
  • printf(" % c \ n " a_character);
  • printf("%s%s\n", a_string, a_struct_requiring_its_own_printf_function);
  • printf(" % s % s \ n ",a_string,a_struct_requiring_its_own_printf_function);

1 个解决方案

#1


3  

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.

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 ...
    }
}

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

你的问题是,QuickCheck的设计利用了javascript的高动态可编程性,这是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);



}

#1


3  

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.

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 ...
    }
}

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

你的问题是,QuickCheck的设计利用了javascript的高动态可编程性,这是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);



}