C++ Serialize my own class

Hi,

i would need an advice on how to serialize my own class. The class will contain different geometries (Mesh, Curve, Points, Brep) among other data.

One way I see is to derive my class from one type of RhinoObject (for example CRhinoCurveObject) and add all other geometries and variables as User Data. I still do not know how to do this and serialize it afterwards, but before the research I wanted to ask if this is the best way? I guess this is the starting point of the research: http://wiki.mcneel.com/developer/userdata

Since it is not necessary for the class to be a part of the Rhino document (although it might be convenient)…is it maybe easier to just derive my class from ON_Object and somehow use the Write method to save all of its members, using this example: http://wiki.mcneel.com/developer/sdksamples/archivecurve
This example seems much simpler to me…I only dont know how to “group” the geometries that belong to one class object. If my class object has a Curve, Mesh, Points and a Brep and I save them all using Write Object…later when I read them, how do I group them back together? How do I “recognize” which ones belong to which object in the MyClass array? Actually if think about it, my class doesn`t need to be derived from ON_Object(?)…only the data it contains…I just need to find a way to reconstruct the class array when data and geometries are loaded…

Thanks!

Hi Milos,

I’ll probably need more information to provide a good recommendation. For example, its not clear to me whether or not you want to serialize your data to 3dm files or to your own file format? Any more details about what you are doing might be helpful.

If you want to save data to 3dm files, you only have two options: 1.) Save data in the ‘document’, we call this document user data, or 2.) Save data on some object, or object user data.

And for both options, you can either write user text (simple, there are plenty of examples of this around), or you can implement complex serialization your self via deriving from ON_UserData.

Anyway, perhaps I can get some specifics from you and then provide you with more details or an example.

Thanks,

– Dale

Hi Dale,

thanks for the quick response. I want to save my data in the .3dm file (my own file would also be an option, but it is all the same fo me and I am guessing 3dm is easier). Let us say I have an array of objects of this class:

class MyClass
{
public:
  MyClass(void);
  ~MyClass(void);	

  ON_3dPointArray pts;
  ON_Mesh mesh;
  ON_Curve* crv;
  ON_SimpleArray<ON_Curve*> crvs;
  ON_Brep* brep;

  int a1;
  double b1;
  CString c1;
};

How do I save them in a 3dm file and later load them, i.e. recreate the array with all the geometries and data inside? What is the easiest way?

Do these point to objects in Rhino’s document, or did you allocate memory for these yourself?

I allocated the memory myself…

Hi Milos,

See the attached sample below. Pay particular attention to the Read() and Write() members of CSampleSerializeData.

And, of course, let me know if you have any questions.

SampleSerialize.zip (13.2 KB)

Hi Dale,

thanks a lot. Let me just see if I understand how to implement it with my class and actually write the data into the file. As I understand CSampleSeriaIizeData represents MyClass that I am trying to save. I guess (following this example http://wiki.mcneel.com/developer/sdksamples/archivecurve) that I would first do:

ON_ClassArray < CSampleSerializeData > array;

FILE* fp;
ON_BinaryFile archive( ON::write3dm, fp );

for(i=0; i< array.Count(); i++)
{
CSampleSerializeData& data = SampleSerializePlugIn().Data();

//then I would get the geometry from my array[i] and use data.Set…() functions to copy everything from array[i] to the “data”? This copying seems redundant, but I dont see the way around it if we need this line above(which I dont quite understand)…and now? Simply:

data.Write( archive ); (or data.Read( archive); for loading)
}

Is this correct? When I read the file, how do I recognize different class objects? Do you think I should put in some “int counter” or “int id” as the class member to recognize when all the elements of one class are loaded and the other ones start? (I am guessing that all the elements will be written in the same order in which they are read?)

P.S. This would save an array of my objects in a separate 3dm file? Is there an easy way to save them together with the objects from the RhinoDocument? All in one file? And then recognize them when unserializing?

Thanks again!

You would only create your own binary archive object if were planning on writing your own file to disk.

Regarding the writing and reading of an array of stuff you might do something like this for writing:

bool Write(FILE* fp, const ON_SimpleArray<CSampleSerializeData*>& data_table)
{
  if (0 == fp)
    return false;

  const int data_count = data_table.Count();

  ON_BinaryFile archive(ON::write3dm, fp);

  // Begin write
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 0);
  if (rc)
  {
    // Write version chunk
    rc = archive.Write3dmChunkVersion(1, 0);

    // Write integer
    if (rc) rc = archive.WriteInt(data_count);

    // Write data table
    if (rc)
    {
      for(int i = 0; rc && i < data_count; i++)
        rc = data_table[i]->Write(archive);
    }

    // End write
    if (!archive.EndWrite3dmChunk())
      rc = false;
  }

  return rc;
}

And something like this for reading:

bool Read(FILE* fp, ON_SimpleArray<CSampleSerializeData*>& data_table)
{
  if (0 == fp)
    return false;

  ON_BinaryFile archive(ON::read3dm, fp);

  int major_version = 0;
  int minor_version = 0;
  ON__UINT32 tcode = 0;
  ON__INT64 big_value = 0;

  // Begin read
  bool rc = archive.BeginRead3dmBigChunk(&tcode, &big_value);
  if (rc)
  {
    // Validate tcode
    if (rc) rc = (tcode == TCODE_ANONYMOUS_CHUNK);

    // Read version chunk
    if (rc) rc = archive.Read3dmChunkVersion(&major_version, &minor_version);

    // Validate version
    if (rc) rc = (major_version == 1);
    if (rc)
    {
      // Read integer
      int data_count = 0;
      rc = archive.ReadInt(&data_count);

      if (rc)
      {
        for(int i = 0; rc && i < data_count; i++)
        {
          CSampleSerializeData* data = new CSampleSerializeData();
          rc = data->Read(archive);
          if (rc)
            data_table.Append(data);
          else
            delete data; // Don't leak...
        }
      }
    }

    // End read
    if (!archive.EndRead3dmChunk())
      rc = false;
  }
  return rc;
}

Look through the CSampleSerializeData::Read code and let me know if this helps.

Yes, as I said, creating your own binary archive will save a new file. I this case, a non-3dm file.

My sample (attached above) demonstrates how to save in the current 3dm file that Rhino is saving. Have you built and run the project?

I should add that if you want to see more examples of how to properly serialize data, then download the openNURBS toolkit source code.

https://www.rhino3d.com/opennurbs