небольшая проблема с C - PullRequest
3 голосов
/ 09 июня 2011

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

Например, если у меня естьэто:

void func(int value_to_compare, int compared_value, int arg_number, char *arg_types, char *function_name, ...)

Я могу назвать это следующим образом:

char arg1;
int arg2, arg3;
int condition1;

func(condition1, 1, 1, "c", func1, arg1);
func(condition1, 0, 2, "ci", func2, arg1, arg2);
func(arg2, 0 , 3, "cii", func3, arg1, arg2, arg3);

и вызываемые функции имеют вид:

func1(char);
func2(char, int);
func3(char, int, int);

Я попытаюсь объяснить больше, если янапример:

struct element_list {
char *element_type;
char *element_name;
}

структуры в моем случае имеют большое количество элементов, условие должно быть передано в качестве аргумента функции, а значение условия зависит от контекста, поэтому я долженчтобы иметь возможность вызывать 'func' в любом контексте, я просто даю условие и значение, и затем функция будет вызываться с соответствующими аргументами, так что это будет что-то вроде

func(element_list.element_type, "CHAR", func1, "s", element_list.element_name);

в дополнение ку меня огромное количество функций и огромное количество условий, поэтому мне нужно что-то настолько универсальное, насколько это возможно.

Ответы [ 3 ]

4 голосов
/ 09 июня 2011

Что бы вы ни делали, сейчас вы работаете с системой типов. Не существует безопасного для типов способа делать то, что вы хотите в C.

Имея это в виду:

Одним из возможных решений является создание всех этих функций одного типа: typedef void my_func_type(void*) и пусть каждая функция «дешифрует» параметр самостоятельно.

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


Продолжение, # 1

Из того, что я понял, вы захотите написать такой func, что этот код

char arg1;
int arg2, arg3;
int condition1;

func(condition1, 1, 1, "c", func1, arg1);
func(condition1, 0, 2, "ci", func2, arg1, arg2);
func(arg2, 0 , 3, "cii", func3, arg1, arg2, arg3);

будет вести себя как:

if (condition1 == 1) func1(arg1);
if (condition1 == 0) func2(arg1, arg2);
if (arg2 == 0)       func3(arg1, arg2, arg3);

Честно говоря ... Лучший способ - написать это так. : -)

Поскольку вы задаете этот вопрос, скорее всего, вы хотите что-то более сложное, чем это. Ситуация такова: вы хотите построить некоторую абстракцию над потоком управления вашего кода . Вот где пригодится стиль функционального программирования ... На самом деле, в C ++ все будет просто (ну, не совсем тривиально, но выполнимо безопасным и переносимым способом, с некоторыми шаблонными волшебными странными функциями с bind в их именах).

Мы говорим о C, а не C ++, об шаблонах, функторах ... так что, возможно, дело сложнее. Но обо всем по порядку - что мешает вам использовать простое решение с if 10 * *? Ответ на этот вопрос - хорошее начало.

1 голос
/ 09 июня 2011

У вас есть два варианта. Предпочтительным является передача вашего ввода через struct, который имеет тип ввода и различные значения, определенные для этого типа ввода. Затем вы просто передаете struct своей функции (по ссылке / указателю).

Второй вариант - отформатировать аргументы, чтобы их можно было прочитать, используя va_start, va_arg и va_end. Чтобы использовать это, вам нужно знать тип следующего аргумента, поэтому вам нужно будет передать его в качестве ввода. Вам также нужно заранее знать, когда будет достигнут последний аргумент, поэтому вам снова нужно будет передать это в качестве входных данных. Это то же самое, что работает printf, и почему вам нужно передать строку формата в printf.

0 голосов
/ 13 июня 2011

Хорошо, вот как я это сделал

void func(int value_type, void *value_to_compare, void *compared_value, char *arg_types, void (*function_name)(), ...)

Я делаю переключение на value_type, в зависимости от типа, я делаю приведение к value_to_compare, и сравнение_value, я сохраняю эти значения в двух разных объединениях, а затем сравниваю их, и в случае, если значения равны, я вызываю Функция имя_функции. Что касается вызываемой функции, я делаю приведение типов в зависимости от arg_types, а затем делаю что-то очень глупое, например:

switch(strlen(arg_types)) {
case 0:
function_name();
break;
case 1:
function_name(args[0]);
break;
case 2:
function_name(args[0], args[1]);
break;
...
}

где args

void* args[MAX_NUMBER_ARGS];

Я беру каждый элемент массива args и в зависимости от arg_types я выполняю приведение, но не могу найти лучшего решения ...

...