Использовать переменные функции в C89 без передачи числа аргументов или последнего аргумента? - PullRequest
2 голосов
/ 17 июля 2010

Допустим, у меня есть переменная функция foo(int tmp, ...), при вызове функции foo мне нужно знать, сколько существует аргументов. Мне известны два способа выяснить, сколько существует аргументов:

  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 был функцией с переменным числом и проверял, был ли expr (первый аргумент) одним из следующих типов (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, данная переменная функция при попытке получить аргументы expr_of_type не нашла -1 как Последний аргумент и продолжил поиск, создав ошибку, на которую у меня ушло некоторое время.

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

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

Ответы [ 3 ]

6 голосов
/ 17 июля 2010

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

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

#define VARARG_SENTINEL (-1)

Тогда nil_e VARARG_SENTINEL сгенерирует ошибку компиляции.

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

enum { VARARG_SENTINEL = -1 };

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

1 голос
/ 17 июля 2010

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

#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:

Упс, недостаточно внимательно прочитал ОП. Извините!

0 голосов
/ 17 июля 2010

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

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

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

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

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

...