来自 API 的回调成员函数接受一个没有参数的自由函数
Callback member function from API taking a free function with no arguments
我使用一个 API 声明是:
some_API.h
typedef void (CALLBACK *EventCallback)();
class some_API{
public:
// ...
void EnableInterrupt(EventCallback func1, EventCallback func2);
// ...
};
在另一边我有一个 class 使用这个 API:
My_class.h
class My_class
{
some_API API;
void CALLBACK my_func() { cout << "This is my_func!\n" }
public:
void using_api()
{
API.EnableInterrupt(my_func, nullptr);
}
};
主要问题是 my_func 的类型,错误:
Error (active) E0167 argument of type "void (__stdcall My_class::*)()" is incompatible with parameter of type "EventCallback"
我找到了 this 答案,但问题是 API 是闭源的,所以我无法更改 void EnableInterrupt(EventCallback, EventCallback)
.
的声明
我也不想将 My_class::using_API
和 My_class::API
声明为静态成员。
我想要类似这样的东西:
API.EnableInterrupt((static_cast<void*>(my_func)), nullptr);
// or
API.EnableInterrupt((static_cast<EventCallback>(my_func)), nullptr); // invalid type conversion
有什么方法可以将该成员函数转换为非成员函数以将其传递给 some_API::EnableInterrupt(...)
?
当您需要 link 回调函数与您的 class 实例时,C 和 C++ 之间的回调工作得很好。为了使其工作,您需要从 C 端传递信息(通常是在回调注册时提供的 void* )。最简单的方法是使用静态方法和 this
指针:
typedef void (*EventCallback)(void*)();
void EnableInterrupt(EventCallback func1, void *userdata);
static void CallbackWrapper(void* data) {
static_cast<My_class*>(data)->my_func();
}
EnableInterrupt(CallbackWrapper, this)
现在,当你的回调是这样的:
typedef void (CALLBACK *EventCallback)();
这可能是因为设计不当,也可能是 API 设计者不希望您注册多个回调处理程序。这可能是一个重要的问题,考虑到该函数会干扰中断,如果他们不希望您注册多个处理程序,我会理解。
因此,您只需要创建一个 class 实例,因此单例模式可能是一个不错的选择。为此,您必须执行以下操作:
void CALLBACK global_callback_handler() {
My_class::getInstance()->my_func()
}
void using_api()
{
API.EnableInterrupt(global_callback_handler);
}
你的问题是回调类型
typedef void (CALLBACK *EventCallback)();
没有任何用户提供的数据指针。这是习惯做法,正是因为用户经常需要它。
为了完整起见,
Is there any way to cast that member function to a non-member function
不,它们根本不同,因为必须使用隐式 this
指针调用非静态成员函数,并且没有地方可以将其存储在常规函数指针中。这正是我们使用用户提供的指针参数 for 如果 API 有一个。
Also I don't want to declare My_class::using_API and My_class::API as static member.
如果我们为每个不同的注册生成另一个 static
怎么样?我们可以通过使用 lambda 类型唯一性来自动化它:
template <typename Lambda>
class TrampolineWrapper
{
inline static std::optional<Lambda> dest_; // optional so we can defer initialization
public:
TrampolineWrapper(some_API *api, Lambda &&lambda)
{
// store your _stateful_ functor into a unique static
dest_.emplace( std::move(lambda) );
// register a unique _stateless_ lambda (convertible to free function)
api->EnableInterrupt(
[](){
(*TrampolineWrapper<Lambda>::dest_)();
},
nullptr);
}
};
// handy type-deducing function
template <typename Lambda>
TrampolineWrapper<Lambda> wrap(some_API *api, Lambda &&lambda)
{
return {api, std::move(lambda)};
}
并像
一样使用它
class My_class
{
some_API API;
void my_func() { cout << "This is my_func!\n" }
public:
void using_api()
{
auto wrapper = wrap(&API, [this](){ my_func(); });
// stateful lambda ^^^^
// not directly convertible to a free function
}
};
这取决于要工作的 lambda 类型的唯一性 - 您使用它的每个 lambda 都会得到一个相同类型的唯一静态 dest_
对象(可能有状态)和一个独特的无状态转发 lambda(对于这个可怕的 API 可以转换为免费函数)。
请注意,只有当您有多个不同的调用者注册不同的 lambda 时,这才是唯一的。对 My_class::using_api
的两次调用仍然会发生冲突(第二次调用将覆盖第一次的 lambda)。
唯一令人讨厌的事情是,一旦设置了静态,我们就丢弃了包装器……保留它会更好,这样我们就可以用它来注销回调或其他东西。如果你愿意,你可以这样做,但由于你无法知道它在你的 lambda 范围之外的类型,它需要做一些动态的事情。
我使用一个 API 声明是:
some_API.h
typedef void (CALLBACK *EventCallback)();
class some_API{
public:
// ...
void EnableInterrupt(EventCallback func1, EventCallback func2);
// ...
};
在另一边我有一个 class 使用这个 API:
My_class.h
class My_class
{
some_API API;
void CALLBACK my_func() { cout << "This is my_func!\n" }
public:
void using_api()
{
API.EnableInterrupt(my_func, nullptr);
}
};
主要问题是 my_func 的类型,错误:
Error (active) E0167 argument of type "void (__stdcall My_class::*)()" is incompatible with parameter of type "EventCallback"
我找到了 this 答案,但问题是 API 是闭源的,所以我无法更改 void EnableInterrupt(EventCallback, EventCallback)
.
我也不想将 My_class::using_API
和 My_class::API
声明为静态成员。
我想要类似这样的东西:
API.EnableInterrupt((static_cast<void*>(my_func)), nullptr);
// or
API.EnableInterrupt((static_cast<EventCallback>(my_func)), nullptr); // invalid type conversion
有什么方法可以将该成员函数转换为非成员函数以将其传递给 some_API::EnableInterrupt(...)
?
当您需要 link 回调函数与您的 class 实例时,C 和 C++ 之间的回调工作得很好。为了使其工作,您需要从 C 端传递信息(通常是在回调注册时提供的 void* )。最简单的方法是使用静态方法和 this
指针:
typedef void (*EventCallback)(void*)();
void EnableInterrupt(EventCallback func1, void *userdata);
static void CallbackWrapper(void* data) {
static_cast<My_class*>(data)->my_func();
}
EnableInterrupt(CallbackWrapper, this)
现在,当你的回调是这样的:
typedef void (CALLBACK *EventCallback)();
这可能是因为设计不当,也可能是 API 设计者不希望您注册多个回调处理程序。这可能是一个重要的问题,考虑到该函数会干扰中断,如果他们不希望您注册多个处理程序,我会理解。
因此,您只需要创建一个 class 实例,因此单例模式可能是一个不错的选择。为此,您必须执行以下操作:
void CALLBACK global_callback_handler() {
My_class::getInstance()->my_func()
}
void using_api()
{
API.EnableInterrupt(global_callback_handler);
}
你的问题是回调类型
typedef void (CALLBACK *EventCallback)();
没有任何用户提供的数据指针。这是习惯做法,正是因为用户经常需要它。
为了完整起见,
Is there any way to cast that member function to a non-member function
不,它们根本不同,因为必须使用隐式 this
指针调用非静态成员函数,并且没有地方可以将其存储在常规函数指针中。这正是我们使用用户提供的指针参数 for 如果 API 有一个。
Also I don't want to declare My_class::using_API and My_class::API as static member.
如果我们为每个不同的注册生成另一个 static
怎么样?我们可以通过使用 lambda 类型唯一性来自动化它:
template <typename Lambda>
class TrampolineWrapper
{
inline static std::optional<Lambda> dest_; // optional so we can defer initialization
public:
TrampolineWrapper(some_API *api, Lambda &&lambda)
{
// store your _stateful_ functor into a unique static
dest_.emplace( std::move(lambda) );
// register a unique _stateless_ lambda (convertible to free function)
api->EnableInterrupt(
[](){
(*TrampolineWrapper<Lambda>::dest_)();
},
nullptr);
}
};
// handy type-deducing function
template <typename Lambda>
TrampolineWrapper<Lambda> wrap(some_API *api, Lambda &&lambda)
{
return {api, std::move(lambda)};
}
并像
一样使用它class My_class
{
some_API API;
void my_func() { cout << "This is my_func!\n" }
public:
void using_api()
{
auto wrapper = wrap(&API, [this](){ my_func(); });
// stateful lambda ^^^^
// not directly convertible to a free function
}
};
这取决于要工作的 lambda 类型的唯一性 - 您使用它的每个 lambda 都会得到一个相同类型的唯一静态 dest_
对象(可能有状态)和一个独特的无状态转发 lambda(对于这个可怕的 API 可以转换为免费函数)。
请注意,只有当您有多个不同的调用者注册不同的 lambda 时,这才是唯一的。对 My_class::using_api
的两次调用仍然会发生冲突(第二次调用将覆盖第一次的 lambda)。
唯一令人讨厌的事情是,一旦设置了静态,我们就丢弃了包装器……保留它会更好,这样我们就可以用它来注销回调或其他东西。如果你愿意,你可以这样做,但由于你无法知道它在你的 lambda 范围之外的类型,它需要做一些动态的事情。