Storing and returning a generic type (even invalid) from a function

I am implementing an RPC system designed to perform tasks in remote processes. One of the nodes of the RPC system is the β€œMonitor”, which should record every call.

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; }; 

It works fine, except in one case where the :: Method result is not valid. To get around this, I had to break the operator () into 2 parts

 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; } 

Is there a way to eliminate copy-paste, assuming the string is journal("<-", Method::Name()); shouldn't be executed in case of an exception (so I can't wrap the record in a construct / destructor)?

+6
source share
1 answer

You can migrate logging inside an RAII object. Just check if the exception is actually in flight before printing in the destructor, which can be done with std::uncaught_exception (which will become std::uncaught_exceptions in C ++ 17).


If something more flexible is required, you can use a wrapper for the return value, specializing in 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 makes a function call and saves the result, which can later be deleted via value() . This is due to the possibility of returning an expression of type void from the void function:

 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

+2
source

All Articles