DescribeIO Manual

About

A custom data type or an implementation of an interface will contain some internal data. A static custom DescribeIO() function describes how this data is written to or from a file.

Note
DescribeIO() does not directly access the file but instructs Cinema 4D how to handle the data.

Data Serialization

A DescribeIO() function handles the given maxon::DataSerializeInterface argument. This object should not be handled directly; only with these macros:

  • PrepareDescribe(): Starts a description. Must happen before any Describe() calls are made.
  • Describe(): Describes a class member for I/O operations.
  • DescribeOther(): Describes a class member for I/O operations. Allows to cast to a different DataType before the description is made.
  • DescribeLegacy(): Describes a legacy element that is no longer needed.

Certain elements may only be handled if a certain condition is fulfilled:

This snippet shows a simple custom data type:

// This example shows a simple custom data type.
class SimpleTriplet
{
public:
SimpleTriplet();
// DescribeIO() describes how the data type is stored in a file
maxon::Int _a; // first element
maxon::Int _b; // second element
maxon::Int _c; // third element
};
MAXON_DATATYPE(SimpleTriplet, "net.maxonexample.datatype.simpletriplet");

This is the straightforward implementation of DescribeIO().

// This example shows a simple implementation of DescribeIO().
// The member data of the data type is simply stored in the file.
{
PrepareDescribe(stream, SimpleTriplet);
// each member variable is stored in the file
return maxon::OK;
}

DescribeIO() is invoked when an instance of the data type is written to or read from a file:

// This example creates an instance of a custom data type that implements DescribeIO().
// The instance is saved to a file using maxon::WriteDocument(). After that,
// maxon::ReadDocument() is used to read the data from the given file.
// define data
SimpleTriplet triplet;
triplet._a = 100;
triplet._b = 200;
triplet._c = 300;
// file URL
const maxon::Url url = (targetFolder + "triplet.txt"_s)iferr_return;
const maxon::Id fileID { "net.maxonexample.simpletriplet" };
// save to file
// read from file
SimpleTriplet loadedTriplet;
maxon::ReadDocument(url, fileID, loadedTriplet) iferr_return;
// display loaded data
DiagnosticOutput("Loaded Triplet: @, @, @", loadedTriplet._a, loadedTriplet._b, loadedTriplet._c);

The write-process will result in a file like this:

{
"identification": "net.example.simpletriplet",
"content": {
"_a": 100,
"_b": 200,
"_c": 300
}
}

Helper Classes

A helper class is used to define code that is executed when Cinema 4D handles the data. The helper class is used with these macros:

  • PrepareHelper(): Declares and initializes an I/O helper class.
  • DescribeHelper(): Describes a helper class member for I/O operations
  • DescribeHelperIf(): Describes a helper class member for I/O operations and compares its value.
  • CONSTHASH(): Expression to calculate the hash code of a member name during compile time. Such a hash value is needed in WriteAction() and ReadAction().
// This example shows a helper class and how it is used in an implementation of DescribeIO().
// The functions of the helper class are invoked as defined with PrepareHelper().
// The helper functions handle modified data that is stored with a file.
// helper class
class TripletHelperClass
{
public:
// WriteAction() is called when data is written to a file, as defined with PrepareHelper()
maxon::Result<void> WriteAction(maxon::DataSerializeInterface* ds, const HelperTriplet& triplet, maxon::UInt elementHash)
{
// construct on initialization
if (elementHash == maxon::INITIALIZEHASH)
{
// all the data should be stored as a single string
// the triplet values are merged into this single string
_combined = maxon::String::IntToString(triplet._a);
_combined += "::"_s;
_combined += maxon::String::IntToString(triplet._b);
_combined += "::"_s;
_combined += maxon::String::IntToString(triplet._c);
}
return maxon::OK;
}
// ReadAction() is called when data is read from a file, as defined with PrepareHelper()
maxon::Result<void> ReadAction(maxon::DataSerializeInterface* ds, HelperTriplet& triplet, maxon::UInt elementHash)
{
// deconstruct on finalization
if (elementHash == maxon::FINALIZEHASH)
{
// all data is stored in a single string
// split string
_combined.Split("::"_s, true, parts) iferr_return;
// check element count
const maxon::Int count = parts.GetCount();
DebugAssert(count == 3);
// store values in the original data type
triplet._a = parts[0].ToInt() iferr_return;
triplet._b = parts[1].ToInt() iferr_return;
triplet._c = parts[2].ToInt() iferr_return;
}
return maxon::OK;
}
maxon::String _combined; // string containing all data
};
{
PrepareDescribe(stream, HelperTriplet);
// prepare helper class
// the helper functions should be invoked on initialization / finalization
// the helper's "_combined" member will be stored in the file
return maxon::OK;
}

This will result in a file like this:

{
"identification": "net.example.helpertriplet",
"content": {
"data": "100::200::300"
}
}

This more complex example uses DescribeHelperIf():

// This example shows a helper class and how it is used in an implementation of DescribeIO().
// The helper class analyses the values stored in the given object. If all triplet values are the same,
// only one number is stored in a file. If the values are not the same, all values are stored.
// helper class
class CompactTripletHelperClass
{
// hashes for relevant helper members
// used to identify values handled in WriteAction() / ReadAction()
CONSTHASH(_compact); // hash for _compact
CONSTHASH(_compactValue); // hash for _compactValue
CONSTHASH(_z); // hash for _z
public:
// WriteAction() is called as defined with DescribeHelperIf()
maxon::Result<void> WriteAction(maxon::DataSerializeInterface* ds, const CompactTriplet& triplet, maxon::UInt elementHash)
{
// writing the " _compact" value
if (elementHash == _compactHash)
{
// if all three values are the same, store only one value in "compact" mode
const maxon::Bool aEqualsB = triplet._a == triplet._b;
const maxon::Bool bEqualsC = triplet._b == triplet._c;
if (aEqualsB && bEqualsC)
{
_compact = true;
_compactValue = triplet._a;
}
else
{
// store each individual value
_compact = false;
_x = triplet._a;
_y = triplet._b;
_z = triplet._c;
}
}
return maxon::OK;
}
// ReadAction() is called as defined with DescribeHelper()
maxon::Result<void> ReadAction(maxon::DataSerializeInterface* ds, CompactTriplet& triplet, maxon::UInt elementHash)
{
switch (elementHash)
{
// load compact value; DESCRIBEFLAGS::ACTION_AFTER_READ was set
case (_compactValueHash):
{
triplet._a = _compactValue;
triplet._b = _compactValue;
triplet._c = _compactValue;
break;
}
// last individual value has been loaded; DESCRIBEFLAGS::ACTION_AFTER_READ was set
case (_zHash):
{
// load all values after the third value has been read
triplet._a = _x;
triplet._b = _y;
triplet._c = _z;
break;
}
default: { break; }
}
return maxon::OK;
}
// helper class member data
// the member name must be the same as the name used with DescribeHelper()
maxon::Bool _compact; // compact mode
maxon::Int _compactValue; // compact value
maxon::Int _x; // x value
maxon::Int _y; // y value
maxon::Int _z; // z value
};
{
PrepareDescribe(stream, CompactTriplet);
// prepare helper
PrepareHelper(CompactTripletHelperClass, maxon::PREPAREHELPERFLAGS::NONE);
// check if compact mode or not when writing to file
{
// store compact value; handle values after reading
}
{
// simply write/read all values into the helper class
// after all values have been loaded, copy to data type
}
return maxon::OK;
}

A "compact" file using the same value for each channel will look like this:

{
"identification": "net.example.compacttriplet",
"content": {
"_compact": true,
"_compactValue": 100
}
}

A non-compact file will look like this:

{
"identification": "net.example.compacttriplet",
"content": {
"_compact": false,
"_x": 10,
"_y": 100,
"_z": 1000
}
}

Further Reading

maxon::ReadDocument
std::enable_if< GetCollectionKind< T >::value==COLLECTION_KIND::ARRAY, Result< void > >::type ReadDocument(const Url &url, const Id &id, T &object, const DataDictionary &dict=DataDictionary())
Definition: io.h:35
maxon::DataSerializeInterface
Definition: dataserialize.h:205
maxon::DescribeIO
Result< void > DescribeIO(const T &s, const DataSerializeInterface &dsi)
Definition: datatypefunctions.h:21
Describe
#define Describe(name, memberName, type, flags)
Definition: dataserialize.h:293
CONSTHASH
#define CONSTHASH(x)
Expression to calculate the hash code of a member name during compile time.
Definition: dataserialize.h:194
maxon::WriteDocument
Result< void > WriteDocument(const Url &url, const Id &id, const T &object, IOFORMAT format=IOFORMAT::DEFAULT, const DataDictionary &dict=DataDictionary())
Definition: io.h:67
maxon
The maxon namespace contains all declarations of the MAXON API.
Definition: c4d_basedocument.h:27
DescribeHelperIf
#define DescribeHelperIf(name, memberName, type, flags, mask, value)
Definition: dataserialize.h:388
maxon::String
Definition: string.h:1197
MAXON_DATATYPE
#define MAXON_DATATYPE(type, id)
Definition: datatype.h:309
maxon::OK
return OK
Definition: apibase.h:2490
maxon::DESCRIBEFLAGS::NONE
static const ValueType NONE
Default.
Definition: dataserialize.h:136
maxon::Bool
bool Bool
boolean type, possible values are only false/true, 8 bit
Definition: apibase.h:177
PrepareDescribe
#define PrepareDescribe(streamClass, className)
Definition: dataserialize.h:267
maxon::Id
Definition: apibaseid.h:273
iferr_return
#define iferr_return
Definition: resultbase.h:1434
maxon::DESCRIBEFLAGS::ACTION_BEFORE_WRITE
static const ValueType ACTION_BEFORE_WRITE
If set there will be a call to the helper class WriteAction member function before an element is writ...
Definition: dataserialize.h:142
maxon::BaseArray
Definition: basearray.h:366
DiagnosticOutput
#define DiagnosticOutput(formatString,...)
Definition: debugdiagnostics.h:168
maxon::Url
Definition: url.h:819
maxon::FINALIZEHASH
static const UInt FINALIZEHASH
Definition: dataserialize.h:172
DescribeHelper
#define DescribeHelper(name, memberName, type, flags)
Definition: dataserialize.h:344
maxon::Result< void >
OK
OK
Ok.
Definition: ge_prepass.h:2
maxon::BaseArray::GetCount
MAXON_ATTRIBUTE_FORCE_INLINE Int GetCount() const
Definition: basearray.h:527
maxon::IOFORMAT::JSON
@ JSON
maxon::Int
Int64 Int
signed 32/64 bit int, size depends on the platform
Definition: apibase.h:184
iferr_scope
#define iferr_scope
Definition: resultbase.h:1343
maxon::PREPAREHELPERFLAGS::INITIALIZE_WRITE
@ INITIALIZE_WRITE
The helper's WriteAction will get a call with INITIALIZEHASH before the class has been written.
DescribeEndIf
#define DescribeEndIf()
Definition: dataserialize.h:429
DescribeElse
#define DescribeElse()
Definition: dataserialize.h:410
maxon::PREPAREHELPERFLAGS::FINALIZE_READ
@ FINALIZE_READ
The helper's ReadAction will get a call with FINALIZEHASH after the class has been read.
PrepareHelper
#define PrepareHelper(helperName, flags)
Definition: dataserialize.h:397
maxon::UInt
UInt64 UInt
unsigned 32/64 bit int, size depends on the platform
Definition: apibase.h:185
maxon::INITIALIZEHASH
static const UInt INITIALIZEHASH
Definition: dataserialize.h:171
maxon::DESCRIBEFLAGS::ACTION_AFTER_READ
static const ValueType ACTION_AFTER_READ
If set there will be a call to the helper class ReadAction member function after an element is read.
Definition: dataserialize.h:143
DebugAssert
#define DebugAssert(condition,...)
Definition: debugdiagnostics.h:247
maxon::PREPAREHELPERFLAGS::NONE
@ NONE