#c #function #pointers #callback #struct
#c #функция #указатели #обратный вызов #структура
Вопрос:
Для текущего проекта я не могу использовать C (к сожалению), только чистый C, и у меня возникла следующая проблема:
У меня есть 3 отличительные структуры, которые должны вызывать одно и то же имя функции, и я чувствую, что это более элегантно, как показано ниже, вместо того, чтобы создавать 3 разные функции для разных типов, как мне кажется, их проще поддерживать. Как я могу получить указатель, который вызывает функцию? В принципе, то, что я пытаюсь воспроизвести, в основном похоже на «this» в C или self (в Obj-C), могу ли я в любом случае сделать это на обычном C?
[ test.h ]
enum
{
TYPE_A = 0,
TYPE_B = 1,
TYPE_C = 2
};
typedef struct
{
char type;
void ( *GetType )( void );
// Some other data different than the other struct
}A;
typedef struct
{
char type;
void ( *GetType )( void );
/* Some other data different than the other struct */
}B;
typedef struct
{
char type;
void ( *GetType )( void );
// Some other data different than the other struct
} C;
A *create_a( void );
B *create_b( void );
C *create_c( void );
void GetType( void );
[ test.c ]
A *create_a( void )
{
A *a = ( A * ) calloc( 1, sizeof( A ) );
a->type = TYPE_A;
a->GetType = GetType;
return a;
}
B *create_b( void )
{
B *b = ( B * ) calloc( 1, sizeof( B ) );
b->type = TYPE_B;
b->GetType = GetType;
return b;
}
C *create_c( void )
{
C *c = ( C * ) calloc( 1, sizeof( C ) );
c->type = TYPE_C;
c->GetType = GetType;
return c;
}
void GetType( void )
{
printf("How to get the pointer that call this function to retrieve the type?");
}
[ main.c ]
int main (int argc, const char * argv[])
{
A *a = create_a();
B *b = create_b();
C *c = create_c();
a->GetType();
b->GetType();
c->GetType();
return 0;
}
Комментарии:
1. Если вы зайдете достаточно далеко в эту кроличью нору, вы в конечном итоге заново изобретете C .
2. Но пока мы повторно внедряем C , вам, вероятно, следует использовать vtables вместо указателей на функции для каждого объекта.
Ответ №1:
Традиционный способ сделать такого рода вещи (например, в ядре UNIX) — использовать #define
для синтаксического сахара:
#define GET_TYPE(X) ((X)->GetType((X)))
Конечно, если есть больше аргументов, он передает и их. Указатели на функции обычно собираются в одну статическую структуру «ops», которая присваивается при создании (вместо каждого отдельного указателя на функцию). Каждый вектор операций имеет соответствующую коллекцию #defines
.
Ответ №2:
Передайте это как обычный параметр (во-первых, чтобы он был обычным):
void GetType(void *this);
...
GetType(a); // instead of C -style a->GetType
Было бы лучше иметь некоторый «базовый класс» (включая поле «int type»), в этом случае указателем будет «struct Base *this», а не «void * this»).
Лично я всегда иду этим путем. Вы можете добавить префикс для функций некоторого «класса» (например, Cl1_GetType). Это способ OO-программирования в C. Также можно использовать «Реальные виртуальные функции» с помощью указателей на функции, но это не такой распространенный случай.
Ответ №3:
«this» должно быть задано в качестве параметра для GetType(). Вот как объектные методы фактически реализованы под капотом. Более того, поскольку GetType должен иметь возможность взаимодействовать со всеми типами struct — структуры должны иметь аналогичный «базовый класс». например:
#include "stdio.h"
typedef enum
{
TYPE_A = 0,
TYPE_B = 1,
TYPE_C = 2
} my_class_type;
typedef struct
{
char type;
my_class_type ( *GetType )( void* );
}my_base;
my_class_type my_base_GetType( void* my_base_ptr)
{
return ((my_base*)my_base_ptr)->type;
}
typedef struct
{
my_base base;
// Some other data different than the other struct
}A;
typedef struct
{
my_base base;
/* Some other data different than the other struct */
}B;
typedef struct
{
my_base base;
// Some other data different than the other struct
}C;
A *create_a( void )
{
A *a = ( A * ) calloc( 1, sizeof( A ) );
a->base.type = TYPE_A;
a->base.GetType = my_base_GetType;
return a;
}
B *create_b( void )
{
B *b = ( B * ) calloc( 1, sizeof( B ) );
b->base.type = TYPE_B;
b->base.GetType = my_base_GetType;
return b;
}
C *create_c( void )
{
C *c = ( C * ) calloc( 1, sizeof( C ) );
c->base.type = TYPE_C;
c->base.GetType = my_base_GetType;
return c;
}
int main(int argc, char* argv[])
{
A *a = create_a();
B *b = create_b();
C *c = create_c();
printf("%dn",a->base.GetType(a));
printf("%dn",b->base.GetType(b));
printf("%dn",c->base.GetType(c));
return 0;
}
В этом примере реализованы наследование и полиморфизм. Все структуры используют одно и то же базовое ядро, и если вы хотите переопределить GetType(), вы просто меняете указатель в base. вы можете удерживать указатель на my_base и динамически разрешать тип.
Комментарии:
1. не могли бы вы, пожалуйста, подробнее рассказать о примере.
Ответ №4:
Из того, что я понял из вашего вопроса: «Вы просто хотите получить значение поля типа в структуре в функции GetType». Это правильно? Если да, вы можете сделать это следующим образом:
enum
{
TYPE_A = 0,
TYPE_B = 1,
TYPE_C = 2
};
typedef struct
{
char type;
// Some other data different than the other struct
}A;
typedef struct
{
char type;
/* Some other data different than the other struct */
}B;
typedef struct
{
char type;
// Some other data different than the other struct
} C;
A *create_a( void );
B *create_b( void );
C *create_c( void );
void GetType( void * );
[test.c]
A *create_a( void )
{
A *a = ( A * ) calloc( 1, sizeof( A ) );
a->type = TYPE_A;
return a;
}
B *create_b( void )
{
B *b = ( B * ) calloc( 1, sizeof( B ) );
b->type = TYPE_B;
return b;
}
C *create_c( void )
{
C *c = ( C * ) calloc( 1, sizeof( C ) );
c->type = TYPE_C;
return c;
}
#define GetType(x) (x->type)
[main.c]
int main (int argc, const char * argv[])
{
A *a = create_a();
B *b = create_b();
C *c = create_c();
GetType(a);
GetType(b);
GetType(c);
return 0;
}
Надеюсь, это ответ на ваш вопрос.