2d dynamic arrays to be readable/writable


#1

Hi,

In my plugin I’d need to have an array of double arrays of variable size, as part of my custom user data. I was using ON_SimpleArray<ON_SimpleArray<double>>, though this cannot be read/written to file by ON_BinaryArchive.

Is there a way to create such an array and store it as user data? In other frameworks I used double**, but in the Rhino SDK I’m not sure about how to properly declare and free it within the command.

Many thanks,
Pablo


(Dale Fugier) #2

Hi Pablo,

Assuming m_data is ON_SimpleArray<ON_SimpleArray<double>>, you can archive this as follows:

BOOL CTestData::Write(ON_BinaryArchive& archive) const
{
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0);
  if (!rc)
    return false;

  for (;;)
  {
    // Write m_data size
    rc = archive.WriteInt(m_data.Count());
    if (!rc) break;

    // Write m_data members
    for (int i = 0; i < m_data.Count() && rc; i++)
      rc = archive.WriteArray(m_data[i]);
    if (!rc) break;

    // TODO: write other stuff...

    break;
  }

  // If BeginWrite3dmChunk() returns true,
  // then EndWrite3dmChunk() must be called, 
  // even if a write operation failed.
  if (!archive.EndWrite3dmChunk())
    rc = false;

  return rc;
}

You could, then, read it back in like this:

BOOL CTestData::Read(ON_BinaryArchive& archive)
{
  int major = 1;
  int minor = 0;
  bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major, &minor);
  if (!rc)
    return false;

  for (;;)
  {
    rc = (1 == major);
    if (!rc) break;

    // Read m_data size
    int count = 0;
    rc = archive.ReadInt(&count);
    if (!rc) break;

    m_data.SetCapacity(count);

    // Read m_data members
    for (int i = 0; i < m_data.Count() && rc; i++)
    {
      ON_SimpleArray<double> datum;
      rc = archive.ReadArray(datum);
      if (rc)
        m_data.Append(datum);
    }
    if (!rc) break;

    // TODO: read other stuff...

    break;
  }

  // If BeginRead3dmChunk() returns true,
  // then EndRead3dmChunk() must be called, 
  // even if a read operation failed.
  if( !archive.EndRead3dmChunk() )
    rc = false;

  return rc;
}

– Dale


#3

This is very helpful!
Thank you very much Dale,

Pablo


#4

@dale I assume the code works well if m_data is ON_ClassArray<ON_SimpleArray<double>> instead, right?

Pablo


(Steve Baer) #5

Yes, use an ON_ClassArray of ON_SimpleArray instead. An ON_SimpleArray should only contain basic data types that don’t need to perform heap allocations/deletions in the constructors/destructors.


#6

Just for the record, when reading the data I had problems with SetCapacity, which apparently didn’t work. I removed it and wrote:

// Read m_data size
int count = 0;
rc = archive.ReadInt(&count);
if (!rc) break;

// Read m_data members
for (int i = 0; i < count && rc; i++)
{
  ON_SimpleArray<double> datum;
  rc = archive.ReadArray(datum);
  if (rc)
    m_data.Append(datum);
}
if (!rc) break;

Actually it’s not the first time I have issues with SetCapacity. When I initialize or reallocate an array, I use Reserve and SetCount instead. But I’d like to know the real difference between the three, and when they are best suited for. The help file is not clear at this point.

Pablo


(Dale Fugier) #7

Reserve just calls SetCapacity.

These implementation are slightly different between ON_SimpleArray and ON_ClassArray. If you are interested in the details, then you might want to download and review the openNURBS source code.

– Dale


#8

Many thanks Dale.

Btw, I’m quite amazed at how well it does the reading/writing of many arrays of different sizes, among many other members. When the Read function reads an integer count, how does it know that it corresponds to the array m_data and not another member of the class? Is it because I read the data in the same order that I wrote it?

Pablo


(Dale Fugier) #9

Again, the openNURBS source code is available for your review…

– Dale