Sto usando la libreria delle espressioni regolari di boost e sto scoprendo che determinare se una corrispondenza con nome è stata trovata e quindi usare quell'informazione è un po 'fastidiosa. Per rilevare una corrispondenza con nome, vorrei fare questo:Come posso creare una cascata pulita se la struttura in C++?
typedef boost::match_result<string::const_iterator> matches_t;
typedef matches_t::const_reference match_t;
boost::regex re("(?:(?<type1>aaaa)|(?<type2>bbbb)" /*...*/ "|(?<typeN>abcdefg)");
string str(SOME_STRING);
matches_t what;
boost::match_flag_type flags = boost::match_default;
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
if((match_t type1 = what["type1"]).matched)
{
// do stuff with type1
}
else if((match_t type2 = what["type2"]).matched)
{
// do stuff with type2
}
// ...
else if((match_t typeN = what["typeN"]).matched)
{
// do stuff with typeN
}
}
Se ciò funzionasse, sarebbe fantastico. Scoping sarebbe limitato al corpo di if, la memoria può essere utilizzata in modo efficiente e sembra abbastanza pulita. Purtroppo, non funziona in quanto non è possibile definire una variabile all'interno di un elenco. :(
Questo avrebbe potuto essere una possibilità:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
match_t found = what["type1"];
if(found.matched)
{
// do stuff with type1
}
else if((found = what["type2"]).matched)
{
// do stuff with type2
}
// ...
else if((found = what["typeN"]).matched)
{
// do stuff with typeN
}
}
Ma match_t è un riferimento const quindi non è assegnabile (tl; dr Anche io non so che cosa il tipo di fondo è e generalmente io. non voglio davvero sapere come preferirei una soluzione più generica che potrei usare al di fuori di questo esempio di regex, anche std :: move() è stato usato attorno a ciò che [...] diventa ancora più dettagliato e la documentazione non dice che usa semantica mossa per sub_match.Tutto questo è ovvio, naturalmente, a causa del motivo indicato nella prima frase di questo paragrafo)
Un'altra opzione è quella di fare è questo:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
match_t type1 = what["type1"];
if(type1.matched)
{
// do stuff with type1
}
else {
match_t type2 = what["type2"];
if(type2.matched)
{
// do stuff with type2
}
// ...
else {
match_t typeN = what["typeN"];
if((match_t typeN = what["typeN"]).matched)
{
// do stuff with typeN
}
}
// ...
}
}
}
Il che non mi piace a causa di profonda nidificazione di parentesi graffe.
Forse aver abusato di una struttura ad anello con break
s alla fine di ogni if
corpo come questo:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
do{
{
match_t type1 = what["type1"];
if(type1.matched)
{
// do stuff with type1
break;
}
}
{
match_t type2 = what["type2"];
if(type2.matched)
{
// do stuff with type2
break;
}
}
// ...
{
match_t typeN = what["typeN"];
if(typeN.matched)
{
// do stuff with typeN
break;
}
}
} while(0);
}
Che è meglio, ma ancora non ottimo. Utilizzando le macro, gran parte del rumore potrebbe essere nascosto alla vista. Come:
#define IF(declare, cond) do{{declare;if(cond){
#define ELSE_IF(declare, cond) break;}}{declare; if(cond){
#define ELSE break;}}{{
#define END_IF break;}}}while(0);
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
IF(match_t type1 = what["type1"], type1.matched)
{
// do stuff with type1
}
ELSE_IF(match_t type2 = what["type2"], type2.matched)
{
// do stuff with type2
}
// ...
ELSE_IF(match_t typeN = what["typeN"], typeN.matched)
{
// do stuff with typeN
}
END_IF
}
Le parentesi sono in realtà implicita nel macro, ma è una lettura più chiara da loro ribadendo.
Un'altra opzione a cui posso pensare è entrare nella classe boost :: sub_match e aggiungere una funzione di conversione per convertire quel tipo in un valore bool che restituirà il valore del membro matched
. Quindi potrei dichiarare una variabile match_t nella espressione if e verrebbe automaticamente convertita in un valore booleano dal if. Non sono sicuro se sono ancora lì e non è generico.
Stilisticamente, sono quelli che propongo buoni o cattivi (solo gli ultimi 3 funzionano davvero, quindi probabilmente limiterei i commenti a loro).
Inoltre, qualcuno ha suggerimenti migliori? Per favore, spiega perché pensi che siano migliori.
Si noti che 'if (foo = bar)' non è lo stesso di 'if (foo == bar)' in C++, il codice probabilmente non fa ciò che si desidera. Inoltre raccomando fortemente di usare le macro per "rimuovere la posta indesiderata" come nel tuo ultimo esempio. Non rimuove la "spazzatura" ma rende il codice incomprensibile. – Damon
@Damon: Sì, lo so che = non è uguale a ==. Il codice fa esattamente ciò che voglio, per limitare l'ambito, ridurre il rumore e riutilizzare lo spazio dello stack (se il compilatore lo ottimizza). Sono un po 'd'accordo con te: i macro, ma riduce il rumore in eccesso a patto che tu sappia a cosa servono le macro, il che può rendere le cose più chiare. Ma YMMV. Quindi suppongo che tu preferisca il secondo con 'do {} while (0);' sans i macro? – Adrian
Non sono sicuro, probabilmente scriverei la primissima versione (dichiarando le variabili nell'output 'if' esterno, quindi compila), dal momento che è la versione meno offuscata, secondo me. Ma alla fine della giornata, si tratta di gusti personali. E metterei doppie parentesi attorno ai compiti all'interno di "if" per rendere esplicito che non sono accidenziali (con gli avvisi corretti abilitati, questo è anche ciò che, ad esempio, GCC suggerisce di fare). – Damon