语法繁重的多重lambda包装器的替代方案-如何避免样板代码

Alternative to syntactically-heavy multiple lambda wrapper - how to avoid boilerplate code?

本文关键字:何避免 代码 方案 lambda 包装 语法      更新时间:2023-10-16

我发现自己在代码的各个部分用一个更好的接口将多个Lambda封装在一个"代理"对象中:

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

代理类的目的是:

  • 对用户隐藏Lambda
  • 给用户一个好的(可能更丰富的)界面,通过它他们可以调用"隐藏的"lambdas

我的代码库中有多个代理类,它们都私下存储一些完全转发的可调用对象(并通过public函数公开它们),并且是使用make_xxx_proxy函数创建的。

虽然make_xxx_proxy通常易于实现且不需要太多维护,但每个代理类(如nice_proxy_impl都需要每个函数一个模板参数、每个函数一一个字段和一个完美的转发构造函数参数。

对于多个代理类,即使添加或删除单个"封装函数"也会很快变得令人讨厌。CCD_ 5在CCD_ 6中被重复5次(以不同的形式)。

对于这种"模式",有没有更好、语法不那么重的解决方案

我正在寻找一种方法来避免恒定的lambda/函数重复、类型衰减和完美转发,这只是样板。

如果传递的参数不仅是函数,而且是附加字段,那么make_xxx_proxy函数也将成为维护的地狱。在这种情况下不能使用参数扩展,并且每个函数都必须衰减和转发。

下面是一个类似make_xxx_proxy的函数的真实例子。代理可能包含额外的数据/方法,这些数据/方法以各种方式使用"封装的lambda"和额外的字段。这是相应的代理类。

不确定我是否理解您的实际要求,但您可以将make_nice_proxy简化为只返回一个具有公共成员的本地类(感谢C++14)(这样您就可以聚合initialize)。这避免了必须重写大多数内容:

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