C - función dentro de la estructura

82

Estoy tratando de crear una función dentro de una estructura, hasta ahora tengo este código:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno AddClient() 

        {
            /* code */
        }

};

int main()
{

    client_t client;

    //code ..

    client.AddClient();

}

Error : client.h: 24: 2: error: esperado ':', ',', ';', '}' o ' atributo ' antes del token '{'.

¿Cuál es la forma correcta de hacerlo?

xRed
fuente
12
No puede tener funciones en estructuras en C; Sin embargo, puede intentar simular eso mediante punteros de función.
Fingolfin
1
¿Son los punteros de función un sustituto aceptable? stackoverflow.com/a/840703/635678
Dan O

Respuestas:

104

No se puede hacer directamente, pero puede emular lo mismo usando punteros de función y pasando explícitamente el parámetro "this":

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno (*AddClient)(client_t *);    
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

Sin embargo, resulta que hacer esto no te compra mucho. Como tal, no verá muchas API de C implementadas en este estilo, ya que también puede llamar a su función externa y pasar la instancia.

Error fatal
fuente
3
La API X11 hace algo parecido a esto. Ver XImage .
Hydranix
1
el código que se encuentra debajo de la API de Windows está lleno de eso. Técnicamente, una "clase de ventana" es una estructura con dos punteros de función de devolución de llamada y con un extremo abierto (para esos "datos específicos de la clase de ventana" que puede proporcionar mientras se registra)
Swift - Friday Pie
1
Seguí esos pasos, pero cuando hago struct.function = newFunction, el compilador imprime: error: esperado '=', ',', ';', 'asm' o ' atributo ' antes de '.' token
Bonfra
Esto también sería útil para cuando quisiera un objetivo común (por ejemplo, disco duro) que tiene una función de lectura y una función de escritura, pero que varía de un tipo de disco duro al nexf
Menotdan
Puede comprarle mucho si se selecciona la implementación real en tiempo de ejecución. Si solo tiene una función estática, siempre debe volver a seleccionar dentro de esa función en cada llamada de función, si usa un puntero de función, la selección ocurre solo una vez. Y la selección puede incluso cambiar a lo largo de la vida útil de la estructura.
Mecki
23

Como han señalado otros, la incrustación de punteros de función directamente dentro de su estructura generalmente se reserva para propósitos especiales, como una función de devolución de llamada.

Lo que probablemente desee es algo más parecido a una tabla de métodos virtuales.

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

Ahora, agregar más operaciones no cambia el tamaño de la client_testructura. Ahora, este tipo de flexibilidad solo es útil si necesita definir muchos tipos de clientes o si desea permitir que los usuarios de su client_tinterfaz puedan aumentar el comportamiento de las operaciones.

Este tipo de estructura aparece en código real. La capa OpenSSL BIO tiene un aspecto similar a esto, y también las interfaces del controlador de dispositivo UNIX tienen una capa como esta.

jxh
fuente
14

Esto solo funcionará en C ++. Las funciones en estructuras no son una característica de C.

Lo mismo ocurre con su cliente. AddClient (); llamada ... esta es una llamada para una función miembro, que es programación orientada a objetos, es decir, C ++.

Convierta su fuente a un archivo .cpp y asegúrese de compilar en consecuencia.

Si necesita ceñirse a C, el siguiente código es (más o menos) el equivalente:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}
QSQ
fuente
1
Este es un proyecto uni y tengo que usar C. ¿Hay alguna forma de que pueda lograr lo que quiero en C?
xRed
Simplemente mueva esa función fuera de la estructura y haga que acepte un puntero a su instancia.
user123
La cosa client.AddClient () no será posible sin un poco de magia compleja (vea la otra respuesta).
QSQ
12

¿Qué tal esto?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 
rogal
fuente
Una "pequeña" solución similar a JavaScrip
The Bitman
7

Está intentando agrupar el código de acuerdo con la estructura. La agrupación C es por archivo. Pones todas las funciones y variables internas en un encabezado o un encabezado y un archivo de objeto ".o" compilado a partir de un archivo fuente de CA.

No es necesario reinventar la orientación a objetos desde cero para un programa en C, que no es un lenguaje orientado a objetos.

He visto esto antes. Es algo extraño. Los codificadores, algunos de ellos, tienen aversión a pasar un objeto que quieren cambiar a una función para cambiarlo, aunque esa es la forma estándar de hacerlo.

Culpo a C ++, porque ocultó el hecho de que el objeto de clase es siempre el primer parámetro en una función miembro, pero está oculto. Así que parece que no está pasando el objeto a la función, aunque sí.

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C es flexible y puede tomar las cosas pasajeras por referencia.

La función AC a menudo devuelve solo un byte de estado o int y eso a menudo se ignora. En su caso, una forma adecuada podría ser

 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { fprintf(stderr, "could not add client (%d) \n", err ); 

addClient estaría en Client.ho Client.c

Chris Reid
fuente
Hay pros y contras en el enfoque. Solo porque es la forma en que siempre se ha hecho , refiriéndose tanto a las estrategias de organización de funciones que no son OO ( do(object, subject)) como a las OO ( subject.do(object)), no significa que debamos dejar de probarlo. Creo totalmente que es válido señalar que esta no es la forma histórica en que se ha escrito C, y que C no necesita escribirse de esta manera (lo mismo ocurre con muchos lenguajes, especialmente los de paradigmas procedimentales, funcionales o lógicos), pero no necesito desalentar activamente el patrón. También viene con algunos beneficios
hiljusti