Próbuję stworzyć funkcję wewnątrz struktury, do tej pory mam taki kod:
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();
}
Błąd : klient.h: 24: 2: błąd: oczekiwano „:”, „,”, „;”, „}” lub „ atrybut ” przed tokenem „{”.
Jaki jest właściwy sposób, aby to zrobić?
Odpowiedzi:
Nie można tego zrobić bezpośrednio, ale można emulować to samo, używając wskaźników do funkcji i jawnie przekazując parametr „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); }
Okazuje się, że robienie tego nie kupuje jednak zbyt wiele. W związku z tym nie zobaczysz wielu interfejsów API C zaimplementowanych w tym stylu, ponieważ równie dobrze możesz po prostu wywołać swoją funkcję zewnętrzną i przekazać instancję.
źródło
Jak zauważyli inni, osadzanie wskaźników funkcji bezpośrednio wewnątrz struktury jest zwykle zarezerwowane do specjalnych celów, takich jak funkcja wywołania zwrotnego.
To, czego prawdopodobnie potrzebujesz, to coś w rodzaju wirtualnej tabeli metod.
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); }
Teraz dodanie większej liczby operacji nie zmienia rozmiaru
client_t
struktury. Ten rodzaj elastyczności jest przydatny tylko wtedy, gdy musisz zdefiniować wiele rodzajów klientów lub chcesz umożliwić użytkownikomclient_t
interfejsu rozszerzenie sposobu, w jaki zachowują się operacje.Taka struktura pojawia się w prawdziwym kodzie. Warstwa OpenSSL BIO wygląda podobnie, a także interfejsy sterowników urządzeń UNIX mają taką warstwę.
źródło
To zadziała tylko w C ++. Funkcje w strukturach nie są cechą C.
To samo dotyczy twojego klienta.AddClient (); call ... to jest wywołanie funkcji składowej, która jest programowaniem obiektowym, tj. C ++.
Przekonwertuj źródło na plik .cpp i upewnij się, że odpowiednio kompilujesz.
Jeśli chcesz trzymać się C, poniższy kod jest (w pewnym sensie) odpowiednikiem:
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); }
źródło
Co powiesz na to?
#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; }
źródło
Próbujesz zgrupować kod według struktury. Grupowanie w C odbywa się według pliku. Umieszczasz wszystkie funkcje i zmienne wewnętrzne w nagłówku lub nagłówku i pliku obiektu „.o” skompilowanym z pliku źródłowego ac.
Nie ma potrzeby odkrywania na nowo orientacji obiektowej od podstaw dla programu C, który nie jest językiem zorientowanym obiektowo.
Widziałem to już wcześniej. To dziwna rzecz. Niektórzy z nich mają niechęć do przekazywania obiektu, który chcą zmienić, w funkcję, która go zmieni, nawet jeśli jest to standardowy sposób.
Obwiniam C ++, ponieważ ukrywało to, że obiekt klasy jest zawsze pierwszym parametrem w funkcji składowej, ale jest ukryty. Wygląda więc na to, że nie przekazuje obiektu do funkcji, mimo że tak jest.
Client.addClient(Client& c); // addClient first parameter is actually // "this", a pointer to the Client object.
C jest elastyczny i może przyjmować przekazywanie rzeczy przez odniesienie.
Funkcja AC często zwraca tylko bajt stanu lub int i często jest to ignorowane. W twoim przypadku może być odpowiednia forma
err = addClient( container_t cnt, client_t c); if ( err != 0 ) { fprintf(stderr, "could not add client (%d) \n", err );
addClient byłby w Client.h lub Client.c
źródło
do(object, subject)
), jak i OO (subject.do(object)
), nie oznacza, że powinniśmy przestać próbować. Całkowicie uważam, że słuszne jest wskazanie, że to nie jest historyczny sposób, w jaki napisano C, i że C nie musi być pisane w ten sposób (to samo dotyczy wielu języków, zwłaszcza paradygmatów proceduralnych, funkcjonalnych lub logicznych) ale nie musimy aktywnie zniechęcać do wzorca. Ma to również pewne zaletyMożesz przekazać wskaźnik struct, aby działał jako argument funkcji. Nazywało się to przekazywaniem przez odniesienie.
Jeśli zmodyfikujesz coś wewnątrz tego wskaźnika, inne zostaną zaktualizowane do. Spróbuj tak:
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 *client) { /* this will change the original client value */ client.password = "secret"; } int main() { client_t client; //code .. AddClient(&client); }
źródło