Как мы можем применить функцию без vararg к списку va_list? - PullRequest
4 голосов
/ 23 сентября 2011

Предыстория

Я портирую инфраструктуру модульного тестирования 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);

1 Ответ

3 голосов
/ 23 сентября 2011

Язык Си является языком статической типизации.Он не обладает способностями отражения во время выполнения, как это делают другие языки.Он также не предоставляет способов для создания произвольных вызовов функций из типов, предоставляемых во время выполнения.У вас должен быть какой-то способ узнать, что такое сигнатура функции 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 был разработан для использования преимуществ высокой динамической программируемости 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);



}
...