Как я могу создать функцию, которая принимает параметр неизвестного типа в C?

#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);
  

не совсем такой безопасный и более избыточный, но все еще довольно хороший.