C++ 静态虚拟方法的替代方法
在 C++ 中不能声明静态虚函数,也不能将非静态函数转换为 C 风格的函数指针.
In C++ is not possible to declare a static virtual function, neither cast a non-static function to a C style function pointer.
现在,我有一个大量使用函数指针的普通 C SDK.
Now, I have a plain ol' C SDK that uses function pointers heavily.
我必须用几个函数指针填充一个结构.我计划使用带有一堆静态纯虚方法的抽象类,并在派生类中重新定义它们并用它们填充结构.直到那时我才意识到 C++ 中不允许使用静态虚拟.
I have to fill a structure with several function pointers. I was planning to use an abstract class with a bunch of static pure virtual methods, and redefine them in derived classes and fill the structure with them. It wasn't until then that I realized that static virtual are not allowed in C++.
此外,此 C SDK 函数签名没有 userData 参数.
Also this C SDKs function signature doesn't have a userData param.
有什么好的选择吗?我能想到的最好的方法是在每个派生类中定义一些纯虚方法 GetFuncA()、GetFuncB()...和一些静态成员 FuncA()/FuncB(),它们将由 GetFuncX() 返回.然后抽象类中的函数会调用这些函数来获取指针并填充结构.
Is there any good alternative? The best I can think of is defining some pure virtual methods GetFuncA(), GetFuncB(),... and some static members FuncA()/FuncB() in each derived class, which would be returned by the GetFuncX(). Then a function in the abstract class would call those functions to get the pointers and fill the structure.
编辑回答 John Dibling,能够做到这一点会很棒:
Edit Answering to John Dibling, it would be great to be able to do this:
class Base
{
FillPointers() { myStruct.funA = myFunA; myStruct.funB = myFunB; ...}
private:
CStruct myStruct;
static virtual myFunA(...) = 0;
static virtual myFunB(...) = 0;
};
class Derived1 : public Base
{
Derived1() { FillPointers(); }
static virtual myFunA(...) {...};
static virtual myFunB(...) {...};
};
class Derived2 : public Base
{
Derived2() { FillPointers(); }
static virtual myFunA(...) {...};
static virtual myFunB(...) {...};
};
int main()
{
Derived1 d1;
Derived2 d2;
// Now I have two objects with different functionality
}
推荐答案
你可以让 Base
成为一个类模板,从模板参数中获取函数指针:
You can make Base
be a class template that takes its function pointers from its template argument:
extern "C" {
struct CStruct
{
void (*funA)(int, char const*);
int (*funB)(void);
};
}
template <typename T>
class Base
{
public:
CStruct myStruct;
void FillPointers() {
myStruct.funA = &T::myFunA;
myStruct.funB = &T::myFunB;
}
Base() {
FillPointers();
}
};
然后,使用每个派生类作为模板参数,将派生类定义为从 Base
的实例派生:
Then, define your derived classes to descend from an instantiation of Base
using each derived class as the template argument:
class Derived1: public Base<Derived1>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 0; }
};
class Derived2: public Base<Derived2>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 1; }
};
int main() {
Derived1 d1;
d1.myStruct.funA(0, 0);
d1.myStruct.funB();
Derived2 d2;
d2.myStruct.funA(0, 0);
d2.myStruct.funB();
}
这种技术被称为奇怪的重复模板模式.如果您忽略在派生类中实现其中一个函数,或者如果您更改了函数签名,您将收到一个编译错误,这正是您在忽略实现一个纯虚函数时所期望得到的原计划中的功能.
That technique is known as the curiously recurring template pattern. If you neglect to implement one of the functions in a derived class, or if you change the function signature, you'll get a compilation error, which is exactly what you'd expect to get if you neglected to implement one of the pure virtual functions from your original plan.
然而,这种技术的结果是 Derived1
和 Derived2
没有共同的基类.就类型系统而言,Base<>
的两个实例没有任何关系.如果你需要把它们关联起来,那么你可以引入另一个类作为模板的基础,然后把通用的东西放在那里:
The consequence of this technique, however, is that Derived1
and Derived2
do not have a common base class. The two instantiations of Base<>
are not related in any way, as far as the type system is concerned. If you need them to be related, then you can introduce another class to serve as the base for the template, and then put the common things there:
class RealBase
{
public:
CStruct myStruct;
};
template <typename T>
class Base: public RealBase
{
// ...
};
int main()
RealBase* b;
Derived1 d1;
b = &d1;
b->myStruct.funA(0, 0);
b->myStruct.funB();
Derived2 d2;
b = &d2;
b->myStruct.funA(0, 0);
b->myStruct.funB();
}
注意:静态成员函数不一定与普通函数指针兼容.根据我的经验,如果编译器接受上面显示的赋值语句,那么您至少可以确信它们与编译器兼容.此代码不可移植,但如果它适用于您需要支持的所有平台,那么您可能会认为它足够可移植".
Beware: Static member functions are not necessarily compatible with ordinary function pointers. In my experience, if the compiler accepts the assignment statements shown above, then you can at least be confident that they're compatible for that compiler. This code isn't portable, but if it works on all the platforms you need to support, then you might consider it "portable enough."
相关文章