Одна функция для работы с двумя типами связанных списков
У меня есть два связанных списка:-
struct Struct_A_s {
int a;
struct Struct_A_s *next;
}Struct_A;
struct Struct_B_s {
int a;
int b;
int c;
int d;
struct Struct_B_s *next;
}Struct_B;
У меня уже есть несколько функций, которые работают на Struct_A. Вещи как:
"Add_tail (Struct_A *)", "Remove_tail (Struct_B *)", "Add_node (Struct_A *, int pos), Add_head и т. Д. И т. Д. Находятся на месте.
Для меня требуется, чтобы я изменил существующие функции, чтобы они могли работать как в Struct_A, так и в Struct_B.
В C есть способ передать пустой указатель (или что-то подобное) и написать некоторый общий код для работы как с Struct_A, так и с Struct_B. Размер кода является большой проблемой здесь. В настоящее время я вижу только один вариант - переписать весь код (операции со связанным списком) с нуля для Struct_B.
3 ответа
Да, вы можете использовать void *
где вы в противном случае использовали бы либо Struct_A *
или же Struct_B *
, Вам нужно будет тщательно спроектировать свои API. Как правило, вы в конечном итоге работать как bsearch()
а также qsort()
брать один или несколько указателей на функции, которые выполняют структурно-специфические операции (сравнение в случае bsearch()
а также qsort()
).
Обратите внимание, что обход ваших связанных списков будет трудным, потому что следующий указатель находится в разных смещениях в структуре. Вы могли бы сделать лучше с фиксированной общей структурой списка, которая содержит следующий указатель и void *
к фактической структуре.
typedef struct List List;
struct List
{
List *next;
//List *prev; // Doubly-linked lists
void *data;
};
В этом разница между "навязчивыми" и "неинтрузивными" структурами списков. Ваш существующий дизайн использует навязчивые структуры списка; структура в списке изменена, чтобы включить указатель. Предлагаемый альтернативный дизайн не навязчив; Вы можете создать список любого типа структуры без изменения типа структуры.
Есть несколько способов сделать это. И я уверен, что другие, более опытные, чем я, могут предложить другим.
- Вариант A: передать пустой указатель и какое-то указание на тип данных, а затем привести его.
- Вариант B: объединить два указателя в союз и передать его.
- Вариант C: объединить тип и объединение в структуру и передать
- Вариант D: использовать более защищенную альтернативу союзу
Пример кода...
enum DataType { TYPE_1, TYPE_2 };
struct Type1 { ... };
struct Type2 { ... };
union Data
{
void* addr;
struct Type1* type1;
struct Type2* type2;
}
struct Object
{
DataType type;
union Data data;
}
struct Thing
{
DataType type;
void* data;
}
void someFunc1( void* data, DataType type )
{
switch( type )
{
case TYPE_1:
{
struct Type1* type1 = (struct Type1*)data;
...
break;
}
case TYPE_2:
{
struct Type2* type2 = (struct Type2*)data;
...
break;
}
}
}
void someFunc2( union Data* data, DataType type )
{
switch( type )
{
case TYPE_1:
{
struct Type1* type1 = data->type1;
...
break;
}
case TYPE_2:
{
struct Type2* type2 = data->type2;
...
break;
}
}
}
void someFunc3( struct Object* object )
{
switch( object->type )
{
case TYPE_1:
{
struct Type1* type1 = object->data.type1;
...
break;
}
case TYPE_2:
{
struct Type2* type2 = object->data.type2;
...
break;
}
}
}
void someFunc4( struct Thing* thing )
{
switch( thing->type )
{
case TYPE_1:
{
struct Type1* type1 = (struct Type1*)thing->data;
...
break;
}
case TYPE_2:
{
struct Type2* type2 = (struct Type2*)thing->data;
...
break;
}
}
}
Глядя на это по-другому... если у вас есть метод, который принимает только a
тогда вы можете сделать что-то вроде этого...
void myFunc( int a ) { ... };
void someFunc3( struct Object* object )
{
myFunc( object->type == TYPE_1 ? object->data.type1->a : object->data.type2->a );
}
Еще лучше... изменить функцию так, чтобы она воспринимала объект и различалась только снизу...
void myFunc( struct Object* object )
{
int* a;
switch( object->type )
{
case TYPE_1:
{
a = &object->data.type1->a
break;
}
case TYPE_2:
{
a = &object->data.type2->a;
break;
}
default:
{
abort();
}
}
// do work with a as though it were passed in as a pointer to int
if( object->type == TYPE_2 )
{
// do additional work with the b, c, d elements, etc.
}
}
Вы можете переопределить структуры, как показано ниже. Вы должны выполнить много приведений в зависимости от типа, чтобы изменить все ваши прежние функции для работы с обоими. Вам нужно использовать Struct_A * для обоих.
struct Struct_A_s {
int type;
Struct_A_s *next;
int a;
}Struct_A;
struct Struct_B_s {
int type;
Struct_A *next;
int a;
int b;
int c;
int d;
}Struct_B;