2011-01-19 11 views
9

Descargo de responsabilidad: Soy un novato completo con C, pero he estado jugando con él tratando de imitar algunas características de las clases. Ok, Sé que si quiero ir por ese camino debería aprender C++, pero considere lo siguiente un pequeño experimento.Spicing C con las clases

Schreiner, en el libro Programación orientada a objetos con la norma ANSI-C sugiere una manera de utilizar punteros para obtener características orientación a objetos en C. Debo admitir que sólo he hojeado el libro, pero no me gusta su enfoque demasiado. Básicamente, se utiliza punteros a funciones con el fin de disponer que

func(foo); 

da lugar realmente a llamar

foo.methods->func(); 

donde foo.methods es una estructura que contiene punteros a funciones. Lo que no me gusta en este enfoque es que uno tiene que tener la función global foo de todos modos; es decir, los métodos no son espaciados por la clase en la que viven. Mi sensación es que esto pronto llevará al desorden: piense en dos objetos foo y bar, ambos con un método func pero con un número diferente de parámetros.

Así que he intentado conseguir algo más adecuado para mi gusto. Un primer intento es el siguiente (Omito las declaraciones de razones de brevedad)

#include <stdio.h> 

//Instances of this struct will be my objects 
struct foo { 
    //Properties 
    int bar; 

    //Methods 
    void (* print)(struct foo self); 
    void (* printSum)(struct foo self, int delta); 
}; 

//Here is the actual implementation of the methods 
static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

static void printSumFoo(struct foo self, int delta) { 
    printf("This is bar plus delta: %d\n", self.bar + delta); 
} 

//This is a sort of constructor 
struct foo Foo(int bar) { 
    struct foo foo = { 
     .bar = bar, 
     .print = &printFoo, 
     .printSum = &printSumFoo 
    }; 
    return foo; 
} 

//Finally, this is how one calls the methods 
void 
main(void) { 
    struct foo foo = Foo(14); 
    foo.print(foo); // This is bar: 14 
    foo.printSum(foo, 2); // This is bar plus delta: 16 
} 

Ésta es unconvenient pero algo funciona. Lo que no me gusta, sin embargo, es que tienes que agregar explícitamente el objeto en sí mismo como primer argumento. Con un poco de trabajo de preprocesador puedo hacerlo un poco mejor:

#include <stdio.h> 
#define __(stuff)  stuff.method(* stuff.object) 

//Instances of this struct will be my objects 
struct foo { 
    //Properties 
    int bar; 

    //Methods 
    //Note: these are now struct themselves 
    //and they contain a pointer the object... 
    struct { 
     void (* method)(struct foo self); 
     struct foo * object; 
    } print; 
}; 

//Here is the actual implementation of the methods 
static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

//This is a sort of constructor 
struct foo Foo(int bar) { 
    struct foo foo = { 
     .bar = bar, 
     //...hence initialization is a little bit different 
     .print = { 
      .method = &printFoo, 
      .object = &foo 
     } 
    }; 
    return foo; 
} 

//Finally, this is how one calls the methods 
void 
main(void) { 
    struct foo foo = Foo(14); 
    //This is long and unconvenient... 
    foo.print.method(* foo.print.object); // This is bar: 14 
    //...but it can be shortened by the preprocessor 
    __(foo.print); // This is bar: 14 
} 

Esto es lo más lejos que puedo conseguir. El problema aquí es que no funcionará para métodos con argumentos, ya que las macros de preprocesador no pueden tomar una cantidad variable de argumentos. Por supuesto, uno puede definir las macros _0, _1 y así sucesivamente de acuerdo con la cantidad de argumentos (hasta que uno se cansa), pero este no es un buen enfoque.

¿Hay alguna manera de mejorar esto y dejar que C use una sintaxis más orientada a objetos?

Debo añadir que en realidad Schreiner hace mucho más de lo que dije en su libro, pero creo que la construcción básica no cambia.

+0

me gustaría utilizar un enfoque vtable para las funciones, que es similar a su segunda aproximación, excepto 'print' sería un puntero. – leppie

+0

"ambos tienen un método func" ... func es el nombre de un campo de puntero a función en una estructura: no hay razón para que la función global a la que apunta deba llamarse simplemente "func". Prefijo/postfijo con algo específico de clase y resuelto ese problema. –

+2

Las macros de preprocesador pueden tomar una cantidad variable de argumentos, desde hace 12 años. –

Respuesta

2

Ya existen varios marcos. Véase, por ejemplo http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

+0

Gracias, esto se ve muy interesante. Echaré un vistazo a la fuente para ver cómo funciona esto – Andrea

+0

500 - Error interno del servidor Ha ocurrido un error al cargar la página solicitada: http: //ldeniau.web .cern.ch/ldeniau/html/oopc.html - Hay un problema con el recurso que está buscando y no se puede mostrar. – lpapp

+0

@LaszloPapp, Laurent tenía documentos (puede encontrar uno o dos al buscar su nombre). yo) y varios marcos vinculados desde su página de inicio. Parece que uno de sus frameworks está disponible en http://sourceforge.net/projects/cos/ (también hay un proyecto githup que parece inactivo). – AProgrammer

1

Un libro (en formato PDF) que explica cómo hacerlo, es object oriented programming in ANSI C Es viejo (1993), pero todavía contiene algunas ideas y consejos válidos, en mi humilde opinión.

+0

De hecho, este es el libro al que me refiero, pero no me gusta demasiado su enfoque. Ver la pregunta. – Andrea

+0

OK, ¡al principio no vi la referencia! –

1

¿Echa un vistazo a Go de Google? Básicamente es una C modernizada donde las cosas se hacen de la manera que sugeriste. Tiene polimorfismos de parámetros. Por lo que no tiene que hacer esto:

static void printFoo(struct foo self) { 
    printf("This is bar: %d\n", self.bar); 
} 

En Go se puede hacer de esta manera:

static void print(Foo self) { 
    printf("This is foo: %d\n", self.foo); 
} 

static void print(Bar self) { 
    printf("This is bar: %d\n", self.bar); 
} 

En Ir Foo y Bar también sería estructuras.Así que básicamente estás en la misma pista que los diseñadores de idiomas Go.

Para una visión general de Go ver http://golang.org/doc/effective_go.html Este es el principal lenguaje de descripción de Go: http://golang.org/doc/effective_go.html

Cuestiones relacionadas