¿Cuáles son algunas formas de escribir código orientado a objetos en C? Especialmente con respecto al polimorfismo.
Véase también esta pregunta desbordamiento de pila La orientación a objetos en C .
¿Cuáles son algunas formas de escribir código orientado a objetos en C? Especialmente con respecto al polimorfismo.
Véase también esta pregunta desbordamiento de pila La orientación a objetos en C .
Respuestas:
Si. De hecho, Axel Schreiner proporciona su libro "Programación orientada a objetos en ANSI-C" de forma gratuita, que cubre el tema con bastante profundidad.
Ya que estás hablando de polimorfismo, entonces sí, puedes, estábamos haciendo ese tipo de cosas años antes de que surgiera C ++.
Básicamente, utiliza a struct
para mantener los datos y una lista de punteros de función para apuntar a las funciones relevantes para esos datos.
Por lo tanto, en una clase de comunicaciones, tendría una llamada abierta, de lectura, escritura y cierre que se mantendría como cuatro punteros de función en la estructura, junto con los datos para un objeto, algo así como:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
Por supuesto, esos segmentos de código anteriores estarían realmente en un "constructor" como rs232Init()
.
Cuando 'hereda' de esa clase, simplemente cambia los punteros para señalar sus propias funciones. Todos los que llamaron a esas funciones lo harían a través de los punteros de función, dándole su polimorfismo:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Algo así como una vtable manual.
Incluso podría tener clases virtuales configurando los punteros a NULL: el comportamiento sería ligeramente diferente a C ++ (un volcado de núcleo en tiempo de ejecución en lugar de un error en tiempo de compilación).
Aquí hay un fragmento de código de muestra que lo demuestra. Primero la estructura de clase de nivel superior:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Luego tenemos las funciones para la 'subclase' de TCP:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
Y el HTTP también:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
Y finalmente un programa de prueba para mostrarlo en acción:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
Esto produce la salida:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
para que pueda ver que se llaman las diferentes funciones, dependiendo de la subclase.
tCommClass
, se cambiaría su nombre tCommVT
y una tCommClass
estructura solo tendría campos de datos y un solo tCommVT vt
campo que apuntara a la "única tabla virtual". Llevar todos los punteros con cada instancia agrega una sobrecarga innecesaria y se asemeja más a cómo haría cosas en JavaScript que C ++, en mi humilde opinión.
Los espacios de nombres a menudo se hacen haciendo:
stack_push(thing *)
en vez de
stack::push(thing *)
Para convertir una estructura C en algo así como una clase C ++ , puede convertir:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
Dentro
struct stack {
struct stack_type * my_type;
// Put the stuff that you put after private: here
};
struct stack_type {
void (* construct)(struct stack * this); // This takes uninitialized memory
struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
int this_is_here_as_an_example_only;
}Stack = {
.construct = stack_construct,
.operator_new = stack_operator_new,
.push = stack_push,
.pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else
Y hacer:
struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
// Do something about it
} else {
// You can use the stack
stack_push(st, thing0); // This is a non-virtual call
Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
st->my_type.push(st, thing2); // This is a virtual call
}
No hice el destructor ni eliminé, pero sigue el mismo patrón.
this_is_here_as_an_example_only es como una variable de clase estática, compartida entre todas las instancias de un tipo. Todos los métodos son realmente estáticos, excepto que algunos toman esto *
st->my_type->push(st, thing2);
lugar dest->my_type.push(st, thing2);
struct stack_type my_type;
lugar destruct stack_type * my_type;
Class
estructura genérica ? Eso haría que el OO C sea más dinámico que C ++. ¿Qué hay sobre eso? Por cierto, +1.
Creo que, además de ser útil por derecho propio, implementar OOP en C es una excelente manera de aprender OOP y comprender su funcionamiento interno. La experiencia de muchos programadores ha demostrado que para usar una técnica de manera eficiente y segura, un programador debe comprender cómo se implementan los conceptos subyacentes. Emular clases, herencia y polimorfismo en C enseña exactamente esto.
Para responder a la pregunta original, aquí hay un par de recursos que enseñan cómo hacer POO en C:
La publicación del blog EmbeddedGurus.com "Programación basada en objetos en C" muestra cómo implementar clases y herencia única en C portátil: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c / /
La nota de aplicación "" C + "- Programación orientada a objetos en C" muestra cómo implementar clases, herencia única y enlace tardío (polimorfismo) en C utilizando macros de preprocesador: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , el código de ejemplo está disponible en http://www.state-machine.com/resources/cplus_3.0.zip
Lo he visto hecho. No lo recomendaría C ++ originalmente comenzó de esta manera como un preprocesador que produjo código C como un paso intermedio.
Esencialmente, lo que termina haciendo es crear una tabla de despacho para todos sus métodos donde almacena sus referencias de función. Obtener una clase implicaría copiar esta tabla de despacho y reemplazar las entradas que desea anular, con sus nuevos "métodos" teniendo que llamar al método original si desea invocar el método base. Finalmente, terminas reescribiendo C ++.
glib
escrito en C de manera objetiva?
Claro que es posible. Esto es lo que hace GObject , el marco en el que se basan todos los GTK + y GNOME .
La subbiblioteca C stdio FILE es un excelente ejemplo de cómo crear abstracción, encapsulación y modularidad en C. no adulterado
La herencia y el polimorfismo, los otros aspectos que a menudo se consideran esenciales para la OOP, no proporcionan necesariamente los aumentos de productividad que prometen y se han hecho argumentos razonables de que pueden obstaculizar el desarrollo y pensar sobre el dominio del problema.
Ejemplo trivial con un animal y un perro: usted refleja el mecanismo vtable de C ++ (en gran medida de todos modos) También separa la asignación y la creación de instancias (Animal_Alloc, Animal_New) para que no llamemos a malloc () varias veces. También debemos pasar explícitamente elthis
puntero.
Si tuviera que hacer funciones no virtuales, eso es trivial. Simplemente no los agrega a vtable y las funciones estáticas no requieren unthis
puntero. La herencia múltiple generalmente requiere múltiples vtables para resolver ambigüedades.
Además, debería poder usar setjmp / longjmp para hacer el manejo de excepciones.
struct Animal_Vtable{
typedef void (*Walk_Fun)(struct Animal *a_This);
typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);
Walk_Fun Walk;
Dtor_Fun Dtor;
};
struct Animal{
Animal_Vtable vtable;
char *Name;
};
struct Dog{
Animal_Vtable vtable;
char *Name; // Mirror member variables for easy access
char *Type;
};
void Animal_Walk(struct Animal *a_This){
printf("Animal (%s) walking\n", a_This->Name);
}
struct Animal* Animal_Dtor(struct Animal *a_This){
printf("animal::dtor\n");
return a_This;
}
Animal *Animal_Alloc(){
return (Animal*)malloc(sizeof(Animal));
}
Animal *Animal_New(Animal *a_Animal){
a_Animal->vtable.Walk = Animal_Walk;
a_Animal->vtable.Dtor = Animal_Dtor;
a_Animal->Name = "Anonymous";
return a_Animal;
}
void Animal_Free(Animal *a_This){
a_This->vtable.Dtor(a_This);
free(a_This);
}
void Dog_Walk(struct Dog *a_This){
printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}
Dog* Dog_Dtor(struct Dog *a_This){
// Explicit call to parent destructor
Animal_Dtor((Animal*)a_This);
printf("dog::dtor\n");
return a_This;
}
Dog *Dog_Alloc(){
return (Dog*)malloc(sizeof(Dog));
}
Dog *Dog_New(Dog *a_Dog){
// Explict call to parent constructor
Animal_New((Animal*)a_Dog);
a_Dog->Type = "Dog type";
a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;
return a_Dog;
}
int main(int argc, char **argv){
/*
Base class:
Animal *a_Animal = Animal_New(Animal_Alloc());
*/
Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());
a_Animal->vtable.Walk(a_Animal);
Animal_Free(a_Animal);
}
PD. Esto se prueba en un compilador de C ++, pero debería ser fácil hacer que funcione en un compilador de C.
typedef
dentro de a struct
no es posible en C.
Echa un vistazo a GObject . Está destinado a ser OO en C y una implementación de lo que estás buscando. Sin embargo, si realmente quieres OO, ve con C ++ o algún otro lenguaje OOP. A veces es muy difícil trabajar con GObject si estás acostumbrado a lidiar con lenguajes OO, pero como todo, te acostumbrarás a las convenciones y al flujo.
Esto ha sido interesante de leer. He estado reflexionando sobre la misma pregunta, y los beneficios de pensarlo son los siguientes:
Intentar imaginar cómo implementar conceptos de OOP en un lenguaje que no sea de OOP me ayuda a comprender las fortalezas del lenguaje de OOP (en mi caso, C ++). Esto me ayuda a juzgar mejor si usar C o C ++ para un tipo de aplicación dado, donde los beneficios de uno superan al otro.
En mi navegación por la web para obtener información y opiniones sobre esto, encontré un autor que estaba escribiendo código para un procesador incorporado y solo tenía un compilador de C disponible: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Creación-Fundación-Clases-Parte-1
En su caso, analizar y adaptar los conceptos de OOP en el plano C era una búsqueda válida. Parece que estaba abierto a sacrificar algunos conceptos de OOP debido al impacto en el rendimiento que resulta de intentar implementarlos en C.
La lección que he tomado es, sí, se puede hacer hasta cierto punto, y sí, hay algunas buenas razones para intentarlo.
Al final, la máquina está haciendo girar los bits del puntero de la pila, haciendo que el contador del programa salte y calculando las operaciones de acceso a la memoria. Desde el punto de vista de la eficiencia, cuantos menos cálculos realice su programa, mejor ... pero a veces tenemos que pagar este impuesto simplemente para poder organizar nuestro programa de una manera que lo haga menos susceptible al error humano. El compilador de lenguaje OOP se esfuerza por optimizar ambos aspectos. El programador debe ser mucho más cuidadoso al implementar estos conceptos en un lenguaje como C.
Puede que le resulte útil consultar la documentación de Apple para su conjunto de API de Core Foundation. Es una API de C pura, pero muchos de los tipos están unidos a equivalentes de objeto Objective-C.
También puede resultarle útil observar el diseño de Objective-C. Es un poco diferente de C ++ en que el sistema de objetos se define en términos de funciones de C, por ejemplo, objc_msg_send
para llamar a un método en un objeto. El compilador traduce la sintaxis de corchetes en esas llamadas de función, por lo que no tiene que saberlo, pero teniendo en cuenta su pregunta, puede resultarle útil aprender cómo funciona bajo el capó.
Hay varias técnicas que se pueden usar. El más importante es más cómo dividir el proyecto. Utilizamos una interfaz en nuestro proyecto que se declara en un archivo .h y la implementación del objeto en un archivo .c. La parte importante es que todos los módulos que incluyen el archivo .h ven solo un objeto comovoid *
, y el archivo .c es el único módulo que conoce las internas de la estructura.
Algo así para una clase que llamamos FOO como ejemplo:
En el archivo .h
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
El archivo de implementación C será algo así.
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
Entonces le doy el puntero explícitamente a un objeto para cada función de ese módulo. Un compilador de C ++ lo hace implícitamente, y en C lo escribimos explícitamente.
Realmente uso this
en mis programas, para asegurarme de que mi programa no se compila en C ++, y tiene la excelente propiedad de estar en otro color en mi editor de resaltado de sintaxis.
Los campos de FOO_struct se pueden modificar en un módulo y no es necesario volver a compilar otro módulo para poder seguir utilizándolo.
Con ese estilo ya manejo una gran parte de las ventajas de OOP (encapsulación de datos). Al usar punteros de función, incluso es fácil implementar algo como la herencia, pero honestamente, en realidad rara vez es útil.
typedef struct FOO_type FOO_type
lugar de un typedef para anular en el encabezado, obtendrá el beneficio adicional de la verificación de tipos, sin exponer su estructura.
Puede simularlo utilizando punteros de función y, de hecho, creo que es teóricamente posible compilar programas C ++ en C.
Sin embargo, rara vez tiene sentido forzar un paradigma en un idioma en lugar de elegir un idioma que utilice un paradigma.
Se puede hacer C orientado a objetos, he visto ese tipo de código en producción en Corea, y fue el monstruo más horrible que había visto en años (esto fue como el año pasado (2007) cuando vi el código). Entonces sí, se puede hacer, y sí, las personas lo han hecho antes, y aún lo hacen incluso hoy en día. Pero recomendaría C ++ u Objective-C, ambos son lenguajes nacidos de C, con el propósito de proporcionar orientación a objetos con diferentes paradigmas.
Si está convencido de que un enfoque OOP es superior para el problema que está tratando de resolver, ¿por qué trataría de resolverlo con un lenguaje que no sea OOP? Parece que estás usando la herramienta incorrecta para el trabajo. Utilice C ++ o algún otro lenguaje de variantes C orientado a objetos.
Si está preguntando porque está comenzando a codificar un proyecto grande ya existente escrito en C, entonces no debe tratar de forzar sus propios paradigmas OOP (o los de cualquier otra persona) en la infraestructura del proyecto. Siga las pautas que ya están presentes en el proyecto. En general, las API limpias y bibliotecas y módulos aislados que recorrer un largo camino para tener un OOP- limpia ish diseño.
Si, después de todo esto, realmente está listo para hacer OOP C, lea esto (PDF).
Sí tu puedes. La gente escribía C orientado a objetos antes de que C ++ u Objective-C aparecieran en escena. Tanto C ++ como Objective-C fueron, en parte, intentos de tomar algunos de los conceptos de OO utilizados en C y formalizarlos como parte del lenguaje.
Aquí hay un programa realmente simple que muestra cómo puedes hacer algo que se vea como una llamada a un método (hay mejores formas de hacerlo. Esto es solo una prueba de que el lenguaje admite los conceptos):
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
Un pequeño código OOC para agregar:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
He estado cavando esto por un año:
Como el sistema GObject es difícil de usar con C puro, intenté escribir algunas buenas macros para facilitar el estilo OO con C.
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
Aquí está el sitio de mi proyecto (no tengo suficiente tiempo para escribir en. Doc, sin embargo, el documento en chino es mucho mejor).
Hay un ejemplo de herencia usando C en la charla de 1996 de Jim Larson dada en el Seminario de Programación de la Sección 312 a la hora del almuerzo aquí: Nivel C alto y bajo .
¿Qué artículos o libros son buenos para usar los conceptos de OOP en C?
Las interfaces e implementaciones C de Dave Hanson son excelentes en encapsulación y denominación y muy buenas en el uso de punteros de función. Dave no intenta simular la herencia.
Una cosa que quizás desee hacer es analizar la implementación del kit de herramientas Xt para X Window . Claro que se está alargando en el diente, pero muchas de las estructuras utilizadas fueron diseñadas para funcionar de manera OO dentro de la C. tradicional. En general, esto significa agregar una capa adicional de indirección aquí y allá y diseñar estructuras para colocarse una sobre la otra.
Realmente puedes hacer mucho en el camino de OO situado en C de esta manera, aunque a veces se siente así, los conceptos de OO no surgieron completamente de la mente #include<favorite_OO_Guru.h>
. Realmente constituyeron muchas de las mejores prácticas establecidas de la época. Los lenguajes y sistemas OO solo destilaron y amplificaron partes del espíritu de programación del día.
La respuesta a la pregunta es 'Sí, puedes'.
El kit C orientado a objetos (OOC) es para aquellos que desean programar de una manera orientada a objetos, pero también se adhiere a la buena C anterior. OOC implementa clases, herencia simple y múltiple, manejo de excepciones.
Caracteristicas
• Utiliza solo macros y funciones C, ¡no se requieren extensiones de idioma! (ANSI-C)
• Código fuente fácil de leer para su aplicación. Se tuvo cuidado de hacer las cosas lo más simples posible.
• Herencia única de clases.
• Herencia múltiple por interfaces y mixins (desde la versión 1.3)
• Implementación de excepciones (¡en C puro!)
• Funciones virtuales para clases.
• Herramienta externa para una fácil implementación de clase.
Para más detalles, visite http://ooc-coding.sourceforge.net/ .
Parece que la gente está tratando de emular el estilo C ++ usando C. Mi opinión es que al hacer programación orientada a objetos, C realmente está haciendo programación orientada a estructuras. Sin embargo, puede lograr cosas como enlace tardío, encapsulación y herencia. Para la herencia, usted define explícitamente un puntero a las estructuras base en su subestructura y obviamente esta es una forma de herencia múltiple. También deberá determinar si su
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
compilar con c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
Por lo tanto, el consejo es mantener un estilo C puro y no tratar de forzar un estilo C ++. También de esta manera se presta a una forma muy limpia de construir una API.
Consulte http://slkpg.byethost7.com/instance.html para obtener otro giro en la OOP en C. Enfatiza los datos de instancia para la reentrada utilizando solo C. nativa La herencia múltiple se realiza manualmente utilizando envoltorios de funciones. Tipo de seguridad se mantiene. Aquí hay una pequeña muestra:
typedef struct _peeker
{
log_t *log;
symbols_t *sym;
scanner_t scan; // inherited instance
peek_t pk;
int trace;
void (*push) ( SELF *d, symbol_t *symbol );
short (*peek) ( SELF *d, int level );
short (*get) ( SELF *d );
int (*get_line_number) ( SELF *d );
} peeker_t, SlkToken;
#define push(self,a) (*self).push(self, a)
#define peek(self,a) (*self).peek(self, a)
#define get(self) (*self).get(self)
#define get_line_number(self) (*self).get_line_number(self)
INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
return d->scan.line_number;
}
PUBLIC
void
InitializePeeker ( peeker_t *peeker,
int trace,
symbols_t *symbols,
log_t *log,
list_t *list )
{
InitializeScanner ( &peeker->scan, trace, symbols, log, list );
peeker->log = log;
peeker->sym = symbols;
peeker->pk.current = peeker->pk.buffer;
peeker->pk.count = 0;
peeker->trace = trace;
peeker->get_line_number = get_line_number;
peeker->push = push;
peeker->get = get;
peeker->peek = peek;
}
Llego un poco tarde a la fiesta, pero quiero compartir mi experiencia sobre el tema: actualmente trabajo con cosas incrustadas, y el único compilador (confiable) que tengo es C, por lo que quiero aplicar orientado a objetos enfoque en mis proyectos integrados escritos en C.
La mayoría de las soluciones que he visto hasta ahora utilizan en gran medida los tipos de letra, por lo que perdemos la seguridad de tipos: el compilador no lo ayudará si comete un error. Esto es completamente inaceptable.
Requisitos que tengo:
He explicado mi enfoque en detalle en este artículo: programación orientada a objetos en C ; Además, hay una utilidad para la autogeneración de código repetitivo para clases base y derivadas.
Construí una pequeña biblioteca donde probé eso y para mí funciona muy bien. Así que pensé en compartir la experiencia.
https://github.com/thomasfuhringer/oxygen
La herencia única se puede implementar con bastante facilidad utilizando una estructura y extendiéndola para cualquier otra clase secundaria. Una conversión simple a la estructura principal hace posible el uso de métodos principales en todos los descendientes. Siempre que sepa que una variable apunta a una estructura que contiene este tipo de objeto, siempre puede lanzar a la clase raíz y hacer introspección.
Como se ha mencionado, los métodos virtuales son algo más complicados. Pero son factibles. Para simplificar las cosas, solo uso una matriz de funciones en la estructura de descripción de clase que cada clase secundaria copia y repobla espacios individuales donde sea necesario.
La herencia múltiple sería bastante complicada de implementar y conlleva un impacto significativo en el rendimiento. Entonces lo dejo. Considero que es deseable y útil en bastantes casos modelar limpiamente las circunstancias de la vida real, pero en probablemente el 90% de los casos la herencia única cubre las necesidades. Y la herencia única es simple y no cuesta nada.
Además, no me importa la seguridad de los tipos. Creo que no debe depender del compilador para evitar errores de programación. Y de todos modos te protege de una pequeña parte de los errores.
Por lo general, en un entorno orientado a objetos, también desea implementar el recuento de referencias para automatizar la gestión de memoria en la medida de lo posible. Así que también puse un recuento de referencia en la clase raíz "Objeto" y alguna funcionalidad para encapsular la asignación y la desasignación de memoria de almacenamiento dinámico.
Todo es muy simple y delgado y me da lo esencial de OO sin obligarme a lidiar con el monstruo que es C ++. Y conservo la flexibilidad de permanecer en C land, lo que, entre otras cosas, facilita la integración de bibliotecas de terceros.
Propongo usar Objective-C, que es un superconjunto de C.
Si bien Objective-C tiene 30 años, permite escribir código elegante.
Sí, pero nunca he visto a nadie intentar implementar ningún tipo de polimorfismo con C.