#c #variadic-functions
#c #переменные функции
Вопрос:
В настоящее время я создаю свой собственный графический интерфейс, используя библиотеку SDL2 для программы, которую я написал на C (c89).
Проблема
Я хочу позволить пользователю моего кода передавать свою собственную функцию обработчика кликов, которая вызывалась бы каждый раз, когда событие click запускается на поверхности соответствующей кнопки.
Что я пробовал
Я думал, что мог бы использовать указатели на функции следующим образом :
bind_ClickHandler(void (*function)(void)) {/* bind it to a button */}
но, очевидно, это слишком ограничивает пользователя.
Итак, я попытался использовать stdarg.h :
/* ... : arguments to pass to the click handler */
bind_ClickHandler(void (*function)(), int nb_args, ...)
{
void *arg_pt;
va_list args;
va_start(args, nb_args);
/* I don't know what to do so i cast it as a garbage pointer */
arg_pt = va_arg(args, void *);
/* repeat this in a loop to save the args and bind the click handler to a button */
}
С помощью этого кода я могу передавать аргументы обработчику кликов, но только в том случае, если я знаю их типы.
Чего я хочу
Я хочу использовать что-то вроде второго фрагмента кода, который я вам показал, чтобы сохранить аргументы для передачи обработчику кликов, но это не работает, потому что мне нужно знать типы аргументов, чтобы использовать va_arg().
Ответ №1:
Просто передайте один void*
указатель. Например, qsort_r
или pthread_create
[*]. Пользователю нужен только один указатель — и этот указатель будет указывать куда угодно, на что захочет пользователь.
void (*clickhandler_user_function)(void *) = NULL;
void *clickhandler_user_function_arg = NULL;
void bind_ClickHandler(void (*function)(void*arg), void *function_arg) {
// store function and fucntion_arg somewhere
clickhandler_user_function = function;
clickhandler_user_function_arg = function_arg;
}
Достаточно одного указателя:
struct user_context_s {
int some_number;
const char *some string;
// etc.
};
void user_function_that_operates_on_user_context(void *p) {
struct user_context_s *ctx = p;
printf("My number is: %dn", ctx->some_number);
}
int main() {
// user can use malloc() and manage the lifetime of it's context
struct user_context_s ctx = {1, "blabla"};
bind_ClickHandler(user_function_that_operates_on_user_context, amp;user_context);
}
[*] — или thrd_create
или fopencookie
использует void *cookie
или стандарт sigevent
использует void *sival_ptr;
in sigval
для передачи контекста, когда sigev_notify == SIGEV_THREAD
.
Комментарии:
1. Итак, я должен просто позволить пользователю передать указатель на аргументы и вызвать обработчик кликов с этим указателем? Это здорово, но для этого пользователю необходимо разработать свою функцию, чтобы использовать этот указатель, если он хочет использовать графический интерфейс.
2.
i have just to let the user pass a pointer to the args and i call the click handler with this pointer ?
ДА.but it requires the user to design its function to use this pointer if he wants to use the GUI
ДА. C не имеет замыканий .3. Мне нравится ваше решение, потому что оно позволяет снизить сложность с моей стороны, но я хотел бы знать, есть ли способ разрешить пользователю передавать аргументы моей функции bind , как если бы он вызывал обработчик кликов.
4. Нет. C не имеет замыканий. Именно для этого в C существуют
std::bind
иstd::function
и лямбды. Вы могли бы реализовать это, заранее зная тип и количество аргументов, передаваемых функции, и выделив память для хранения значений аргументов. И это то, для чегоtemplate
предназначен C и чтоstd::bind
на самом деле делает. Это C. C не имеет шаблонов. C не имеет замыканий.