我使用LuaBridge将一个大型类框架导入到Lua可访问的框架中。LuaBridge使用复杂的模板函数,这些函数维护一个链接到每个类的方法和属性的列表。Lua语言本身是松散类型的,在你调用它之前,它不会检查方法或属性是否存在。我的框架在每个级别上实现了一个ClassName
方法,允许Lua程序知道它在处理哪个类。
这只是我项目的背景。这是一个C++问题。我想调用一个函数,在其最广泛的抽象中,它看起来像这样:
template <typename T>
do_something_in_lua(T * object); // LuaBridge will create a pointer to a T instance in Lua
并这样称呼它:
class foobase
{
public:
void notify_lua()
{ do_something_in_lua(this); }
};
class foo : public foobase
{
public:
int some_method();
};
foo x;
x.notify_lua();
我的问题是,do_something_in_lua
是否有一种简单的方法来使用T
的最大下变频版本?无论是在我调用它时,还是在模板化函数中?使用dynamic_cast
很困难,因为我必须维护每个可能的子类的显式列表才能找到正确的子类。即使它在foobase
中添加了一个返回所需类型的this
的模板化虚拟函数,我也会对优雅解决方案的建议感兴趣。
我想我的问题的一个更广泛的版本是,现代C++是否提供了我应该研究的模板编程中的下转换工具(例如,在type_traits
等中(?
由于一些有用的评论,该解决方案是CRTP和Double Dispatch的混合。这是基于我上面的例子的一个版本。我喜欢
- 不需要纯虚拟函数
- 不需要模板化基类(由于特定于我的代码库的原因(
如果我需要添加一个新的子类,我只需要将其添加到std::variant
中的列表中,更好的是,编译器会抱怨,直到我这样做。
template <typename T>
void do_something_in_lua(T * object);
// every subclass of foobase must be listed here.
class foosub1;
class foosub2;
using foobase_typed_ptr = std::variant<foosub1*, foosub2*>;
class foobase
{
foobase_typed_ptr _typedptr;
public:
foobase(const foobase_typed_ptr &ptr) : _typedptr(ptr) {}
void notify_lua()
{
std::visit([](auto const &ptr)
{ do_something_in_lua(ptr); },
_typedptr);
}
};
class foosub1 : public foobase
{
public:
foosub1() : foobase(this) {}
int some_method1();
};
class foosub2 : public foobase
{
public:
foosub2() : foobase(this) {}
int some_method2();
};
// and to call it in code:
foosub2 x;
x.notify_lua(); // now Lua gets called with a foosub2* instead of a foobase*.