我有一个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();
}
答案 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
中,只需使用finishedHandlerWrapper
或SomeResult
致电AnotherResult
。