语法繁重的多个 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 的目的是:

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

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

使用多个代理 classes,即使添加或删除单个 "encapsulated function" 也会很快变得烦人。 get_foonice_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)};
}