C ++ 11:绑定和存储具有不同签名的处理程序仿函数

时间:2014-09-05 19:29:17

标签: c++ templates c++11 functor

我有一个worker类,它通过队列从外部接受作业。 作业以异步方式处理,完成后,应调用处理函数。 此处理函数的签名取决于作业类型。问题是我不知道我需要将处理程序存储在队列中的结果值,所以我不能在那里绑定处理函数和结果值。

所以我使用了一个boost :: variant来枚举所有可能的结果类型,这样我就可以将所有的functor存储在同一个队列中。 我不喜欢我必须手动枚举结果类型,我觉得我的代码可以简化。

有没有办法避免" boost :: variant - > static_visitor"方法

#include <functional>
#include <queue>
#include <boost/variant.hpp>
#include <iostream>

// jobs

struct DoSomeWork {};
struct AnotherJob {};

// job results

struct SomeResult { int value; };
struct AnotherResult { float fValue; };

struct worker
{
    typedef boost::variant<SomeResult, AnotherResult> JobResultVariant;    

    struct NoOp
    {
    template <typename... Ts>
    void operator()(Ts&&...) const{}
    };
    template <typename JobType, typename JobFinishedHandler = NoOp>
    void enqueue_job(const JobType& job, const JobFinishedHandler& handler = NoOp())
    {

    // enqueue job
    // ...

    // store handler
    jobFinishedHandlerQueue.push(
            std::bind(&worker::call_finished_handler<JobFinishedHandler>, this, handler, std::placeholders::_1)
            );
    }

    template <typename JobFinishedHandler>
    void call_finished_handler(const JobFinishedHandler& handler, const JobResultVariant& result)
    {
    boost::apply_visitor(dispatch_job_result<JobFinishedHandler>(handler), result);
    }

    template <typename JobFinishedHandler>
    struct dispatch_job_result : public boost::static_visitor<>
    {
    dispatch_job_result(const JobFinishedHandler& handler) : handler(handler) {}


    // check if the event handler accepts this argument
    template <typename EventType,
          class = decltype(std::declval<JobFinishedHandler>()(std::declval<EventType>()))>

    void operator()(const EventType& event) const
    {
        handler(event);
    }

    // fallback if the above check fails
    template <typename... Ts>
    void operator()(Ts&&...) const
    {
        // do nothing
    }

    const JobFinishedHandler& handler;
    };

    void run()
    {
    // do the work
    // ...
    // the actual work is omitted here
    // fake the generation of a result to be able to compile this example

    SomeResult result;
    result.value = 100;

    // result is available, now call result handler

    auto finishedHandlerWrapper = jobFinishedHandlerQueue.front();
    jobFinishedHandlerQueue.pop();
    finishedHandlerWrapper(result);
    }

    typedef std::function<void(const JobResultVariant&)> JobFinishedHandlerWrapper;
    typedef std::queue<JobFinishedHandlerWrapper> JobFinishedHandlerQueue;
    JobFinishedHandlerQueue jobFinishedHandlerQueue;
};

int main()
{
    worker w;

    auto handler = [](const SomeResult& result){ std::cout << result.value << std::endl; };
    w.enqueue_job(DoSomeWork(), handler);
    w.run();
}

1 个答案:

答案 0 :(得分:0)

我认为您需要的是一个重载(多重签名)功能,您可以找到here。 由于你使用的是Boost,为简洁起见,我将在这里使用Boost.TypeErasure:

#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/callable.hpp>

template<class... Sig>
using overload =
    boost::type_erasure::any<
        boost::mpl::vector<
            boost::type_erasure::copy_constructible<>,
            boost::type_erasure::typeid_<>,
            boost::type_erasure::relaxed,
            boost::type_erasure::callable<Sig>...
        >
    >;
在您的示例中

,替换

std::function<void(const JobResultVariant&)>

overload<void(SomeResult const&), void(AnotherResult const&)>

现在你不必使用variant,但它也意味着处理程序必须处理你列出的所有类型,但是,你可以写一个回退什么都不做的包装:

struct whatever
{
    template<class T>
    whatever(T const&) {}
};

void do_nothing(whatever) {}

template<class F>
struct handler_wrapper : F
{
    using fallback = void(*)(whatever);

    handler_wrapper(F const& f) : F(f) {}

    operator fallback() const
    {
        return do_nothing;
    }
};

enqueue_job内,您可以

jobFinishedHandlerQueue.push(handler_wrapper<JobFinishedHandler>(handler));

run中,只需使用finishedHandlerWrapperSomeResult致电AnotherResult