有没有办法改进这个 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);
我想改进 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{});
我同意可以改进我选择的名称。当我的大脑休息时,我会找到更好的名字。
我正在尝试实现一个委托类型,它可以使用通用回调并在存在重载集的情况下工作。
为了简化这里是 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);
我想改进 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{});
我同意可以改进我选择的名称。当我的大脑休息时,我会找到更好的名字。