предлагает ли varargs своего рода полиморфизм бедняков? - PullRequest
6 голосов
/ 17 декабря 2010

Нет сомнений, что каждый другой ученик С заметил это;для меня это ново.

Если я объявляю:

int xlate( void *, ... );

, а затем определяю xlate( ) несколькими различными способами (возможно, все определения, кроме одного, #ifdef исключены):

int xlate ( char *arg1 ) { ... }

int xlate ( int arg1, char *arg2, int arg3 ) { ... }

int xlate ( char arg1, int *arg2 ) { ... }

и опускать любое упоминание va_list - никогда не упоминая его - в каждом определении xlate ();и затем вызывать xlate (), соблюдая одно из нескольких его определений, кажется, что каждая скомпилированная версия xlate () работает так, как я хочу, по крайней мере в gcc и msvc.щедрое поведение компилятора гарантировано под C99?

Спасибо!

- Пит

Ответы [ 5 ]

4 голосов
/ 17 декабря 2010

Нет, это скорее перегруз бедняка . Полиморфизм (способность выполнять действие над несколькими типами объектов и каждый из них делает свое дело правильно) в C обычно делается с структурами, содержащими указатели на функции .

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

Другими словами, что-то вроде:

printf ("%d: %s\n", int1, charpointer2);
x = sum_positive_values (1, 2, 3, 4, 5, -1);
3 голосов
/ 17 декабря 2010

В дополнение к ответу caf:

Это не может работать из-за нескольких проблем, и стандарт не может сделать ничего, кроме как запретить такую ​​вещь:

Прототипы сообщаютвызывающая сторона, как преобразования аргументов должны выполняться при вызове функции.Уже приведенный вами пример не будет надежно работать для первого параметра.Вы объявляете это void*, а затем int в другом.Поскольку оба могут иметь разную ширину, ваш код обречен на сбой на большинстве 64-битных архитектур.

Еще хуже, нотация ... говорит вызывающей стороне применять продвижение по умолчанию для оставшихся аргументов.Например, если ваша реализация ожидает float, вызывающая сторона всегда будет предоставлять double, и, опять же, ваш код будет аварийно завершать работу (= в последнее время).

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

3 голосов
/ 17 декабря 2010

Нет, такое поведение не гарантировано стандартом.Соответствующий текст содержится в §6.5.2.2:

9 Если функция определена с типом, который не совместим с типом (выражения), на который указывает выражение, обозначающее вызываемую функциюповедение не определено.

Некоторые платформы должны использовать другое соглашение о вызовах при вызове функций varargs, потому что их обычное соглашение о вызовах требует, чтобы вызываемый объект знал, сколько фактических аргументов было передано.Стандарт C был написан специально для этого - поэтому функции varargs можно вызывать только через правильно типизированное объявление varargs, а выражения, обозначающие функции varargs, можно использовать только для вызова функций varargs.

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

3 голосов
/ 17 декабря 2010

Если вы объявляете xlat как void *, вы не можете просто пойти и реализовать его с int.Даже перегрузка бедняков должна выполняться правильно и, возможно, будет выглядеть как

enum {
        T_FOO,
        T_BAR,
};

void xlat(enum tp type, ...)
{
        struct foo *foo;
        struct bar *bar;
        va_list argp;
        va_start(argp, type);
        if (type == T_FOO) {
                foo = va_arg(argp, struct foo *);
                do_something_with_foo;
        } else if (type == T_BAR) {
                bar = va_arg(argp, struct bar *);
                do_something_with_bar;
        }
}

Хотя я думаю, это больше похоже на перегрузку, чем на полиморфизм.

2 голосов
/ 17 декабря 2010

Это не тот компилятор, который заставляет работать этот забавный бизнес настолько, насколько это ABI платформы, для которой вы компилируете.Вызов функции с переменным числом аргументов выполняется с использованием тех же правил, что и при любом другом вызове функции, поэтому вполне понятно, что если вы передадите правильное число и тип аргументов, даже если вызывающая функция сочтет, что она является переменной, получающая функция сможет понятьаргументы корректны (даже если принимающая функция не является переменной).

...