Open Search
    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.
    {
    public:
    // 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");
    [files_describeio_simple_datatype]
    Definition: describeio.h:10
    maxon::Int _a
    Definition: describeio.h:17
    maxon::Int _c
    Definition: describeio.h:19
    maxon::Int _b
    Definition: describeio.h:18
    static maxon::Result< void > DescribeIO(const maxon::DataSerializeInterface &stream)
    Definition: dataserialize.h:207
    PyObject * stream
    Definition: codecs.h:186
    Int64 Int
    signed 32/64 bit int, size depends on the platform
    Definition: apibase.h:188
    #define MAXON_DATATYPE(type, id)
    Definition: datatype.h:295

    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.
    {
    // each member variable is stored in the file
    return maxon::OK;
    }
    static constexpr ValueType NONE
    Default.
    Definition: dataserialize.h:136
    #define Describe(name, memberName, type, flags)
    Definition: dataserialize.h:294
    #define PrepareDescribe(streamClass, className)
    Definition: dataserialize.h:268
    return OK
    Definition: apibase.h:2667
    #define iferr_scope
    Definition: resultbase.h:1374
    #define iferr_return
    Definition: resultbase.h:1465

    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);
    Definition: apibaseid.h:253
    Definition: url.h:942
    #define DiagnosticOutput(formatString,...)
    Definition: debugdiagnostics.h:176
    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
    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
    @ NONE
    No flags set.

    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()
    {
    // 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();
    // 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
    };
    {
    // 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;
    }
    Py_ssize_t count
    Definition: abstract.h:640
    [files_describeio_simple_datatype]
    Definition: describeio.h:26
    maxon::Int _a
    Definition: describeio.h:32
    maxon::Int _c
    Definition: describeio.h:34
    maxon::Int _b
    Definition: describeio.h:33
    static maxon::Result< void > DescribeIO(const maxon::DataSerializeInterface &stream)
    MAXON_ATTRIBUTE_FORCE_INLINE Int GetCount() const
    Definition: basearray.h:573
    Definition: string.h:1235
    #define PrepareHelper(helperName, flags)
    Definition: dataserialize.h:398
    #define DescribeHelper(name, memberName, type, flags)
    Definition: dataserialize.h:345
    UInt64 UInt
    unsigned 32/64 bit int, size depends on the platform
    Definition: apibase.h:189
    #define DebugAssert(condition,...)
    Definition: debugdiagnostics.h:248
    @ FINALIZE_READ
    The helper's ReadAction will get a call with FINALIZEHASH after the class has been read.
    @ INITIALIZE_WRITE
    The helper's WriteAction will get a call with INITIALIZEHASH before the class has been written.
    static const UInt INITIALIZEHASH
    Definition: dataserialize.h:172
    static const UInt FINALIZEHASH
    Definition: dataserialize.h:173

    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()
    {
    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
    };
    {
    // 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;
    }
    Definition: describeio.h:41
    maxon::Int _a
    Definition: describeio.h:47
    maxon::Int _c
    Definition: describeio.h:49
    maxon::Int _b
    Definition: describeio.h:48
    static maxon::Result< void > DescribeIO(const maxon::DataSerializeInterface &stream)
    static constexpr 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
    static constexpr 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
    #define DescribeHelperIf(name, memberName, type, flags, mask, value)
    Definition: dataserialize.h:389
    #define DescribeEndIf()
    Definition: dataserialize.h:430
    #define DescribeElse()
    Definition: dataserialize.h:411
    #define CONSTHASH(x)
    Expression to calculate the hash code of a member name during compile time.
    Definition: dataserialize.h:195
    bool Bool
    boolean type, possible values are only false/true, 8 bit
    Definition: apibase.h:181
    The maxon namespace contains all declarations of the MAXON API.
    Definition: autoweight.h:14

    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