Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing & returning generic type (even void) from function

Tags:

c++

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)?

like image 756
sliser Avatar asked Jun 08 '16 07:06

sliser


1 Answers

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

like image 175
Quentin Avatar answered Nov 06 '22 09:11

Quentin