Come gestisci gli errori nell'elaborazione dell'enumerazione / lista (API di livello inferiore)

1

Ive ha lottato con varianti di questo problema molte volte, sperimentando soluzioni diverse, soddisfatto di nessuno.

Estratto: Enumerando un elenco di elementi, un errore per un elemento non influisce sugli altri. I potenziali chiamanti hanno requisiti diversi su come trattare questi errori, l'obiettivo dell'API è semplicemente utilizzare e ridurre la complessità.

Un caso più generale in cui si verifica questo problema sarebbe l'elaborazione di un elenco di elementi indipendenti, e il successo parziale è accettabile / desiderato, cioè è non eseguito come una transazione.

Esempio di scenario :

Ho a che fare con un'API di enumerazione C-Style di basso livello come questa:

 // gets called for each "stuff" item
 typedef bool (*tEnumStuffCallback)(stuff * item, void * callbackArg);

 // calls "callback" for each "stuff" item. 
 // caller enumeration context in callbackArgs
 void EnumStuff(tEnumStuffCallback callback, void * callbackArg)
 {
     stuff * item = 0;
     for(;;)
     {
         int err = stuff_get_next_item(&item);
         if (err == no_more_stuff)
           return;

         // === TODO: handle "real" errors ===

         // pass item  to callback, stop enumeration if requested
         if (!callback(item, callbackArg))
           return;
     }
 }

Per compatibilità con le versioni precedenti e "aspettative del chiamante", desidero / devo mantenere lo stile C puro senza eccezioni. Un wrapper di livello superiore, creato sopra, potrebbe avere il seguente aspetto:

 EnumStuffs(std::vector< boost::shared_ptr<stuff> > & items);

Il chiamante potrebbe aver bisogno di una delle seguenti strategie quando si verificano errori di enumerazione:

  • Interrompi l'enumerazione al primo errore, ad es. perché il chiamante ignorerà l'intera lista e l'enumerazione è costosa. Un client C ++ potrebbe voler lanciare un'eccezione dal callback (permesso).
  • Ignora gli elementi - ad es. quando si enumerano i dispositivi connessi e si cerca uno specifico disponibile e gli errori non rilevati dalla periferica vengono gestiti separatamente
  • Ignora, ma registra la diagnostica in un modo specifico del chiamante (ad esempio, il chiamante deve conoscere gli errori abotu)
  • Il chiamante necessita di un elenco dettagliato di errori per ulteriori analisi, ad es. gli articoli possono essere "riprovati" su determinati tipi di errori

Si noti che questa è la scelta dei chiamanti , non posso forzare una decisione.

Nota / Contesto Esempio idealizzato, in pratica, ho più enumeratori che coinvolgono diversi tipi; a volte devo fornire l'enumeratore, a volte la funzione di enumerazione proviene dal sistema operativo. Le informazioni sugli errori possono essere più complesse di un singolo int . L'API sottostante include già dozzine di tipi piuttosto confusionari, voglio evitare di introdurre la stessa quantità di tipi aggiuntivi / wrapper. Il bridging di diversi stili di sviluppo è in realtà parte di la mia descrizione del lavoro, le opportunità per educare entrambe le parti sono difficili (devo scegliere le mie battaglie).

Le mie idee / soluzioni

  • fornisce un callback separato per gli errori. Abilita tutti gli scenari, ma complica l'interfaccia.

  • Sostituisci la funzione di enumerazione con un oggetto stateful. Aumenta il numero di tipi, non fornisce la compatibilità con lo stile "indietro".

  • passa gli errori all'enumeratore. questo è qualche volta un po 'meglio della soluzione precedente, ma di solito richiede una struttura di "contesto di enumerazione" invece di argomenti separati. Va contro il piano per evitare tipi aggiuntivi per ogni enumeratore

  • introdurre un "tipo di errore", il chiamante può richiedere un "elenco di errori" come risultato, o ottenere il comportamento "stop / lancio al primo errore" per impostazione predefinita. L'ho già usato in passato, ma alla fine le informazioni dettagliate sono generalmente utilizzate solo per creare alcuni strumenti diagnostici e non sono "apprezzate" in quanto rendono "complicato gestire gli errori".

Domande

Quali sono i tuoi pensieri? Hai idee per o hai usato altri approcci? In quali circostanze useresti / non utilizzerai gli approcci precedenti?

posta peterchen 28.08.2011 - 20:35
fonte

1 risposta

1

Versione abbreviata:

Dici che è la scelta del chiamante come gestire gli errori, quindi implementa la tua prima idea. È semplice e un altro argomento non può essere considerato un'interfaccia bloat , infatti, nel tuo caso è persino desiderato in quanto stringe il tuo contratto tra il chiamante e il chiamato! È abbastanza facile da usare e il chiamante gestisce tutti gli errori.

Versione lunga:

provide a separate callback for the errors. Enables all scenarios, but bloats the interface.

Non vedo alcuna interfaccia gonfia quando si passa un secondo puntatore a funzione per la gestione degli errori se è responsabilità dei chiamanti gestirli. Tre argomenti non è molto. Dovresti passare un terzo argomento comunque quando proverai ad applicare un modello di strategia. Vuoi una richiamata per ogni errore? Perché non avere una callback per tutti gli errori? Il callback potrebbe accettare l'errore corrente come argomento e reagire ad esso. Dovrebbe avere un modo per fermare l'enumerazione o ignorare anche l'elemento, che può essere fatto da un tipo di ritorno personalizzato (il migliore è probabilmente un enum semplice: continue_processing / ignore_item / abort_processing). Di solito consiglierei di usare un modello di strategia qui, ma se non riesci a controllare i chiamanti, cioè, non puoi farli scrivere strategie o vuoi mantenere l'interfaccia in puro stile C, quindi usa la funzione semplice pointer callback (fondamentalmente una "buca nel mezzo") TBH, imho è persino desiderato avere l'error-handler nella firma dei metodi, in quanto comunica il contratto molto più chiaro.

Replace the enumerator function with a stateful object. Increases number of types, does not provide backward "style" compatibility.

L'omogeneità è sacra. Cercate sempre di avere un modo chiaro per gestire le cose, il che renderà l'API più intuitiva da comprendere, quindi è necessario meno apprendimento di particolari eccezioni. Fallo solo se non è possibile fornire un'interfaccia "standard". Non penso che dovresti incapsulare ogni enumerazione in un oggetto di stato solo perché hai un "buco nel mezzo" (ne parlerò più tardi). Ciò potrebbe violare i principi ASCIUTTI. Se puoi avere una classe base d'altra parte e avere solo oggetti che gestiscono la gestione personalizzata degli errori, quella sarebbe una progettazione accettabile. Ma non sacrificherei l'omogeneità se ci sono altre soluzioni che funzionano altrettanto bene.

pass the errors to the enumerator. that's sometimes a bit better than the previous solution, but usually requires an "enumeration context" structure instead of separate arguments. Goes against the plan to avoid additional types for each enumerator

Questa è fondamentalmente l'idea 1 solo con un elenco di errori e gestori (il contesto). Imho è solo la versione complicata dell'idea 1. Mantieni la semplicità.

introduce an "error type", caller can either request a "list of errors" as a result, or get the "stop/throw at first error" behavior by default. I've used that before, but in the end the detailed information is typically used only to build some diagnostics, and isn't "liked" as it makes it "complicated to deal with errors".

Quindi ottieni un elenco di errori prima di enumerare a cui puoi quindi iscriverti con un callback? Di nuovo, sembra una versione complicata dell'idea 1. Ancora: Mantieni la semplicità.

Quello che stai incontrando è chiamato "buco nel mezzo". I programmatori funzionali hanno da tempo risolto questo tipo di problemi passando le funzioni (che sono cittadini di prima classe in tali lingue). Un puntatore a funzione (come nell'idea 1) può ottenere il comportamento desiderato quasi altrettanto facilmente. L'interfaccia / firma è solo un puntatore di funzione in più e poiché la responsabilità della gestione degli errori appartiene al chiamante, è persino desiderato averlo nell'interfaccia / firma.

    
risposta data 28.08.2011 - 22:57
fonte

Leggi altre domande sui tag