#c #types
#c #типы
Вопрос:
Допустим, у меня есть следующий код:
struct test* t1;
t1 = get_t(1);
… где get_t
находится:
struct test* get_t(int);
Как я могу реорганизовать приведенный выше код и поместить его в функцию? Что-то вроде следующего:
void r1(?* t, ?* (fn*)(int)) {
t = fn(1);
}
/* ... */
struct test* t1;
r1(t1, amp;get_t);
Комментарии:
1. Ну, вы могли бы использовать
void r1(struct test* t, struct test* (fn*)(int))
… почему параметр неизвестного типа?2. Потому что я хочу использовать ту же функцию для, скажем,
struct another_test
. Точно так же, как я имеюstruct test* t1
и используюr1(t1, amp;get_t)
, я хочу иметь возможность также иметьstruct another_test* t2
и использоватьr1(t2, amp;get_at)
, гдеget_at
возвращаетstruct another_test*
и выполняет те же действия для его получения.
Ответ №1:
используйте void *param
указатель на что угодно … обычно используется в glib как gpointer
Комментарии:
1. Если я определяю
r1
какvoid r1(void* t, void* (fn*)(int))
, то компилятор жалуется, что я не предоставляю ему правильную функцию, поскольку мояget_t
функция возвращаетstruct test*
.2.
get_t
также необходимо вернутьvoid *
.3. @rdineiu: Вам также нужно соответствующим образом изменить вашу функцию get_t. Сигнатуру необходимо изменить, но ваша реализация может предполагать, что параметры имеют тот тип, который вам нужен.
4. Ага, значит, единственный способ сделать это — заставить
get_t
возвращатьvoid*
?5. Указатели Void действительно единственный способ написать полный универсальный код на C. Следует также отметить, что единственный случай, когда вам действительно нужно написать такой универсальный код, — это при разработке модулей абстрактного кода, таких как ADT и алгоритмы поиска / сортировки. Если вы обнаружите, что вам нужен полностью универсальный код в любой другой ситуации, вам, вероятно, следует пересмотреть дизайн вашей программы.
Ответ №2:
У меня есть две идеи:
[1] Передайте void
указатель на переменную / объект и введите приведение его в функции.
[2] Создайте объединение всех типов данных вместе с целочисленным типом данных, который определит, какая переменная типа данных в объединении содержит фактические данные. Передайте это объединение как значение или как void *
struct _unknown {
union {
int a;
float b;
char c;
double d;
} data;
int type;
} unknown;
.
.
.
if (unknown.type == FLOAT)
{
/* Process variable b */
}
else if (unknown.type == INT)
{
/* Process variable a */
}
.
.
.
Что-то вроде этого.
Вы можете хэшировать FLOAT
и INT
и другие как уникальные значения.
Или просто
struct _unknown {
void *data;
int type;
} unknown;
.
.
.
if (unknown == FLOAT)
{
/* process (float *) data */
}
else if (unknown == INT)
{
/* process (int *) data */
}
else if (unknown == MY_DATA_TYPE)
{
/* process (my_data_type *) data */
/* where my_data_type could be a typedef, or struct */
}
Ответ №3:
Если у вас есть gcc, вы можете использовать эту более типизированную версию:
#define r1(varp,func) ({
typeof(**varp)* (*_func_)(int);
typeof(**varp)* _varp_ = (varp);
_func_ = (func);
r1_((void**)(_varp_),(void*(*)(int))_func_);
})
void r1_(void** varp,void*(*func)(int))
{
*varp = func(1);
}
Вызовите как:
struct test* get_t(int);
struct test* t1;
r1(amp;t,get_t);
(Вам не нужно использовать amp;
для функций, они автоматически преобразуются в указатели, как массивы). Это проверяет, что t
является указателем, а это get_t
функция, возвращающая указатель этого типа. _varp_
технически не нужна, но сохраняет оценку аргументов в правильном порядке.
Редактировать:
Если у вас нет gcc, вы все равно можете это сделать, но вы должны указать тип явно:
#define r1(T,varp,func) do {
T*(*_func_)(int);
T* _varp_ = (varp);
_func_ = (func);
r1_((void**)(_varp_),(void*(*)(int))_func_);
} while(0)
void r1_(void** varp,void*(*func)(int))
{
*varp = func(1);
}
Вызовите как:
struct test* get_t(int);
struct test* t1;
r1(struct test*,amp;t,get_t);
не совсем такой безопасный и более избыточный, но все еще довольно хороший.