我有一个工作分配计划,每个单位都会进行一些簿记和管理,并将任务传递给下一个订单链,例如;让我们说有3个班:老板,经理,工人
class Boss
{
void do_async(Request req, std::function<void(Result)> callback)
{
//Find eligible manager etc.
manager.do_async(Boss_request(req,...),std::bind(&Boss::callback,this,callback,std::placeholders::_1));
}
void callback(std::function<void(Result)> main_callback,Boss_result res)
{
//some book keeping
main_callback(res.main_part);
}
};
class Manager
{
void do_async(Boss_request req, std::function<void(Boss_result)> boss_callback)
{
//Find eligible worker etc. add some data to request
worker.do_async(Manager_request(req,...),std::bind(&Manager::callback,this,boss_callback,std::placeholders::_1));
}
void callback(std::function<void(Boss_result)> boss_callback,Manager_result res)
{
//some book keeping
boss_callback(res.boss_part);
}
};
class Worker
{
void do_async(Manager_request req, std::function<void(Manager_result)> manager_callback)
{
//Do job async
work_async(Worker_task(req,...),std::bind(&Worker::callback,this,manager_callback,std::placeholders::_1));
}
void callback(std::function<void(Manager_result)> manager_callback,Worker_result res)
{
//some book keeping
manager_callback(res.manager_part);
}
};
正如您所看到的,我正在广泛使用std :: bind,std :: function和std :: placeholder。这种方法有什么优点/缺点吗?如果不是更可取,那么更好的方法是什么?在这种状态下使用lambda
函数是否可能/更可取(作为性能或代码质量)?
编辑:为什么我需要在每个级别进行异步访问,而不仅仅是第一级?因为每个类之间存在多对多的关系。我有几层处理单元(Boss或Manager或Worker),可以订购下一层中的任何人。当一个单位将作业命令到下一行。必须立即免费从上面接受新订单。
我没有直接使用lambda
,因为回调可能有点大,可能会让它更难阅读。但是,如果存在任何显着的性能损失,则可以牺牲代码质量。
答案 0 :(得分:3)
你在这里做的是管道数据。拥抱管道。
namespace chain {
template<class T, class Base=std::function<void(T)>>
struct sink:Base{
using Base::operator();
using Base::Base;
};
template<class T, class F>
sink<T> make_sink( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
using source=sink<sink<T>>;
template<class T, class F>
source<T> make_source( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
source<std::decay_t<T>> simple_source( T&& t ) {
return [t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
};
}
template<class In, class Out>
using pipe = std::function< void(source<In>, sink<Out>) >;
template<class In, class Out>
sink<In> operator|( pipe<In, Out> p, sink<Out> s ) {
return [p,s]( In in ) {
p( [&]( auto&& sink ){ sink(std::forward<In>(in)); }, s );
};
}
template<class In, class Out>
source<Out> operator|( source<In> s, pipe<Out> p ) {
return [s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
};
}
template<class T>
std::function<void()> operator|( source<T> in, sink<T> out ) {
return [in, out]{ in(out); };
}
template<class In, class Mid, class Out>
pipe<In, Out> operator|( pipe<In, Mid> a, pipe<Mid, Out> b ) {
return [a,b]( source<In> src, sink<Out> dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
};
}
}//namespace
然后写下这些:
pipe<Request, Result> Boss::work_pipe();
pipe<Boss_request, Boss_result> Manager::work_pipe();
pipe<Boss_request, Manager_request> Manager::process_request();
pipe<Manager_request, Manager_result> Manager::do_request();
pipe<Manager_result, Boss_results> Manager::format_result();
pipe<Manager_request, Manager_result> Worker::work_pipe();
和工人和老板类似。
pipe<Request, Result> Boss::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Boss_request, Boss_result> Manager::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Manager_request, Manager_result> Worker::work_pipe() {
return process_request() | do_request() | format_result();
}
然后:
pipe<Manager_request, Manager_result> Manager::do_request() {
return [this]( source<Manager_request> src, sink<Manager_result> dest ) {
// find worker
worker.do_request( src, dest );
};
}
pipe<Manager_output, Boss_result> Manager::format_result() {
return [this]( source<Manager_output> src, sink<Boss_result> dest ) {
src([&]( Manager_output from_worker ) {
// some book keeping
dest( from_worker.boss_part );
});
};
}
现在,我创建了源和#34;接收器用于接收器&#34;,因为它允许源(或管道)从一次调用生成1,0或许多消息。我觉得这在很多情况下很有用,但它确实使编写管道有点奇怪。
您也可以在c++14中完整地编写此内容,而不必使用std::function
,只需应用&#34;我就是一个接收器&#34;和&#34;我是一个来源&#34;和&#34;我是一个管道&#34;标签为lambdas(通过组合,如override
),然后用|
盲目地挂钩,并希望它们的类型兼容。
要do_sync
,您只需执行此操作:
void Boss::do_async( Request req, sink<Result> r ) {
work_async( simple_source(req) | work_pipe() | r );
}
即,整个计算可以捆绑并移动。这会将线程工作移到顶部。
如果您需要将异步线程实现放在底部,则可以将早期工作整理好并将其传递下去。
void Boss::do_async( source<Request> req, sink<Result> r ) {
find_manager().do_async( req|process_request(), format_result()|r );
}
void Manager::do_async( source<Boss_request> req, sink<Boss_result> r ) {
find_worker().do_async( req|process_request(), format_result()|r );
}
void Worker::do_async( source<Manager_request> req, sink<Manager_result> r ) {
work_async( req|process_request()|do_request()|format_result()|r );
}
由于水槽/水源/水管的组成方式,您可以选择传递的构图的哪些部分以及传递的部分。
std::function
- 更少版本:
namespace chain {
struct pipe_tag{};
struct sink_tag{};
struct source_tag{};
template<class T, class=void>
struct is_source:std::is_base_of<source_tag, T>{};
template<class T, class=void>
struct is_sink:std::is_base_of<sink_tag, T>{};
template<class T, class=void>
struct is_pipe:std::is_base_of<pipe_tag, T>{};
template<class F, class Tag>
struct tagged_func_t: F, Tag {
using F::operator();
using F::F;
tagged_func_t(F&& f):F(std::move(f)) {}
};
template<class R, class...Args, class Tag>
struct tagged_func_t<R(*)(Args...), Tag>: Tag {
using fptr = R(*)(Args...);
fptr f;
R operator()(Args...args)const{
return f( std::forward<Args>(args)... );
}
tagged_func_t(fptr fin):f(fin) {}
};
template<class Tag, class F>
tagged_func_t< std::decay_t<F>, Tag >
tag_func( F&& f ) { return {std::forward<F>(f)}; }
template<class F>
auto as_pipe( F&& f ) { return tag_func<pipe_tag>(std::forward<F>(f)); }
template<class F>
auto as_sink( F&& f ) { return tag_func<sink_tag>(std::forward<F>(f)); }
template<class F>
auto as_source( F&& f ) { return tag_func<source_tag>(std::forward<F>(f)); }
template<class T>
auto simple_source( T&& t ) {
return as_source([t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
});
}
template<class Pipe, class Sink,
std::enable_if_t< is_pipe<Pipe>{} && is_sink<Sink>{}, bool> = true
>
auto operator|( Pipe p, Sink s ) {
return as_sink([p,s]( auto&& in ) {
p( [&]( auto&& sink ){ sink(decltype(in)(in)); }, s );
});
}
template<class Source, class Pipe,
std::enable_if_t< is_pipe<Pipe>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source s, Pipe p ) {
return as_source([s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
});
}
template<class Source, class Sink,
std::enable_if_t< is_sink<Sink>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source in, Sink out ) {
return [in, out]{ in(out); };
}
template<class PipeA, class PipeB,
std::enable_if_t< is_pipe<PipeA>{} && is_pipe<PipeB>{}, bool> = true
>
auto operator|( PipeA a, PipeB b ) {
return as_pipe([a,b]( auto&& src, auto&& dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
});
}
template<class T>
using sink_t = tagged_func_t< std::function<void(T)>, sink_tag >;
template<class T>
using source_t = tagged_func_t< std::function<void(sink_t<T>)>, source_tag >;
template<class In, class Out>
using pipe_t = tagged_func_t< std::function<void(source_t<In>, sink_t<Out>)>, pipe_tag >;
}
执行较少的类型检查,但摆脱了类型擦除开销。
当您需要对其进行类型删除时,sink_t
,source_t
和pipe_t
typedef非常有用。
"Hello world"
example使用非类型擦除版本。