sogou / workflow

C++ Parallel Computing and Asynchronous Networking Framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WFServer类构造时std::function传参问题

mallocnew opened this issue · comments

Uploading 截屏2024-03-31 13.57.07.png…
WFServer类构造的时候std::function<void (WFNetworkTask<REQ, RESP> *)> proc是以拷贝的方式传参进来的,process(std::move(proc))进行了一次移动来转移所有权,转移了proc的所有权。
这样依旧发生了一次拷贝构造(用户传参进行WFServer构造的时候)。

可以使用完美转发来传参?这样的话就减少了一次拷贝构造

	WFServer(const struct WFServerParams *params,
			 std::function<void (WFNetworkTask<REQ, RESP> *)>&& proc) :
		WFServerBase(params),
		process{std::forward<std::function<void (WFNetworkTask<REQ, RESP> *)>>(proc)}
	{
	}

感谢建议。有几个问题。

  1. 为了减少用户理解和使用复杂性,我们用户接口上不使用右值。
  2. 目前的接口,也支持用户传入右值。和你这个写法性能上几乎没区别,甚至编译器优化之后可能完全相同。
  3. Server只构造一次,性能无所谓。传给Server task时是引用传递。
  4. 我们尽可能减少C++高级特征的使用。虽然forward也没多高级。

std::forward是个高级特性,以至于很容易被人错误地理解,例如您的示例代码便是对其常见的误用。

通常来说,完美转发应结合引用折叠来使用,若要使用该特性,WFServer的构造应改为

template<typename Proc>
WFServer(const struct WFServerParams *params, Proc&& proc)
    : WFServerBase(params),
      process{std::forward<Proc>(proc)}
{
}

这在构造函数上引入了模板参数,此处忽略更深入的讨论。

从使用者的角度看,通常有两种创建WFServer实例的方式,

// 1. proc较为复杂,先构建proc
auto proc = [&some_global_var](WFXXXTask *) { /* do someting with very long code */ };
// or
auto proc = std::bind(real_proc, /* some very long args that you don't want to write in servers construct */, _1, /* some args */);
// or
const auto &proc = create_processor(/*very very very complicated args used to create a processor instance */);
WFXXXServer server(params, proc);

// 2. proc较为简单,直接用来构建server

void process(WFXXXTask *);
void process_with_ctx(Context *, WFXXXTask *);
struct Processor {
    void operator(WFXXXTask *);
};

WFXXXServer server(params, process);
WFXXXServer server(params, [ctx](WFXXXTask *task) mutable { process_with_ctx(ctx, task); });
WFXXXServer server(params, Processor{});

从设计者角度看,需要同时可以处理参数是T &, const T &,以及T &&的情况(另可能有const T &&可规约到const T &),此时该构造函数需要下述两种格式

WFServer(const struct WFServerParams *params,
		std::function<void (WFNetworkTask<REQ, RESP> *)>&& proc) :
    WFServerBase(params),
    process{std::move(proc)}
{
}

以及

WFServer(const struct WFServerParams *params,
	const std::function<void (WFNetworkTask<REQ, RESP> *)>& proc) :
    WFServerBase(params),
    process{proc}
{
}

其中,const std::function<...> &可用来处理std::function<...> &const std::function<...> &两种情况。

也就是说,要想完美地解决问题,可以选择使用1. 模板函数或者2. 重载多个函数,而如果需要一个不那么完美的、可以少写代码的方法,则使用目前WFServer中使用的,以std::function<...>为参数的风格。