Я использую библиотеку, которая вызывает мои функции. Эти функции делают в основном одно и то же; единственное отличие состоит в том, что они имеют другой тип структуры, передаваемой в функцию.
static void func_a(void * a, TypeA * b) {
...
printf("%d\n", b -> code);
}
static void func_b(void * a, TypeB * b) {
...
printf("%s\n", b -> string);
}
Есть ли способ в C избежать использования дублирующего кода в этом случае?
Вы можете написать одну функцию следующим образом:
enum struct_type {
A, B, // ...
};
static void func(void *a, void *b, enum struct_type st)
{
// ...
char const *str;
switch(st) {
case A:
str = ((TypeA *)b)->string;
case B:
str = ((TypeB *)b)->string;
// ...
}
printf("%s\n", str);
}
Это то, что вы ищете?
Один простой способ -
type
, который говорит вам, какой тип struct
вы хотите использовать.void*
для хранения необходимых struct
данных.Простой прототип будет таким:
static void func_common( void *a, void* data , int type )
{
/* data contains your required data structure */
/* type tells what type you want to use */
}
Внутри func_common
использование может работать с вашими необходимыми данными на основе type
if (type == STRCUT1)
printf("%d\n", *(int *)data->code);
else if (type == STRCUT2)
printf("%s\n", (char*) data->string);
Примечание. Вам нужно будет позаботиться о преобразовании void*
в требуемые типы в зависимости от того, что вы передаете.
Я проголосовал за ответ Питера , но хотел указать на возможность использования структуры «наследование» (также см. здесь) в качестве альтернативного решения.
Причина, по которой я бы предложил этот подход, заключается в том, что он обеспечивает лучшую безопасность типов во время компиляции, поскольку мы избегаем использования указателя void *
, и компилятор может проверять ошибки типов.
то есть:
typedef struct {
int type_id;
int i;
float f;
} common_s;
typedef struct {
common_s parent;
int code;
} type_A_s;
typedef struct {
common_s parent;
char *string;
} type_B_s;
static void func_common(void *a, common_s *b) { /* ... */ }
static void func_a(void *a, type_A_s *b) {
func_common(a, &b->parent);
printf("%d\n", b->code);
}
static void func_b(void *a, type_B_s *b) {
func_common(a, &b->parent);
printf("%s\n", b->string);
}
Это похоже на то, как ядро обрабатывает адреса сетевых сокетов, где типы адресов сокетов IP4, IP6 и Unix имеют общее поле заголовка (которое включает идентификатор типа адреса), а некоторые функции являются общими для всех типов, в то время как другие используют идентификатор типа. для маршрутизации адресного объекта к правильной функции.
Подход
func(void *a, void *b, int type)
теряет проверку типов и рискует несоответствием аргумента иtype
. Есть альтернатива_Generic
, но ОП, похоже, доволен текущими решениями. Чтобы лучше ответить, необходимы вызовы обратных вызовов библиотеки и требования.