gpt4 book ai didi

c++ - 许多嵌套回调的优点/缺点?

转载 作者:行者123 更新时间:2023-11-30 02:22:41 29 4
gpt4 key购买 nike

我有一个工作分配方案,其中每个单元都进行一些簿记和管理,并将任务传递给订单链中的下一个单元,例如;假设有 3 个类:老板、经理、 worker

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 函数是否可能/更可取(作为性能或代码质量)?

编辑:为什么我需要在每个级别进行异步访问,而不仅仅是第一级?因为每个类之间存在多对多关系。我有几层处理单元(老板或经理或 worker )可以订购下一层的任何人。当一个单位将工作交给下一个单位时。它必须立即免费接受来自上面的新订单。

我没有直接使用 lambda,因为回调可能有点大,可能会使其更难阅读。但是,如果存在任何显着的性能损失,则可能会牺牲代码质量。

最佳答案

您在这里所做的是传输数据。拥抱管道。

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();

对于 Worker 和 Boss 也是类似的。

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 );
});
};
}

现在,我将源设为“汇汇汇”,因为它允许源(或管道)从一次调用中生成 1、0 或许多消息。我发现这在很多情况下都很有用,但它确实让编写管道变得有点陌生。

也可以写在中根本不使用 std::function,只需将“我是一个接收器”、“我是一个源”和“我是一个管道”标签应用于 lambda(通过组合,如 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-less 版本:

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_tsource_tpipe_t 类型定义在您需要对它们进行类型删除时很有用。

"Hello world" example使用非类型删除版本。

关于c++ - 许多嵌套回调的优点/缺点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47011725/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com