Alternative to C++ vtable modification to promote objects on document load

Okay so this is starting to really bother me. And introduce bugs since my constructor is never called and my subclass members have wild initialized values after "promotion. Is there a better alternative to doing the below? This seems like a giant hack to me.

If I must stick with this, then please explain how I can init my data members post promotion, since the cost reference to my new object doesn’t allow me to modify anything… (reinit() call doesn’t compile)

void CSuperDEventWatcher::promoteRhinoMeshObjectToSuperDObject(const CRhinoMeshObject *meshObj)
{
  static void *CSuperDMeshObject__vtable_ptr = 0;
  static void *CRhinoMeshObject__vtable_ptr = 0;

  if(!meshObj)
    return;

  if(!CSuperDMeshObject__vtable_ptr)
  {
    // The first time we call this function, the statics are initialized
    CRhinoSuperDMeshObject a;
    CSuperDMeshObject__vtable_ptr = ON_ClassVtable((void *)(&a));
    CRhinoMeshObject b;
    CRhinoMeshObject__vtable_ptr = ON_ClassVtable((void *)(&b));
  }

  if(CRhinoMeshObject__vtable_ptr == *((void **)meshObj))
  {
    // Change from CRhinoMeshObject vtable to CSuperDMeshObject__vtable_ptr vtable
    *((void**)meshObj) = CSuperDMeshObject__vtable_ptr;

    if(auto *superDmesh = CRhinoSuperDMeshObject::Cast(meshObj))
    {
      superDmesh->reinit();
    }
  }
}

Edit

I’ve modified my code to make a copy instead below, is there anything that will break rhino object consistency if I do this?

if(CRhinoMeshObject__vtable_ptr == *((void **)meshObj))
{
  //Change from CRhinoMeshObject vtable to CSuperDMeshObject__vtable_ptr vtable
  //*((void**)meshObj) = CSuperDMeshObject__vtable_ptr;

  /*if(auto *superDmesh = CRhinoSuperDMeshObject::Cast(meshObj))
  {
    superDmesh->reinit();
  }*/

  auto userDataCopy = new CSuperDUserData(*CSuperDUserData::Cast(meshObj->GetAttributeUserData(CSuperDUserData::Id())));
  auto superDMesh = new CRhinoSuperDMeshObject();
  superDMesh->DeleteAttributeUserData(CSuperDUserData::Id());
  superDMesh->AttachAttributeUserData(userDataCopy);
  superDMesh->resurface();

  doc.ReplaceObject(CRhinoObjRef(meshObj), superDMesh);
}

Hi @gccdragoonkain,

First, Rhino does not allow for a true custom object. If we did, would be possible to distribute 3dm files that others could not open without your plug-in.

However, you are allowed to derive classes from our run-time object classes, such as CRhinoPointObject, CRhinoCurveObject, CRhinoBrepObject, etc. and override any virtual members you want.

If your derived class has additional data, then for this data to persist it must be store as user data using a class that derives from ON_UserData. The reason for this is that custom objects do not serialize - Rhino will only serialize Rhino objects. By storing your data as user data on the object, your custom data will persist.

When reading a 3dm file, again, Rhino only reads Rhino objects. However, when a Rhino object is read, your attached user data will also be read (by your plug-in).

At some point, when the file I/O has completed, you have the opportunity to convert the Rhino object, along with your attached user data, to your own object. One way of doing this is by using the vtable slight-of-hand, which is the most efficient. Another way is to construct one of your objects from the user data and then call CRhinoDoc::ReplaceObject as you’ve mentioned.

Hope this helps.

– Dale

Thanks Dale. So just to confirm, the replaceobject method is a valid way to do this? I’m willing to accept the overhead incurred by the copy in this case. I’m assuming an object UUID change will occur, but thats likely ok?

Hi @gccdragoonkain,

I guess if it works, then it’s valid. The vtable method is certainly the most efficient; it causes no document churn. If you class needs some kind of initialization, then you mighi consider doing so i a lazy manner, such as the the first time your object’s Draw member is called.

– Dale