C'è più di un modo per skinare questo particolare gatto, ma in generale la soluzione a questo è nascondere l'implementazione dell'interfaccia pubblica dietro ad un riferimento indiretto che consente di iniettare "implementazioni" separate.
(Questa incarnazione del problema è anche strettamente legato al problema un po 'diverso di garantire la compatibilità ABI tra le versioni di codice.)
per risolvere questo in C è possibile farlo simile al Pimpl con-ereditarietà in C++ (protetto invece di d-pointer privato, con costruttori protetti sottoposti a override):
Si crea un oggetto opaco 'lettore'/'stream' (puntatore a forward struct dichiarato/typedef in C) e funzioni di costruzione opportunamente denominate da istanziare l'oggetto opaco che inietta l'implementazione desiderata.
Analizziamo i file di intestazione di esempio per darti un'idea di come le funzioni combacino. Cominciamo con il coraggio, la definizione degli oggetti/p-impl d-pointer (NB: Sto omettendo alcune boilerplate come guardie di intestazione):
lettore-private.h:
/* probably should be in its proper C file, but here for clarification */
struct FileReaderPrivateData {
FILE * fp;
};
/* probably should be in its proper C file, but here for clarification */
struct StringReaderPrivateData {
size_t nlines;
size_t cursor;
char ** lines;
};
/* in C we don't have inheritance, but we can 'fix' it using callbacks */
struct ReaderPrivate {
int (* close)(void* pData); /* impl callback */
ssize_t (* readLine)(void* pData, char** into); /* impl callback */
/* impl-specific data object, callbacks can type cast safely */
void * data;
};
/* works like a plain p-impl/d-pointer, delegates to the callbacks */
struct Reader {
struct ReaderPrivate * dPtr;
}
reader.h:
typedef struct Reader* Reader;
/* N.B.: buf would be a pointer to set to a newly allocated line buffer. */
ssize_t readLine(Reader r, char ** buf);
int close(Reader r);
file reader.h
#include "reader.h"
Reader createFileReader(FILE * fp);
Reader createFileReader(const char* path);
stringa-reader.h
#include "reader.h"
Reader createStringReader(const char**, size_t nlines);
Questo è un modello generale per fare Pimpl/d-pointer con l'ereditarietà in C, in modo da poter astratto il coraggio di implementazione dietro un'interfaccia pubblica a cui si accede attraverso puntatori opachi. Questo meccanismo è generalmente utile per garantire la compatibilità API e ABI tra varie implementazioni dell'interfaccia pubblica e per implementare un modello di ereditarietà semplice.
Non c'è niente di integrato. Il tuo metodo mi sembra buono. Se stavi usando C++ potresti usare classi o funzioni sovraccariche, ma C non ha nulla di simile. – Barmar
È possibile sovraccaricare creando funzioni con argomenti variadici. * Spallucce *. –
puoi semplicemente scrivere una funzione che legge un file in un elenco di caratteri *? come read_from_chars (get_lines (file)); – user3125280