有没有办法改进这个 Delegate<void(int&)>::Bind<TestStruct&, &TestStruct::somefunction>(testStruct, value) 语法?

Is there a way to improve this Delegate<void(int&)>::Bind<TestStruct&, &TestStruct::somefunction>(testStruct, value) syntax?

我正在尝试实现一个委托类型,它可以使用通用回调并在存在重载集的情况下工作。

为了简化这里是 class 的摘录,绑定方法实现为静态函数以简化问题。

template <typename Signature>
class Delegate;

template <typename Ret, typename... Args>
struct Delegate<Ret(Args...)> {
    using Trampoline_T = Ret (*)(void*, Args&&...);
    using TrampolineConst_T = Ret (*)(const void*, Args&&...);

    using Function_Sig = Ret(Args...);
    using Function_Ptr = std::add_pointer_t<Function_Sig>;

    template <typename Instance_T>
    using MemberFunction_Sig = Ret(std::remove_reference_t<Instance_T>&,
                                   Args...);
    template <typename Instance_T>
    using MemberFunction_Ptr =
        Ret (std::remove_reference_t<Instance_T>::*)(Args...);
    template <typename Instance_T>
    using MemberFunctionConst_Ptr =
        Ret (std::remove_reference_t<Instance_T>::*)(Args...) const;
    template <typename Instance_T>
    using MemberFunctionConstOrNot_Ptr =
        std::conditional_t<std::is_const_v<std::remove_reference_t<Instance_T>>,
                           MemberFunctionConst_Ptr<Instance_T>,
                           MemberFunction_Ptr<Instance_T>>;

    template <typename Instance_T, MemberFunctionConstOrNot_Ptr<Instance_T> fct, typename T>
    static void Bind(T&& instance, auto&& value) {

        if constexpr (!std::is_const_v<std::remove_reference_t<Instance_T>>) {
            Trampoline_T trampoline = [](void* storage, Args&&... args) -> Ret {
                std::invoke(
                    fct,
                    *reinterpret_cast<std::remove_reference_t<Instance_T>*>(
                        storage),
                    std::forward<decltype(args)>(args)...);
            };
            trampoline(&instance, value);
        } else {
            TrampolineConst_T trampoline = [](const void* storage,
                                              Args&&... args) -> Ret {
                std::invoke(
                    fct,
                    *reinterpret_cast<std::remove_reference_t<Instance_T>*>(
                        storage),
                    std::forward<decltype(args)>(args)...);
            };
            trampoline(&instance, value);
        }
    }

    template <typename Instance_T>
    static void Bind(Instance_T&& instance, auto&& value) {
        if constexpr (!std::is_const_v<std::remove_reference_t<Instance_T>>) {
            Trampoline_T trampoline = [](void* storage, Args&&... args) -> Ret {
                std::invoke(
                    *reinterpret_cast<std::remove_reference_t<Instance_T>*>(
                        storage),
                    std::forward<decltype(args)>(args)...);
            };
            trampoline(&instance, value);
        } else {
            TrampolineConst_T trampoline = [](const void* storage,
                                              Args&&... args) -> Ret {
                std::invoke(
                    *reinterpret_cast<std::remove_reference_t<Instance_T>*>(
                        storage),
                    std::forward<decltype(args)>(args)...);
            };
            trampoline(&instance, value);
        }
    }
};

现在将这种类型用于对象测试

class TestStruct {
   public:
    constexpr static int m_staticValue = 42;
    uint8_t m_value = m_staticValue;

    void somefunctionGeneric(auto& value) {
        std::cout << "somefunctionGeneric " << value << "\n";
    }
    void somefunctionGeneric(auto& value) const {
        std::cout << "somefunctionGeneric const " << value + 1 << "\n";
    }
    void somefunction(int& value) {
        std::cout << "somefunction " << value << "\n";
    }
    void somefunction(int& value) const {
        std::cout << "somefunction const " << value + 1 << "\n";
    }

    void operator()(auto& value) {
        std::cout << "operator()(auto&) " << value << "\n";
    }
    void operator()(auto& value) const {
        std::cout << "operator()(auto&) const " << value + 1 << "\n";
    }
    void operator()(int& value) {
        std::cout << "operator()(int&) " << value << "\n";
    }
    void operator()(int& value) const {
        std::cout << "operator()(int&) const " << value + 1 << "\n";
    }
};

你会这样使用它:

int value = 42;
double valueDouble = 42;
TestStruct testStruct;
Delegate<void(int&)>::Bind<TestStruct&, &TestStruct::somefunction>(testStruct, value);
Delegate<void(double&)>::Bind<TestStruct&,&TestStruct::somefunctionGeneric<double>>(testStruct, valueDouble);

Delegate<void(int&)>::Bind(testStruct, value);
Delegate<void(double&)>::Bind(testStruct, valueDouble);

const TestStruct testStructConst;
Delegate<void(int&)>::Bind<const TestStruct&, &TestStruct::somefunction>(testStructConst, value);
Delegate<void(double&)>::Bind<const TestStruct&, &TestStruct::somefunctionGeneric<double>>(testStructConst, valueDouble);

Delegate<void(int&)>::Bind(testStructConst, value);
Delegate<void(double&)>::Bind(testStructConst, valueDouble);

Compiler explorer code

我想改进 Bind 调用语法。我必须传递 class 和函数作为模板。我觉得有一些技巧可以改善它。就像使用 class 模板来推断类型一样。这可能是不可能的,如果知道它不能用 c++20 改进,我会很乐意接受这个答案。

要求: 该函数必须是要在蹦床 lambda 中捕获的模板参数。蹦床 lambda 必须可转换为函数指针。 它必须与重载集函数一起使用,例如:void somefunction(int& value)void somefunction(int& value) const 该函数应该可以是模板 const 或不是。 应该支持 Lambda。 请不要建议 std::function,这是我所拥有的,当函数可内联时,性能提高了 10%。

它目前有效,但我很确定我看到了改进它的技术Delegate<void(int&)>::Bind<TestStruct&, &TestStruct::somefunction>(testStruct, value);我现在无法弄清楚它们。

非常感谢

注意:是的,值是在绑定函数中传递的,这只是为了说明,因为绑定函数是静态的。在我的代码中有一个绑定和一个调用函数。

之所以要改进这一点,是因为实例类型必须由用户提供,这确实很容易出错。他必须弄清楚他的实例是 const 还是 not,是 ref 还是右值 ref。当他将实例传递给绑定函数时,实际上所有这些信息都已经存在。

我是如何改进它的,方法是使用 public 内部 class。事情可以改进,名字可以更好,但这里是原则和使用方法。

template <typename Signature>
class Delegate;

template <typename Ret, typename... Args>
struct Delegate<Ret(Args...)> {
    /* previous code from the question */

    // Don't mind the Trampoline type they are for the question in my real code this is a type ereased type.
    template <typename Instance_T, MemberFunctionConstOrNot_Ptr<Instance_T> fct>
    inline static Trampoline_T trampolineLambda = [](void* storage, Args&&... args) -> void {
        std::invoke(fct, *reinterpret_cast<std::remove_reference_t<Instance_T>*>(storage), std::forward<Args>(args)...);
    };

    template <typename Instance_T, MemberFunctionConstOrNot_Ptr<Instance_T> fct>
    inline static TrampolineConst_T trampolineLambdaConst = [](const void* storage, Args&&... args) -> void {
        std::invoke(fct, *reinterpret_cast<std::remove_reference_t<Instance_T>*>(storage), std::forward<Args>(args)...);
    };

    template <typename Instance_T>
    struct MemFnBinder
    {
        Delegate<Ret(Args...)>* m_delegate;

        Instance_T m_intance;

        template <MemberFunctionConstOrNot_Ptr<Instance_T> fct>
        void Bind()
        {
            using Type = std::remove_reference_t<Instance_T>;

            // Don't mind the fact that I am dealing with the constness like this. 
            // It is a by product of simplifying the code for the question. 
            constexpr bool IsConst = std::is_const_v<Type>;
            if constexpr (not IsConst) {
                m_delegate->m_trampoline = trampolineLambda<Instance_T, fct>;
            } else {
                m_delegate->m_trampolineConst = trampolineLambdaConst<Instance_T, fct>;
            }
        }
    };

    template <typename Instance_T>
    decltype(auto) GetMemFnBinder(Instance_T&& instance)
    {
        return MemFnBinder<Instance_T>{this, instance};
    }
};

它是如何使用的,使用与问题相同的 TestStruct。也忽略了我正在创建 Delegate<void(int&)>{} 临时对象,只是我无法使函数 GetMemFnBinder 静态化,因为我是如何实现 trampolines 的。对于设计模式来说是无关紧要的。

TestStruct testStruct;
const TestStruct testStructConst;
Delegate<void(int&)> delegate{};
delegate.GetMemFnBinder(testStruct).Bind<&TestStruct::somefunction>(); 
delegate.GetMemFnBinder(testStructConst).Bind<&TestStruct::somefunction>();
delegate.GetMemFnBinder(std::move(testStructConst)).Bind<&TestStruct::somefunction>();
delegate.GetMemFnBinder(TestStruct{}).Bind<&TestStruct::somefunction>();

我相信这比

Delegate<void(int&)>::Bind<TestStruct&, &TestStruct::somefunction>(testStruct);
Delegate<void(int&)>::Bind<const TestStruct&, &TestStruct::somefunction>(testStructConst);
Delegate<void(int&)>::Bind<TestStruct&&, &TestStruct::somefunction>(TestStruct{});

我同意可以改进我选择的名称。当我的大脑休息时,我会找到更好的名字。