Одна функция для работы с двумя типами связанных списков

У меня есть два связанных списка:-

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;
Другие вопросы по тегам