¿Cómo puedo simular el polymorphism de OO en C?

¿Hay alguna forma de escribir código tipo OO en el C progtwigción C ?


Ver también:

  • ¿Puedes escribir código orientado a objetos en C?
  • Orientación a objetos en C

Se encuentra buscando en “[c] oo”.

El primer comstackdor de C ++ (“C con clases”) en realidad generaría código C, por lo que definitivamente es factible.

Básicamente, su clase base es una estructura; las estructuras derivadas deben incluir la estructura base en la primera posición, de modo que un puntero a la estructura “derivada” también sea un puntero válido a la estructura base.

 typedef struct { data member_x; } base; typedef struct { struct base; data member_y; } derived; void function_on_base(struct base * a); // here I can pass both pointers to derived and to base void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class 

Las funciones pueden ser parte de la estructura como punteros de función, de modo que una syntax como p-> call (p) sea posible, pero aún tiene que pasar explícitamente un puntero a la estructura a la función misma.

El enfoque común es definir struct con punteros a las funciones. Esto define ‘métodos’ que pueden invocarse en cualquier tipo. Los subtipos establecen sus propias funciones en esta estructura común y la devuelven.

Por ejemplo, en Linux kernel, hay struct:

 struct inode_operations { int (*create) (struct inode *,struct dentry *,int, struct nameidata *); struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); ... }; 

Cada tipo registrado de sistema de archivos registra sus propias funciones para create , lookup y funciones restantes. El rest del código puede usar las inode_operations genéricas:

 struct inode_operations *i_op; i_op -> create(...); 

C ++ no está tan lejos de C.

Las clases son estructuras con un puntero oculto a una tabla de indicadores de función llamada VTable. El Vtable en sí mismo es estático. Cuando los tipos apuntan a Vtables con la misma estructura pero donde los punteros apuntan a otra implementación, obtienes polymorphism.

Se recomienda encapsular la lógica de las llamadas en la función que toma la estructura como parámetro para evitar el desorden del código.

También debe encapsulctar la creación de instancias y la inicialización de estructuras en funciones (esto es equivalente a un constructor C ++) y la eliminación (destructor en C ++). Estas son buenas prácticas de todos modos.

 typedef struct { int (*SomeFunction)(TheClass* this, int i); void (*OtherFunction)(TheClass* this, char* c); } VTable; typedef struct { VTable* pVTable; int member; } TheClass; 

Para llamar al método:

 int CallSomeFunction(TheClass* this, int i) { (this->pVTable->SomeFunction)(this, i); } 

Miré las respuestas de todos los demás y se me ocurrió esto:

 #include  typedef struct { int (*get)(void* this); void (*set)(void* this, int i); int member; } TheClass; int Get(void* this) { TheClass* This = (TheClass*)this; return This->member; } void Set(void* this, int i) { TheClass* This = (TheClass*)this; This->member = i; } void init(TheClass* this) { this->get = &Get; this->set = &Set; } int main(int argc, char **argv) { TheClass name; init(&name); (name.set)(&name, 10); printf("%d\n", (name.get)(&name)); return 0; } 

Espero que responda algunas preguntas.

El Apéndice B del artículo Open Reusable Object Models , de Ian Piumarta y Alessandro Warth de VPRI es una implementación de un modelo de Objeto en GNU C, cerca de 140 líneas de código. ¡Es una lectura fascinante!

Aquí está la versión no almacenada de la macro que envía mensajes a objetos, usando una extensión de GNU a C ( expresión de statement ):

 struct object; typedef struct object *oop; typedef oop *(*method_t)(oop receiver, ...); //... #define send(RCV, MSG, ARGS...) ({ \ oop r = (oop)(RCV); \ method_t method = _bind(r, (MSG)); \ method(r, ##ARGS); \ }) 

En el mismo documento, eche un vistazo al object , vtable , vtable_delegated y structs de symbol , y las funciones _bind y vtable_lookup .

¡Aclamaciones!

Las funciones de archivo fopen, fclose, fread son ejemplos de código OO en C. En lugar de los datos privados en clase, funcionan en la estructura FILE que se utiliza para encapsular los datos y las funciones C actúan como funciones de clase miembro. http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

De Wikipedia: En los lenguajes de progtwigción y la teoría de tipos, el polymorphism (del griego πολύς, polys, “muchos, mucho” y μορφή, morphē, “forma, forma”) es la provisión de una única interfaz para entidades de diferentes tipos.

Así que diría que la única forma de implementarlo en C es mediante el uso de argumentos variados junto con alguna gestión de información de tipo (semi) automática. Por ejemplo, en C ++ puedes escribir (perdón por la trivialidad):

 void add( int& result, int a1, int a2 ); void add( float& result, float a1, float a2 ); void add( double& result, double a1, double a2 ); 

En C, entre otras soluciones, lo mejor que puede hacer es algo como esto:

 int int_add( int a1, int a2 ); float float_add( float a1, fload a2 ); double double_add( double a1, double a2 ); void add( int typeinfo, void* result, ... ); 

Entonces necesitas:

  1. para implementar el “typeinfo” con enums / macros
  2. para implementar la última función con stdarg.h cosas
  3. despedirse de la comprobación de tipo estático C

Estoy casi seguro de que cualquier otra implementación de polymorphism debería parecerse mucho a esta. ¡Las respuestas anteriores, en cambio, parecen intentar abordar la herencia más que el polymorphism!

 #include  typedef struct { int x; int z; } base; typedef struct { base; int y; int x; } derived; void function_on_base( base * a) // here I can pass both pointers to derived and to base { printf("Class base [%d]\n",a->x); printf("Class base [%d]\n",a->z); } void function_on_derived( derived * b) // here I must pass a pointer to the derived class { printf("Class derived [%d]\n",b->y); printf("Class derived [%d]\n",b->x); } int main() { derived d; base b; printf("Teste de poliformismo\n"); bx = 2; dy = 1; bz = 3; dx = 4; function_on_base(&b); function_on_base(&d); function_on_derived(&b); function_on_derived(&d); return 0; } 

La salida fue:

 Class base [3] Class base [1] Class base [4] Class derived [2] Class derived [3] Class derived [1] Class derived [4] 

así que funciona, es un código polimórfico.

UncleZeiv lo explicó al principio.

Para construir también la funcionalidad OO en C, puede ver las respuestas anteriores.

Pero, (como se ha preguntado en otras preguntas redirigidas a este) si quieres entender qué es el polymorphism, por ejemplos en lenguaje C. Tal vez estoy equivocado, pero no puedo pensar en algo tan fácil de entender como la aritmética de los indicadores C. En mi opinión, la aritmética del puntero es inherentemente polimórfica en C. En el siguiente ejemplo, la misma función (método en OO), es decir, la adición (+), producirá un comportamiento diferente según las propiedades de las estructuras de entrada.

Ejemplo:

 double a*; char str*; a=(double*)malloc(2*sizeof(double)); str=(char*)malloc(2*sizeof(char)); a=a+2; // make the pointer a, point 2*8 bytes ahead. str=str+2; // make the pointer str, point 2*1 bytes ahead. 

Descargo de responsabilidad: Soy muy nuevo en C y estoy ansioso por ser corregido y aprender de los comentarios de otros usuarios, o incluso borrar por completo esta respuesta, en caso de que sea incorrecta. Muchas gracias,