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?