【问题标题】:How to write a generic non-member wrapper around arbitrary member functions?如何围绕任意成员函数编写通用非成员包装器?
【发布时间】:2020-05-24 21:15:02
【问题描述】:

问题

拥有一个为任意成员函数生成包装器的模板,允许在对象上调用该函数,例如:

给定bool std::string::empty(),要生成的包装器将是bool wrapper(const std::string& str) { return str.empty(); }

包装函数不能是 lambda(以便它可以作为函数指针传递)。

我的方法(对于非常量成员函数)

/* Wrapper (for non-const member functions) */

template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
    using fType = R (T::*)(ARGS...);
    template<fType FUNCTION>
    static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
};

template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
    //auto function = [memberFunction] (T& object, ARGS... args) -> R { return (object.*memberFunction)(args...); };
    using Helper = staticWrapperHelper<T, R, ARGS...>;

    R (*wrapper)(T&, ARGS...) = &Helper::template wrapper<R (T::*)(ARGS...)>;
    return wrapper;
}

/* Example */

class A
{
public:
    void member() {}
};

int main()
{
    A a;
    auto f = staticWrapper<A>(&A::member);
    f(a);

    return 0;
}

我的方法有问题

它不会编译。看起来返回的包装器仍然是一个模板:http://coliru.stacked-crooked.com/a/d4c0dd9ab631aa1f

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out main.cpp: 在 'auto staticWrapper(R (T::*)(ARGS ...)) [with T = A; R = 无效; ARGS = {}]': main.cpp:32:41:从这里需要 main.cpp:17:9: 错误: 没有匹配转换函数'wrapper'到类型'void (*)(class A&)' 17 | R (*wrapper)(T&, ARGS...) = &Helper::template wrapper; | ^~~~~~~ main.cpp:8:14:注意:候选是:'template static R staticWrapperHelper::wrapper(T&, ARGS ...) [with R (T::* FUNCTION)(ARGS ...) = FUNCTION; T = A; R = 无效; ARGS = {}]' 8 |静态 R 包装器(T& 对象,ARGS... args){ 返回对象。*FUNCTION(args...); } | ^~~~~~~

有什么想法吗?

【问题讨论】:

    标签: c++ c++14 metaprogramming function-pointers pointer-to-member


    【解决方案1】:

    关于using fType = R (T::*)(ARGS...);,类型fType是指针类型,所以不能作为模板参数在运行时记忆/保存实例方法地址以备后用。所以,你应该在staticWrapperHelper中定义一个成员来保存方法指针以供以后使用:

    template<typename T, typename R, typename... ARGS>
    struct staticWrapperHelper
    {
        using fType = R (T::*)(ARGS...);
        fType method;
        R operator() (T& object, ARGS... args) { return (object.*method)(args...); }
    };
    

    我还将wrapper 静态方法更改为实例方法:operator() 成为仿函数对象。

    现在,在用作staticWrapperHelper 的工厂函数的staticWrapper 函数中,我们返回一个存储方法指针的staticWrapperHelper 实例。稍后可以使用此实例对象在提供的 T 类型的对象上调用实例方法。

    template<typename T, typename R, typename... ARGS>
    auto staticWrapper(R (T::*memberFunction)(ARGS...))
    {
        using Helper = staticWrapperHelper<T, R, ARGS...>;
        return Helper{.method = memberFunction};
    }
    

    现场演示:http://coliru.stacked-crooked.com/a/9a7b7c04b36d9dc0

    【讨论】:

    • 我搞错了,打算取Helper::template wrapper&lt;memberFunction&gt;的地址而不是Helper::template wrapper&lt;R (T::*)(ARGS...)&gt;;是的,因为memberFunction 是一个函数参数,所以我不能在这里将它用作staticWrapperHelper 的模板参数,尽管一般来说,函数指针可以是模板参数。
    • 现在到你的解决方案(谢谢!):我对必须先在这里有一个实例持怀疑态度,但似乎整个事情都可以是 constexpr 所以这对我来说没有问题。然而,虽然返回的函子可能是可调用的,但它不能用作函数指针。
    • @haku 正如您所说,模板参数必须在编译时已知,因此通过函数参数传递的函数指针不能用于模板参数。我想到的一件事是让 staticWrapper 函数成为 constexpr 函数,但我不确定这是否是一个解决方案。
    • 我找到了一种将函数指针作为模板参数和(稍后)作为函数参数的方法——后者将签名分解为类、返回类型和参数。看我的回答:stackoverflow.com/a/60225302/10614843
    • @haku 好。看起来和我上面提出的想法一样;使工厂方法成为constexpr 函数。
    【解决方案2】:

    我找到了一个我可以接受的解决方案,方法是将成员函数指针作为模板参数传递,不幸的是,这需要在 C++14 中明确提及类型(辅助宏在这里可以提供帮助)。然后函数指针的签名被一个辅助函数分解以创建包装器。

    #include <iostream>
    
    /* Wrapper (for non-const member functions) */
    
    template<typename PTM, PTM ptm>
    struct staticWrapper
    {
        constexpr static auto make()
        {
            return breakDownAndWrap(ptm);
        }
    
    private:
        template<typename C, typename R, typename... ARGS>
        constexpr static auto breakDownAndWrap(R (C::*)(ARGS...))
        {
            R (*wrapper)(C&, ARGS...) = &Wrapper<C, R, ARGS...>::get;
            return wrapper;
        }
    
        template<typename C, typename R, typename... ARGS>
        struct Wrapper
        {
            constexpr static R get(C& obj, ARGS... args)
            {
                return (obj.*ptm)(args...);
            }
        };
    };
    
    /* Optional helper macro */
    
    #define AUTO_T(val) decltype(val), val
    
    /* Example */
    
    class A
    {
    public:
        int number;
    
        void member() { std::cout << number << std::endl; }
    };
    
    int main()
    {
        A a {2};
        auto f = staticWrapper<decltype(&A::member), &A::member>::make();
        auto g = staticWrapper<AUTO_T(&A::member)>::make();
        f(a);
        g(a);
    
        return 0;
    }
    

    工作示例:http://coliru.stacked-crooked.com/a/4c5cb1d98437c789

    【讨论】:

    • 在 C++17 中你可以拥有template &lt;auto ptm&gt;
    猜你喜欢
    • 2018-06-29
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    • 2014-07-29
    • 1970-01-01
    • 2017-03-10
    • 2020-01-23
    • 1970-01-01
    相关资源
    最近更新 更多