Downcasting to furthest subclass when calling templated function

91 views Asked by At

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?

1

There are 1 answers

0
rpatters1 On BEST ANSWER

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

  • requires no pure virtual functions
  • does not require templatizing the base class (for reasons specific to my code base)

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*.