2016-06-08 11 views
6

Sto implementando un sistema RPC progettato per eseguire attività nei processi remoti. Un nodo del sistema RPC è Monitor che dovrebbe registrare ogni chiamata.Memorizzare e restituire un tipo generico (anche vuoto) dalla funzione

template<typename Transport, typename Journal> 
class Monitor 
{ 
public: 
    Monitor(Transport transport, Journal &journal) : 
     transport{std::move(transport)}, 
     journal{journal} 
    { 
    } 

public: 
    template<typename Method> 
    typename Method::Result operator()(const Method &method) 
    { 
     Method::Result result; 
     journal("->", Method::Name()); 
     result = transport(method); 
     journal("<-", Method::Name()); 
     return result; 
    } 

private: 
    Transport transport; 
    Journal &journal; 
}; 

Funziona bene tranne un caso quando Method :: Result è nullo. Per ovviare a questo ho dovuto dividere operatore() in 2 parti

template<typename Transport, typename Journal> 
template<typename Method> 
std::enable_if_t<std::is_same<typename Method::Result, void>::value, typename Method::Result> operator()(const Method &method) 
{ 
    journal("->", Method::Name()); 
    transport(method); 
    journal("<-", Method::Name()); 
} 

template<typename Transport, typename Journal> 
template<typename Method> 
std::enable_if_t<!std::is_same<typename Method::Result, void>::value, typename Method::Result> operator()(const Method &method) 
{ 
    Method::Result result; 
    journal("->", Method::Name()); 
    result = transport(method); 
    journal("<-", Method::Name()); 
    return result; 
} 

Esiste un modo per eliminare copia-incolla, partendo dal presupposto che la linea journal("<-", Method::Name()); non dovrebbe essere eseguito in caso di un'eccezione (quindi non posso wrap logging in construct/destructor)?

risposta

2

È possibile registrare wrap all'interno di un oggetto RAII. Basta controllare se è attualmente in volo un'eccezione prima di stampare nel distruttore, operazione che può essere eseguita con std::uncaught_exception (che diventerà std::uncaught_exceptions in C++ 17).


Se è necessario qualcosa di più flessibile, è possibile utilizzare un wrapper per il valore di ritorno, specializzata per void:

template <class T> 
struct RetWrapper { 
    template <class Tfunc, class... Targs> 
    RetWrapper(Tfunc &&func, Targs &&... args) 
    : val(std::forward<Tfunc>(func)(std::forward<Targs>(args)...)) {} 

    T &&value() { return std::move(val); } 

private: 
    T val; 
}; 

template <> 
struct RetWrapper<void> { 
    template <class Tfunc, class... Targs> 
    RetWrapper(Tfunc &&func, Targs &&... args) { 
     std::forward<Tfunc>(func)(std::forward<Targs>(args)...); 
    } 

    void value() {} 
}; 

RetWrapper esegue la chiamata di funzione e memorizza il risultato, che può essere successivamente trasferito via value(). Ciò concorda con la possibilità di restituire un'espressione void tipo da una funzione void:

template<typename Method> 
typename Method::Result operator()(const Method &method) 
{ 
    journal("->", Method::Name()); 
    RetWrapper<typename Method::Result> retVal{transport, method}; 
    journal("<-", Method::Name()); 
    return retVal.value(); 
} 

Live on Coliru

+0

Grazie per il suggerimento. Non ho mai sentito parlare di std :: uncaught_exception prima. L'unico svantaggio è che il controllo viene eseguito in runtime e devo pagare una penalizzazione delle prestazioni – sliser

Problemi correlati