lua-intf icon indicating copy to clipboard operation
lua-intf copied to clipboard

beginExtendClass<> does not currently support multiple inheritance (vtables)

Open demianmnave opened this issue 9 years ago • 0 comments

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.

demianmnave avatar Sep 01 '15 07:09 demianmnave