DescribeIO Manual


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.

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
// 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
// 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
// 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;
// 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;
// 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;
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

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
Definition: dataserialize.h:205
Result< void > DescribeIO(const T &s, const DataSerializeInterface &dsi)
Definition: datatypefunctions.h:21
#define Describe(name, memberName, type, flags)
Definition: dataserialize.h:293
#define CONSTHASH(x)
Expression to calculate the hash code of a member name during compile time.
Definition: dataserialize.h:194
The maxon namespace contains all declarations of the MAXON API.
Definition: c4d_basedocument.h:15
#define DescribeHelperIf(name, memberName, type, flags, mask, value)
Definition: dataserialize.h:388
Definition: string.h:1225
return OK
Definition: apibase.h:2620
static const ValueType NONE
Definition: dataserialize.h:136
bool Bool
boolean type, possible values are only false/true, 8 bit
Definition: apibase.h:183
#define PrepareDescribe(streamClass, className)
Definition: dataserialize.h:267
Definition: apibaseid.h:250
#define iferr_return
Definition: resultbase.h:1465
No flags set.
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< maxon::String >
#define DiagnosticOutput(formatString,...)
Definition: debugdiagnostics.h:176
Definition: url.h:875
static const UInt FINALIZEHASH
Definition: dataserialize.h:172
#define DescribeHelper(name, memberName, type, flags)
Definition: dataserialize.h:344
maxon::Result< void >
Definition: ge_prepass.h:2
Definition: basearray.h:574
Int64 Int
signed 32/64 bit int, size depends on the platform
Definition: apibase.h:190
#define iferr_scope
Definition: resultbase.h:1374
The helper's WriteAction will get a call with INITIALIZEHASH before the class has been written.
#define DescribeEndIf()
Definition: dataserialize.h:429
#define DescribeElse()
Definition: dataserialize.h:410
The helper's ReadAction will get a call with FINALIZEHASH after the class has been read.
Result< void > WriteDocument(const Url &url, OPENSTREAMFLAGS flags, const Id &id, const T &object, IOFORMAT format=IOFORMAT::DEFAULT, const DataDictionary &dict=DataDictionary())
Definition: io.h:67
#define PrepareHelper(helperName, flags)
Definition: dataserialize.h:397
UInt64 UInt
unsigned 32/64 bit int, size depends on the platform
Definition: apibase.h:191
static const UInt INITIALIZEHASH
Definition: dataserialize.h:171
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
#define DebugAssert(condition,...)
Definition: debugdiagnostics.h:248