2010-07-17 3 views
2

Предположим, что у меня есть вариационная функция foo(int tmp, ...), при вызове функции foo мне нужно знать, сколько аргументов есть. Я знаю два способа узнать, сколько аргументов есть:Использовать вариационные функции в C89 без прохождения числа аргументов или окончательного аргумента?

  1. Используйте последний аргумент при вызове Foo, как -1, так что ваш вызов функции будет выглядеть следующим образом: foo(tmp, 1, 2, 9, -1) и когда вы внутри foo и va_arg вызывает -1, вы знаете, что вы прочитали все аргументы функции.

  2. Добавить еще один аргумент в foo, где у программиста будет общее количество аргументов, поэтому у вас будут вызовы для foo следующим образом: foo(tmp, 5, 1, 2, 3, 4, 5) или foo(tmp, 2, 7, 8)

Я привык следовать первому пути и однажды имел следующую ошибку. С кодом:

expr_of_type(expr, boolexpr_e, newtable_e, nil_e, -1) 

где expr_of_type был VARIADIC функция и была проверка, если выражение (первый аргумент) был один из следующих типов (boolexpr_e или new_table_e или nil_e имели все типы перечислимого типа). Я один случайно написал:

expr_of_type(expr, boolexpr_e, newtable_e, nil_e -1) 

Я забыл запятую между nil_e и -1, потому что nil_e был перечисляемого типа, nil_e - 1 является действительным выражением и потому nil_e не было 0 данная функция VARIADIC при попытке get expr_of_type аргументы не нашли -1 в качестве последнего аргумента и продолжали поиск, создав ошибку, которая потребовала мне времени, чтобы узнать.

Я тоже не считаю, что второй способ хорош, потому что при добавлении или удалении еще одного аргумента из вариационной функции вам нужно изменить параметр, содержащий количество общих аргументов.

В поисках лучшего способа использования/создания вариационных функций я нашел variadic macros, который может решить ошибку, которую я использовал при первом использовании. Но переменные макросы доступны для стандарта C99. Я искал лучший способ использовать/создавать вариативные функции в C89. Есть идеи?

ответ

6

В общем, вы все равно должны проходить по счету аргументов, как через значение дозорного, так и через явное подсчет.

Вы можете, однако, исправить свою проблему дозорного, сделав лучший страж. Это одна из причин, почему макросы препроцессора, которые расширяют до отрицательных констант должны быть окруженными в скобках:

#define VARARG_SENTINEL (-1) 

Тогда nil_e VARARG_SENTINEL вызовет ошибку компиляции.

Использование enum или const int будет работать тоже:

enum { VARARG_SENTINEL = -1 }; 

Используя символическую константу для значения дозорного будет лучше по другим причинам тоже (более самодокументирован, легче изменить базовое значение позже).

+0

Использование 'enum' или' const int 'приведет к такой же проблеме при забывании запятой? –

+1

Нет, не будет. Если у вас есть 'const int VARARG_SENTILEL = -1;', когда вы забыли запятую, у вас будет что-то вроде этого 'nil_e VARARG_SENTINEL', которое дает ошибку компиляции. То же самое происходит, когда вы используете 'enum {VARARG_SENTINEL = -1};'. С обоими решениями при забывании запятой это похоже на 'number1 number2' (например,' 2 3'), который является недопустимым выражением, но при использовании -1, как я использовал, у вас будет 'номер-1', который является действительное выражение. –

0

Существует также возможность избежать полностью вариационных параметров с использованием динамической структуры.

struct vararray { 
    uint_t n; 
    uint_t params[0]; 
}; 

void foo(int tmp, struct varray *pVA); 

Может быть даже Комплексифицированное с union из структур различных размеров.

У нас был встроенный контроллер с конкретным API, в котором мы использовали такой подход: union фиксированного размера struct, который был передан обработчику событий. Это имело некоторые преимущества, поскольку конкретные типы могли быть использованы, и компилятор мог лучше проверить типы параметров функции, потому что не следует забывать, что нет проверки типов параметров для переменных функций.

0

Если ваши компиляции C99, вы можете использовать VARIADIC макросы, чтобы обеспечить переменные аргументы без необходимости проходить подсчет явно:

#include <stdio.h> 
#include <stdarg.h> 

void _foo(size_t n, int xs[]) 
{ 
    for(int i=0 ; i < n ; i++) { 
     int x = xs[i]; 
     printf("%d\n", x); 
    }   
} 

#define foo(arg1, ...) do {   \ 
    int _x[] = { arg1, __VA_ARGS__ }; \ 
    _foo(sizeof(_x)/sizeof(_x[0]), _x); \ 
} while(0) 

int main() 
{ 
    foo(1, 2, 3, 4); 
    foo(-1, -2, -3, -4, -5, -6, -7); 
    return 0; 
} 

Выход:

1 
2 
3 
4 
-1 
-2 
-3 
-4 
-5 
-6 
-7 

Это предотвращает возвращение значения, Однако. Вы можете вернуть значение с помощью GCC расширений:

#include <stdio.h> 
#include <stdarg.h> 

int _foo(size_t n, int xs[]) 
{ 
    int i; 
    for(i=0 ; i < n ; i++) { 
     int x = xs[i]; 
     printf("%d\n", x); 
    }   
    return n; 
} 

#define foo(arg1, ...) ({    \ 
    int _x[] = { arg1, __VA_ARGS__ }; \ 
    _foo(sizeof(_x)/sizeof(_x[0]), _x); \ 
}) 

int main() 
{ 
    int x = foo(1, 2, 3, 4); 
    printf("foo returned %d\n", x); 
    x = foo(-1, -2, -3, -4, -5, -6, -7); 
    printf("foo returned %d\n", x); 
    return 0; 
} 

Выход:

1 
2 
3 
4 
foo returned 4 
-1 
-2 
-3 
-4 
-5 
-6 
-7 
foo returned 7 

Но, конечно, макросы мертвы. Да здравствует макрос!

EDIT:

К сожалению, не читал ОП достаточно тщательно. Сожалею!

 Смежные вопросы

  • Нет связанных вопросов^_^