Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
485 views
in Technique[技术] by (71.8m points)

c - How can we apply a non-vararg function over a va_list?

Backstory

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

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

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?

Example

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

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 ", some_int, some_int, some_int);
  • printf("%c " a_character);
  • printf("%s%s ", a_string, a_struct_requiring_its_own_printf_function);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

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



}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...