2015-08-20 6 views
6

Mi piace usare std :: experimental :: opzionale nel mio codice C++, ma il problema è value_or richiede che il valore predefinito sia dello stesso tipo del valore facoltativo.equivalente in C++ del risultato di Rust <T, E> tipo?

Questo non funziona molto bene quando voglio un opzionale che contiene un file int o contiene un messaggio di errore.

Suppongo che potrei usare una struttura unione che ha un valore booleano per indicare se il valore è presente o è un errore, ma sicuramente sarebbe bello se C++ avesse solo un tipo Result<T, E> come Rust.

Esiste un tipo simile? Perché Boost non l'ha implementato?

Il risultato è davvero molto più utile di Option, e sicuramente le persone a Boost sono consapevoli della sua esistenza. Forse andrò a leggere l'implementazione di Rust e poi a copiarlo in C++?

Es:

// Function either returns a file descriptor for a listening socket or fails 
// and returns a nullopt value. 
// My issue: error messages are distributed via perror. 
std::experimental::optional<int> get_tcp_listener(const char *ip_and_port); 
// You can use value_or to handle error, but the error message isn't included! 
// I have to write my own error logger that is contained within 
// get_tcp_listener. I would really appreciate if it returned the error 
// message on failure, rather than an error value. 
int fd = get_tcp_listener("127.0.0.1:9123").value_or(-1); 
// Rust has a type which does what I'm talking about: 
let fd = match get_tcp_listener("127.0.0.1:9123") { 
    Ok(fd) => fd, 
    Err(msg) => { log_error(msg); return; }, 
} 
+2

Probabilmente il proposto 'std :: expected', o un altro tipo' Previsto' che qualcuno ha implementato. – chris

+0

Un'espressione in C++ deve avere un tipo noto in fase di compilazione; non può essere né un 'int' né una stringa. –

+2

In che modo una cosa è più utile di un'altra cosa quando risolvono problemi ortogonali completamente estranei? 'Vector' è più utile di' cout'? – Barry

risposta

15

optional<T> è un tipo sicuro unione asimmetrica di T e nulla (nullopt_t). Puoi chiedere se ha un con explicit operator bool e ottenere lo T con unario *. L'asimmetria significa che "preferisce" facoltativo per essere un T, motivo per cui operazioni non qualificate (come * o bool operatore) si riferiscono al suo T ness.

variant<A,B,C> from paper n4218 è un tipo simmetrico unione sicura di A, B e C (ecc.). boost::variant è sempre occupato e std::experimental::variant è quasi sempre occupato.

Poiché è simmetrico, non esiste un tipo univoco per * unario da restituire e explicit operator bool non può dire molto di interesse, quindi nessuno dei due è supportato.

Invece, è necessario visitarlo o interrogarlo per determinati tipi.

std::experimental::expected<E, T> from paper n4015 è un'unione di tipo sicuro asimmetrico. È uno T o uno E. Ma come optional, "preferisce" essere un T; ha un explicit operator bool che ti dice se è un T, e unario * ottiene il T.

In un certo senso, expected<E,T> è un optional<T>, ma quando vuoto invece di sprecare lo spazio memorizza un E, che è possibile interrogare.

Result<T,E> sembra vicino a expected<E,T> (notare che a partire da n4015, l'ordine dei parametri viene scambiato rispetto a Result).

+0

Wow grazie per le informazioni! Voi ragazzi siete stati davvero d'aiuto! –

+1

Un'ottima spiegazione concisa di opzionale vs variante vs prevista. La proposta per l'attesa sembra avere un contenuto simile al discorso che ho postato e lo richiama in effetti. Passare attraverso entrambi è probabilmente la pena, altrimenti scegli il mezzo che preferisci. –

3

optional di progettazione o contiene un valore di qualche tipo o nulla.

Forse stai cercando qualcosa come Boost::Variant.

Questo non fa ancora parte della libreria standard, anche se qualcosa di simile potrebbe essere alla fine.

+0

Questa è quasi una soluzione perfetta, ma penso che l'attesa sia più adatta al compito. Tuttavia, atteso non è ancora in alcun standard. –

6

Quello che stai cercando è esattamente Alexandrescu's Expected. Raccomando di ascoltare i suoi discorsi per una comprensione approfondita: https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C. Effettua effettivamente l'implementazione riga per riga, e puoi facilmente scriverlo da solo e usarlo bene dopo.

La variante è uno strumento più generale, può essere forzato a fare ciò che si desidera ma si sta meglio con le aspettative.

+0

Grazie, questo è più come quello che stavo cercando! –

+0

"si può facilmente scrivere da soli e usarlo bene dopo che" sì è un discorso molto utile. in particolare ho usato il suo modello in progetti in cui dovremmo evitare di fare eccezioni. puoi creare una struttura standard di "messaggio di errore" che riproduce il ruolo dell'eccezione e provare a creare la tua classe in modo che gli oggetti che sono effettivamente errori possano essere convertiti implicitamente in un tipo diverso, o provare a farlo più come Haskell forse dove in pratica gestisci la cosa con i visitatori. se lo implementate da soli invece di usare la variante, puoi anche farlo gestire correttamente move_ctors –

1

Se non è disponibile solo l'incremento, è possibile utilizzare result. Questo è un bel contenitore a singola intestazione.