将C++子类成员函数(虚拟实现)传递给 C 类型函数指针

Pass a C++ child class member function (virtual implementation) to a C-type function pointer

本文关键字:函数 指针 类型 实现 子类 C++ 成员 虚拟      更新时间:2023-10-16

我已经搜索并看到了许多与此主题相关的SO答案,但没有一个对我的情况有用。我必须将 C 类型函数指针传递给我正在使用的库的函数。下面是一个简短的描述:

baseClass.h

Class BaseClass
{
public:
virtual lib_returnType capabilityImplementation(lib_type input_1, ..., lib_type input_11) = 0;
bool performLocalCheckFct(...){ /* perform check */ }
lib_returnType methodCallback(lib_type input_1, ..., lib_type input_11)
{
if(performLocalCheckFct(...)== true)
{
return capabilityImplementation(input_1, ..., input_11);
}
}
/* something else */
protected:
/* some data */
}

我使用此基类来实现其他特定于用例的子类,这些子类具有相同的结构,但以不同的方式实现功能实现功能

ChildClass_1.h

Class ChildClass_1 : public BaseClass
{
public:
ChildClass_1(){ /* modify base class protected data */ }
lib_returnType capabilityImplementation(lib_type input_1, ..., lib_type input_11)
{ /* do something */ }
}

C-回调在"ImplementationClass"内的createMacaroni方法是必需的:

ImplementationClass.h

typedef lib_returnType (*c_type_FC)(lib_type, ..., lib_type);
Class HandlerClass{
public:
HandlerClass(lib_type input_1){
initializeHashMap();
/* do something else */
}
initializeHashMap(){
childHashTable["myChild1"] = std::make_shared<ChildClass_1>();
/*... e.g. 50 different child classes */
childHashTable["myChild50"] = std::make_shared<ChildClass_50>();
}
bool createMacaroni(std::string childClassName) {
/* do stuff */
/* perform C-type function pointer binding here*/ 
c_type_FC callback = /* get member function BaseClass::methodCallback from object stored in hashpmap childHashTable[childClassName]*/ ;
/* lib_function is the function I have to pass the pointer to */
lib_function(input_1, ..., callback);
}
private:
std::unordered_map<std::string, std::shared_ptr<BaseClass> > childbHashTable;
}

尝试的解决方案

我可以使用创建静态 std::function 实现的类模板获得一个工作解决方案,但每个ChilClass_n都需要大量代码行:

template <typename T>
struct CB_capab;
template <typename Ret, typename... Params>
struct CB_capab<Ret(Params...)> {
template <typename... Args>
static Ret callback(Args... args) {
func(args...);
}
static std::function<Ret(Params...)> func;
};
template <typename Ret, typename... Params>
std::function<Ret(Params...)> CB_capab<Ret(Params...)>::func;

并且使用 std::bind 在创建通心粉方法中实现如下:

CB_capab<UA_StatusCode(lib_type, ... lib_type)> myClassTemplate;
myClassTemplate.func = std::bind(&BaseClass::methodCallback, capabHashTable[childClassName], std::placeholders::_1, ..., std::placeholders::_11);
lib_returnType local_cb = static_cast<lib_returnType>(myClassTemplate.callback);

这是非常糟糕的,因为它需要 10^2 个子类中的每一个都有很多代码行。

有什么想法吗?

若要调用非静态成员函数C++需要两位信息,即要调用的成员函数的地址和调用该函数的对象的地址。其中作为 C 函数(或 C++ 中的自由函数(只需要函数的地址。这意味着不能将非静态成员函数折叠为函数指针。

这并不意味着你想做的事情是不可能的。通常,接受函数指针的 C 库还接收指向用户数据的指针,这些数据将在调用时传递到指向函数中。您可以使用它来传递指向对象的指针。为此,可以使用自由函数或静态成员函数。下面是如何做到这一点的粗略示例。

// This would be the callback function signature your library is expecting
void typedef (* callback)(void * userState, int data);
class Foo
{
// Your original member function you want to call
void doSomething(int data) { /* stuff */ }
// The static member function that can be passed around as a function pointer
static void wrapDoSomething(void * userState, int data)
{
Foo * ptr = static_cast<Foo *>(userState);
ptr->doSomething(data);
}
// Your way to have the object register a callback
void registerCallback()
{
lib_register_function(&Foo::wrapDoSomething, this);
}
};

在此示例中,wrapDoSomething()registerCallback()不必是成员函数。严格来说,这里的所有函数都不需要公开,但是将doSomething()等效的函数公开是有意义的,因为它最终是从类外部调用的。

所以我使用的库是open62541堆栈,接收C函数指针的函数是UA_Server_addMethodNode(在原始问题中称为lib_function(。

正如@bobshowrocks(以及注释@Jarod42(提到的,我使用未在任何点设置的void *methodContext指针(等于 void *user_data 指针(和 BaseClass 中额外的静态成员函数解决了这个问题。我还必须修改我的 ImplementationClass.h 中的一些内容,以便哈希映射不会超出范围。

所以BaseClass.h转向(添加了一个额外的静态成员函数(:

Class BaseClass
{
public:
virtual UA_StatusCode capabilityImplementation(/* same inputs as staticMethodCallback */) = 0;
bool performLocalCheckFct(...){ /* perform check */ }
UA_StatusCode methodCallback(/* same inputs as staticMethodCallback */)
{
if(performLocalCheckFct(...)== true)
{
return capabilityImplementation(input_1, ..., input_11);
}
}
static UA_StatusCode staticMethodCallback(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *methodId, void *methodContext,
const UA_NodeId *objectId, void *objectContext,
size_t inputSize, const UA_Variant *input,
size_t outputSize, UA_Variant *output)
{
BaseClass* ptr = static_cast<BaseClass *>(methodContext);
ptr->methodCallback(server, sessionId, sessionContext, methodId, NULL, objectId, objectContext, inputSize, input, outputSize, output);
}
/* something else */
protected:
/* some data */
}

它在ImplementationClass.h中实现为:

Class HandlerClass{
public:
HandlerClass(lib_type input_1){
initializeHashMap();
/* do something else */
}
initializeHashMap(){
childHashTable["myChild1"] = std::make_shared<ChildClass_1>();
/*... e.g. 50 different child classes */
childHashTable["myChild50"] = std::make_shared<ChildClass_50>();
}
bool createMacaroni(std::string childClassName) {
/* do stuff */
/* Get the respective pointer of object to refer the callback */
BaseClass* ptr_for_callback = capabHashTable[childClassName].get();
/* Define the function callback */
UA_MethodCallback local_cb = &BaseClass::staticMethodCallback;
/* lib_function is the function I have to pass the pointer to */
UA_Server_addMethodNode(input_1, ..., local_cb, ..., ptr_for_callback, ..., input_11);
}
private:
static std::unordered_map<std::string, std::shared_ptr<BaseClass> > childbHashTable;
}
std::unordered_map< std::string, std::shared_ptr<BaseClass> > HandlerClass::capabHashTable;

请注意类内部哈希映射的静态定义和旨在避免全局变量的相应外部定义。

再次感谢@Jarod42和@bobshowrocks