I use LuaBridge to import a large framework of classes into a Lua-accessible framework. LuaBridge uses complex template functions that maintain a list of linkages back to methods and properties of each class. The Lua language itself is loosely typed, and it does not check to see if a method or property exists until you call it. My framework implements a ClassName
method at every level that allows the Lua programs to know which class it is dealing with.
That's just background for my program. This is a C++ question. I would like to call a function that, in its broadest abstraction, looks something like this:
template <typename T>
do_something_in_lua(T * object); // LuaBridge will create a pointer to a T instance in Lua
and call it like this:
class foobase
{
public:
void notify_lua()
{ do_something_in_lua(this); }
};
class foo : public foobase
{
public:
int some_method();
};
foo x;
x.notify_lua();
My question is, is there a simple way for do_something_in_lua
to use the maximally downcasted version of T
? Either when I call it or in the templated function? Using dynamic_cast
is difficult because I would have to maintain an explicit list of every possible subclass to find the right one. Even if it is adding a templated virtual function in foobase
that returns the desired type of this
, I would be interested in suggestions for an elegant solution.
I guess a broader version of my question would be, does modern C++ provide any tools for downcasting in template programming (e.g., in type_traits
or the like) that I should be investigating?
Thanks to several helpful comments, the solution turns out to be a hybrid of CRTP and Double Dispatch. Here is a version of it based on my example above. I like the fact that it
If I ever need to add a new subclass, I just need to add it to the list in the std::variant
, and better yet, the compiler will complain until I do so.
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*.