lua-intf
lua-intf copied to clipboard
beginExtendClass<> does not currently support multiple inheritance (vtables)
This isn't really a bug report, so much as an FYI. static_cast<> doesn't work as expected when the type of the source pointer has more than one base with a virtual table. This causes CppObject::get<T>() to return the wrong pointer when T has multiple base virtual tables.
I was able to work around the problem for my application (in VC++2013) with a few pretty simple modifications:
- added a new objectPtr(void* class_id, bool is_const) overload to CppObject that calls objectPtr() by default;
- implemented a helper template, Upcast<class Derived>, with a function void* get(Derived* d, void* other_id, bool is_const) that returns d when not specialized for a particular class, or that, when specialized, should search up the hierarchy from d for a base class with matching class_id;
- changed calls to objectPtr() in CppObject methods into calls to objectPtr(class_id,is_const);
- implemented an override of objectPtr(void*,bool) in CppObjectSharedPtr<SP,T> that uses Upcast<T>::get() to search for the matching base class; and
- implemented specializations of Upcast<> for my class hierarchy (used macros, but could also be done with C++11 for a class hierarchy having consistently-defined in-class typedef's of base and derived classes).
Here are snippets of the relevant changes:
- Unspecialized Upcast<>:
template<class Derived> struct Upcast {
static void* get(Derived* derived, void* other_id, bool is_const) {
void* class_id = CppObject::getClassID<Derived>(is_const);
return (class_id == other_id) ? derived : nullptr;
}
};
- CppObject:
virtual void* objectPtr(void* /*class_id*/, bool /*is_const*/) {
return objectPtr();
}
template <typename T>
static T* cast(lua_State* L, int index, bool is_const)
{
void* class_id = getClassID<T>(is_const);
CppObject* object = getObject(L, index, class_id, is_const, false, false);
return object ? static_cast<T*>(object->objectPtr(class_id, is_const)) : nullptr;
}
template <typename T>
static T* get(lua_State* L, int index, bool is_const)
{
void* class_id = getClassID<T>(is_const);
return static_cast<T*>(getObject<T>(L, index, is_const)->objectPtr(class_id, is_const));
}
- CppSharedObjectPtr:
virtual void* objectPtr(void* class_id, bool is_const) override
{
auto derived = const_cast<T*>(&*m_sp);
return Upcast<T>::get(derived, class_id, is_const);
}
- Example specialization:
template<> struct Upcast<ClassA> {
typedef ClassA Derived;
typedef ClassA::Base Base;
static void* get(Derived* derived, void* other_id, bool is_const) {
void* class_id = CppObject::getClassID<Derived>(is_const);
return (class_id == other_id) ? derived : Upcast<Base>::get(derived, other_id, is_const);
}
};
This did the trick for my current set of test cases. CppObjectPtr would need to be made into a template class, but otherwise it could be modified the same was as CppSharedObjectPtr. I am currently only using CppSharedObjectPtr (via boost::intrusive_ptr), so I did not make this modification.
Hope this can be of use to someone.