Problemi di progettazione dell'API in C [chiuso]

9

Quali sono alcuni difetti che ti fanno impazzire nelle API C (incluse librerie standard, librerie di terze parti e intestazioni all'interno di un progetto)? L'obiettivo è identificare i problemi di progettazione dell'API in C, così le persone che scrivono nuove librerie C possono imparare dagli errori del passato.

Spiega perché il difetto è cattivo (preferibilmente con un esempio) e cerca di suggerire un miglioramento. Anche se la tua soluzione potrebbe non essere pratica nella vita reale (è troppo tardi per correggere strncpy ), dovrebbe dare un vantaggio ai futuri scrittori di librerie.

Sebbene il focus di questa domanda siano le API C, i problemi che riguardano la tua capacità di usarli in altre lingue sono i benvenuti.

Per favore, dai una pecca per risposta, in modo che la democrazia possa ordinare le risposte.

    
posta Joey Adams 13.08.2011 - 06:09
fonte

5 risposte

5

Funziona con valori di ritorno inconsistenti o illogici. Due buoni esempi:

1) Alcune funzioni di Windows che restituiscono un HANDLE usano NULL / 0 per un errore (CreateThread), alcuni usano INVALID_HANDLE_VALUE / -1 per un errore (CreateFile).

2) La funzione 'tempo' POSIX restituisce '(time_t) -1' in caso di errore, il che è davvero illogico poiché 'time_t' può essere un tipo con segno o senza segno.

    
risposta data 13.08.2011 - 08:35
fonte
4

Funzioni o parametri con nomi non descrittivi o che confondono affermativamente. Ad esempio:

1) CreateFile, nell'API di Windows, in realtà non crea un file, ma crea un handle di file. Può creare un file, proprio come "open", se richiesto attraverso un parametro. Questo parametro ha valori chiamati "CREATE_ALWAYS" e "CREATE_NEW", i cui nomi non lasciano nemmeno intendere la loro semantica. (Fa 'CREATE_ALWAYS' significa che fallisce se il file esiste? Oppure crea un nuovo file su di esso? 'CREATE_NEW' significa che crea sempre un nuovo file e fallisce se il file esiste già? Oppure crea un nuovo file file su di esso?)

2) pthread_cond_wait nell'API pthreads POSIX, che nonostante il suo nome, è un incondizionato wait.

    
risposta data 13.08.2011 - 08:38
fonte
3

Tipi opachi che vengono passati attraverso l'interfaccia come maniglie cancellate dal tipo. Il problema è, naturalmente, che il compilatore non può controllare il codice utente per i tipi di argomenti corretti.

Questo è disponibile in varie forme e gusti, inclusi, ma non limitati a:

  • void* abuse

  • utilizzando int come handle di risorse (esempio: la libreria CDI)

  • argomenti

I tipi più distinti (= non possono essere utilizzati completamente in modo intercambiabile) sono mappati allo stesso tipo di tipo cancellato, il peggio. Naturalmente, il rimedio è semplicemente quello di fornire dei puntatori opachi tipicamente lungo le linee di (esempio C):

typedef struct Foo Foo;
typedef struct Bar Bar;

Foo* createFoo();
Bar* createBar();

int doSomething(Foo* foo);
void somethingElse(Foo* foo, Bar* bar);

void destroyFoo(Foo* foo);
void destroyBar(Bar* bar);
    
risposta data 03.10.2015 - 10:59
fonte
2

Funziona con convenzioni di restituzione di stringhe inconsistenti e spesso ingombranti.

Ad esempio, getcwd richiede un buffer fornito dall'utente e le sue dimensioni. Ciò significa che un'applicazione deve impostare un limite arbitrario sulla lunghezza della directory corrente o fare qualcosa di simile a questo (da CCAN ):

 /* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
    if (errno != ERANGE) {
        talloc_free(cwd);
        return NULL;
    }
    cwd = talloc_realloc(ctx, cwd, char, len *= 2);
}

La mia soluzione: restituisce una stringa malloc ed. È semplice, robusto e non meno efficiente. Ad eccezione delle piattaforme embedded e dei sistemi meno recenti, malloc è in realtà abbastanza veloce.

    
risposta data 13.08.2011 - 08:01
fonte
1

Funzioni che prendono / restituiscono tipi di dati composti per valore o che usano le callback.

Peggio ancora se detto tipo è un sindacato o contiene campi di bit.

Dal punto di vista di un chiamante C, questi sono in realtà OK, ma non scrivo in C o C ++ a meno che non sia richiesto, quindi di solito sto chiamando tramite un FFI. La maggior parte degli FFI non supporta i sindacati oi campi di bit e alcuni (come Haskell e MLton) non supportano le strutture passate per valore. Per coloro che sono in grado di gestire le strutture di valore, almeno Common Lisp e LuaJIT sono forzati su percorsi lenti - L'interfaccia Common Foreign Function di Lisp deve effettuare una chiamata lenta tramite libffi e LuaJIT rifiuta di compilare JIT il percorso del codice che contiene la chiamata. Le funzioni che possono richiamare gli host attivano anche percorsi lenti su LuaJIT, Java e Haskell, con LuaJIT che non è in grado di compilare tale chiamata.

    
risposta data 03.10.2015 - 09:15
fonte

Leggi altre domande sui tag