in

POO e interfaces en C

apple touch icon@2

OOP e interfaces en C


















Pedido

Visto39k veces

Directamente del bate, entiendo que ANSI C no es un lenguaje de programación orientado a objetos. Quiero aprender a aplicar una técnica oo particular usando c.

Por ejemplo, quiero crear varias clases de efectos de audio que tengan los mismos nombres de función pero diferentes implementaciones de esas funciones.

Si estuviera haciendo esto en un lenguaje de nivel superior, primero escribiría una interfaz y luego la implementaría.

AudioEffectInterface

-(float) processEffect 



DelayClass

-(float) processEffect

{
 // do delay code

  return result

}

FlangerClass

-(float) processEffect

{
 // do flanger code

  return result

}



-(void) main

{
   effect= new DelayEffect()
   effect.process()

   effect = new FlangerEffect()
   effect.process()


}

¿Cómo puedo lograr tal flexibilidad usando C?

1

Hay tres formas distintas de lograr polimorfismo en C:

  1. Codifíquelo
    En las funciones de la clase base, solo switch en un ID de tipo de clase para llamar a las versiones especializadas. Un ejemplo de código incompleto:

    typedef enum classType {
        CLASS_A,
        CLASS_B
    } classType;
    
    typedef struct base {
        classType type;
    } base;
    
    typedef struct A {
        base super;
        ...
    } A;
    
    typedef struct B {
        base super;
        ...
    } B;
    
    void A_construct(A* me) {
        base_construct(&me->super);
        super->type = CLASS_A;
    }
    
    int base_foo(base* me) {
        switch(me->type) {
            case CLASS_A: return A_foo(me);
            case CLASS_B: return B_foo(me);
            default: assert(0), abort();
        }
    }
    

    Por supuesto, esto es tedioso de hacer para clases numerosas.

  2. Almacenar punteros de función en el objeto
    Puede evitar las declaraciones de cambio utilizando un puntero de función para cada función miembro. Nuevamente, este es un código incompleto:

    typedef struct base {
        int (*foo)(base* me);
    } base;
    
    //class definitions for A and B as above
    
    int A_foo(base* me);
    
    void A_construct(A* me) {
        base_construct(&me->super);
        me->super.foo = A_foo;
    }
    

    Ahora, el código de llamada puede funcionar

    base* anObject = ...;
    (*anObject->foo)(anObject);
    

    Alternativamente, puede usar una macro de preprocesador en las líneas de:

    #define base_foo(me) (*me->foo)(me)
    

    Tenga en cuenta que esto evalúa la expresión me dos veces, así que esta es realmente una mala idea. Esto puede solucionarse, pero está más allá del alcance de esta respuesta.

  3. Utilice una vtable
    Dado que todos los objetos de una clase comparten el mismo conjunto de funciones miembro, todos pueden usar los mismos punteros de función. Esto está muy cerca de lo que hace C ++ bajo el capó:

    typedef struct base_vtable {
        int (*foo)(base* me);
        ...
    } base_vtable;
    
    typedef struct base {
        base_vtable* vtable;
        ...
    } base;
    
    typedef struct A_vtable {
        base_vtable super;
        ...
    } A_vtable;
    
    
    
    //within A.c
    
    int A_foo(base* super);
    static A_vtable gVtable = {
        .foo = A_foo,
        ...
    };
    
    void A_construct(A* me) {
        base_construct(&me->super);
        me->super.vtable = &gVtable;
    };
    

    Nuevamente, esto permite que el código de usuario realice el envío (con una indirección adicional):

    base* anObject = ...;
    (*anObject->vtable->foo)(anObject);
    

El método que debe utilizar depende de la tarea en cuestión. los switch El enfoque basado es fácil de preparar para dos o tres clases pequeñas, pero es difícil de manejar para grandes clases y jerarquías. El segundo enfoque escala mucho mejor, pero tiene una gran sobrecarga de espacio debido a los punteros de función duplicados. El enfoque vtable requiere un poco de estructura adicional e introduce aún más indirección (lo que hace que el código sea más difícil de leer), pero sin duda es el camino a seguir para las jerarquías de clases complejas.

5

¿Puede comprometerse con lo siguiente:

#include <stdio.h>

struct effect_ops {
    float (*processEffect)(void *effect);
    /* + other operations.. */
};

struct DelayClass {
    unsigned delay;
    struct effect_ops *ops;
};

struct FlangerClass {
    unsigned period;
    struct effect_ops *ops;
};

/* The actual effect functions are here
 * Pointers to the actual structure may be needed for effect-specific parameterization, etc.
 */
float flangerEffect(void *flanger)
{
   struct FlangerClass *this = flanger;
   /* mix signal delayed by this->period with original */
   return 0.0f;
}

float delayEffect(void *delay)
{
    struct DelayClass *this = delay;
    /* delay signal by this->delay */
    return 0.0f;
}

/* Instantiate and assign a "default" operation, if you want to */
static struct effect_ops flanger_operations = {
    .processEffect = flangerEffect,
};

static struct effect_ops delay_operations = {
    .processEffect = delayEffect,
};

int main()
{
    struct DelayClass delay     = {.delay = 10, .ops = &delay_operations};
    struct FlangerClass flanger = {.period = 1, .ops = &flanger_operations};
    /* ...then for your signal */
    flanger.ops->processEffect(&flanger);
    delay.ops->processEffect(&delay);
    return 0;
}

2

Implementas interfaces usando estructuras de punteros de función. Luego, puede tener la estructura de la interfaz incrustada en la estructura de su objeto de datos y pasar el puntero de la interfaz como primer parámetro de cada función miembro de la interfaz. En esa función, luego obtiene el puntero a su clase de contenedor (que es específica de su implementación) usando la macro container_of (). Busque «container_of linux kernel» para una implementación. Es una macro muy útil.

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas

lang-c

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

desktop icon

Compilador Java8 en línea – Editor Java8 en línea – IDE Java8 en línea – Codificación Java8 en línea – Practique Java8 en línea – Ejecute Java8 en línea – Compile Java8 en línea

gfg 200x200 min

Puntero doble (puntero a puntero) en C

Back to Top
close