语法繁重的多个 lambda 包装器的替代方案——如何避免样板代码?
Alternative to syntactically-heavy multiple lambda wrapper - how to avoid boilerplate code?
我发现自己将多个 lambda 封装在一个 "proxy" 对象中,在我的代码的各个部分具有更好的接口:
auto create_proxy()
{
auto f_get_foo = [something]
{
return something_else();
};
auto f_set_bar = [something](auto x)
{
something_else(x);
};
auto f_render = [&window]
{
window.render();
};
return make_nice_proxy( // .
std::move(f_get_foo), // .
std::move(f_set_bar), // .
std::move(f_render));
}
我可以这样使用代理:
nice_proxy.get_foo(); // calls the stored `f_get_foo` lambda
nice_proxy.set_foo(15); // calls the stored `f_set_foo` lambda
nice_proxy.render(); // calls the stored `f_render` lambda
问题是为这些代理编写和维护代码非常麻烦且语法繁重:
template < // .
typename TFGetFoo, // .
typename TFSetBar, // .
typename TFRender // .
>
class nice_proxy_impl
{
// Hide the lambdas:
private:
TFGetFoo _f_get_foo;
TFSetBar _f_set_bar;
TFRender _f_render;
int _render_count = 0;
public:
template < // .
typename TFwdFGetFoo, // .
typename TFwdFSetBar, // .
typename TFwdFRender // .
>
nice_proxy_impl( // .
TFwdFGetFoo&& f_get_foo, // .
TFwdFSetBar&& f_set_bar, // .
TFwdFRender&& f_render) // .
: _f_get_foo(FWD(f_get_foo)),
_f_set_bar(FWD(f_set_bar)),
_f_render(FWD(f_render))
{
}
// Expose the lambdas:
void set_bar(int x)
{
some_side_effect();
_f_set_bar(x);
}
auto get_foo()
{
return _f_get_foo();
}
void render()
{
std::cout << "rendering...\n";
_f_render();
++_render_count;
}
};
template <typename... TFs>
auto make_nice_proxy(TFs&&... fs)
{
return nice_proxy_impl<std::decay_t<TFs>...>(FWD(fs)...);
}
代理 class 的目的是:
- 对用户隐藏 lambda。
- 为用户提供一个漂亮的(可能更丰富的)界面,他们可以通过该界面调用 "hidden" lambda。
我的代码库中有多个代理 class,它们都私下存储了一些完美转发的可调用对象 (并通过 public
函数公开它们),并且是使用 make_xxx_proxy
函数创建的。
虽然 make_xxx_proxy
通常易于实施且不需要太多维护,但每个代理 class (如 nice_proxy_impl
) 都需要一个每个函数的模板参数,每个函数一个字段和一个完美转发构造函数参数。
使用多个代理 classes,即使添加或删除单个 "encapsulated function" 也会很快变得烦人。 get_foo
在nice_proxy_impl
中重复了5次(以不同的形式)。
对于这个 "pattern" 是否有更好的语法更少的解决方案?
我正在寻找一种方法来避免常量 lambda/function 重复、类型衰减和完美转发,这只是样板文件。
make_xxx_proxy
如果传递的参数不仅是函数,而且是附加字段,函数也变得难以维护。那种情况下不能使用参数扩展,每个函数都必须衰减和转发。
这里是a real example of a make_xxx_proxy
-like function. Proxies may contain additional data/methods, that use the "encapsulated lambdas" and additional fields in various ways. Here's the corresponding proxy class。
不确定我是否理解您实际要问的内容,但您可以将 make_nice_proxy
减少为仅 return 本地 class(感谢 C++14)并使用 public 成员(所以你可以聚合初始化)。这避免了重写大部分内容:
template <class Getter, class Setter, class Render>
auto make_nice_proxy(Getter&& g, Setter&& s, Render&& r)
{
struct Proxy {
std::decay_t<Getter> _f_get_foo;
std::decay_t<Setter> _f_set_bar;
std::decay_t<Render> _f_render;
int _render_count = 0;
void set_bar(int x) {
some_side_effect();
_f_set_bar(x);
}
auto get_foo() {
return _f_get_foo();
}
void render() {
std::cout << "rendering...\n";
_f_render();
++_render_count;
}
};
return Proxy{std::forward<Getter>(g), std::forward<Setter>(s), std::forward<Render>(r)};
}
我发现自己将多个 lambda 封装在一个 "proxy" 对象中,在我的代码的各个部分具有更好的接口:
auto create_proxy()
{
auto f_get_foo = [something]
{
return something_else();
};
auto f_set_bar = [something](auto x)
{
something_else(x);
};
auto f_render = [&window]
{
window.render();
};
return make_nice_proxy( // .
std::move(f_get_foo), // .
std::move(f_set_bar), // .
std::move(f_render));
}
我可以这样使用代理:
nice_proxy.get_foo(); // calls the stored `f_get_foo` lambda
nice_proxy.set_foo(15); // calls the stored `f_set_foo` lambda
nice_proxy.render(); // calls the stored `f_render` lambda
问题是为这些代理编写和维护代码非常麻烦且语法繁重:
template < // .
typename TFGetFoo, // .
typename TFSetBar, // .
typename TFRender // .
>
class nice_proxy_impl
{
// Hide the lambdas:
private:
TFGetFoo _f_get_foo;
TFSetBar _f_set_bar;
TFRender _f_render;
int _render_count = 0;
public:
template < // .
typename TFwdFGetFoo, // .
typename TFwdFSetBar, // .
typename TFwdFRender // .
>
nice_proxy_impl( // .
TFwdFGetFoo&& f_get_foo, // .
TFwdFSetBar&& f_set_bar, // .
TFwdFRender&& f_render) // .
: _f_get_foo(FWD(f_get_foo)),
_f_set_bar(FWD(f_set_bar)),
_f_render(FWD(f_render))
{
}
// Expose the lambdas:
void set_bar(int x)
{
some_side_effect();
_f_set_bar(x);
}
auto get_foo()
{
return _f_get_foo();
}
void render()
{
std::cout << "rendering...\n";
_f_render();
++_render_count;
}
};
template <typename... TFs>
auto make_nice_proxy(TFs&&... fs)
{
return nice_proxy_impl<std::decay_t<TFs>...>(FWD(fs)...);
}
代理 class 的目的是:
- 对用户隐藏 lambda。
- 为用户提供一个漂亮的(可能更丰富的)界面,他们可以通过该界面调用 "hidden" lambda。
我的代码库中有多个代理 class,它们都私下存储了一些完美转发的可调用对象 (并通过 public
函数公开它们),并且是使用 make_xxx_proxy
函数创建的。
虽然 make_xxx_proxy
通常易于实施且不需要太多维护,但每个代理 class (如 nice_proxy_impl
) 都需要一个每个函数的模板参数,每个函数一个字段和一个完美转发构造函数参数。
使用多个代理 classes,即使添加或删除单个 "encapsulated function" 也会很快变得烦人。 get_foo
在nice_proxy_impl
中重复了5次(以不同的形式)。
对于这个 "pattern" 是否有更好的语法更少的解决方案?
我正在寻找一种方法来避免常量 lambda/function 重复、类型衰减和完美转发,这只是样板文件。
make_xxx_proxy
如果传递的参数不仅是函数,而且是附加字段,函数也变得难以维护。那种情况下不能使用参数扩展,每个函数都必须衰减和转发。
这里是a real example of a make_xxx_proxy
-like function. Proxies may contain additional data/methods, that use the "encapsulated lambdas" and additional fields in various ways. Here's the corresponding proxy class。
不确定我是否理解您实际要问的内容,但您可以将 make_nice_proxy
减少为仅 return 本地 class(感谢 C++14)并使用 public 成员(所以你可以聚合初始化)。这避免了重写大部分内容:
template <class Getter, class Setter, class Render>
auto make_nice_proxy(Getter&& g, Setter&& s, Render&& r)
{
struct Proxy {
std::decay_t<Getter> _f_get_foo;
std::decay_t<Setter> _f_set_bar;
std::decay_t<Render> _f_render;
int _render_count = 0;
void set_bar(int x) {
some_side_effect();
_f_set_bar(x);
}
auto get_foo() {
return _f_get_foo();
}
void render() {
std::cout << "rendering...\n";
_f_render();
++_render_count;
}
};
return Proxy{std::forward<Getter>(g), std::forward<Setter>(s), std::forward<Render>(r)};
}