Получить указатель на функцию, вызывающую структуру

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

Надеюсь, это ответ на ваш вопрос.