I'm implementing a RPC system designed to execute tasks in remote processes. One node of the RPC system is Monitor that should log 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 one case when Method::Result is void. To work around this I had to split 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 any way to eliminate copy-paste, assuming that line journal("<-", Method::Name());
should not be executed in case of an exception (so I can't wrap logging in construct/destructor)?
You can wrap logging inside a RAII object. Just check whether an exception is currently 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 needed, you can use a wrapper for your return value, specializing it for 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
performs the function call and stores the result, which can later be moved out via value()
. This ties in with the possibility of returning a void
-type expression from a 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With