Writing a type descriptor

A type descriptor is meta-data about a C++ type. DevOps Model RealTime uses the type descriptor to know how to initialize, copy, move, destroy, encode or decode an instance of the type. Type descriptors for all primitive C++ types are included in the TargetRTS, and based on them Model RealTime is able to automatically generate type descriptors for many types that are defined in the model. A generated type descriptor shows up in generated code as a global variable typed by RTObject_class and with the name RTType_<TypeName>.

However, there are two cases when you have to write a type descriptor yourself:

  1. If a type defined in the model is too complex for the model compiler to be able to automatically generate a type descriptor for it. In this case you will see a warning that looks like this:
    WARNING : HelloWorld::MyType::a : Unable to find a descriptor for the type of this property, deselect 'Generate Descriptor' or specify a valid descriptor in the 'Type Descriptor' property.
  2. If you want to use an existing C++ type, defined outside of Model RealTime, in a way that requires the presence of a type descriptor.

If you will not use the C++ type in a way that requires a type descriptor you don't need to create one. That is why the message above only is a warning and not an error. An example of when a type descriptor is necessary is if you will define a protocol event with a data parameter typed by the C++ type. When you send a message for that event, the TargetRTS has to copy (and later destroy) the message data, and it will use the type descriptor for doing that.

Let's look at a practical example of writing a type descriptor for a common C++ library type. Assume you want to use std::string in your model. This is a library type that does not have a predefined type descriptor in the TargetRTS. So you have to write it yourself.

Init Function Body

// target is already allocated memory, so use placement new and invoke the default constructor
target = new (target) std::string();

Copy Function Body

// target is already allocated memory, so use placement new and invoke the copy constructor
target = new(target) std::string(*source);

Destroy Function Body

// The target object is deleted by the TargetRTS and we only need to ensure its destructor gets invoked
target->~StdString();

Encode Function Body

// We can use the functions of RTEncoding to get a string enclosed in double quotes. Let's use that as our encoding.
return coding->put_string(source->c_str());

Decode Function Body

// We can use the functions of RTDecoding to parse the string representation into an std::string object.
char * stringValue;
coding->get_string(stringValue);
target->assign(stringValue);
return 1;

Note that all these 5 functions are mandatory to implement for the model compiler to generate your custom type descriptor. There is also a Move Function Body which you optionally can implement. Doing so will make it possible for the TargetRTS to move, instead of copy, an object of the type when it is sent as message data. For more information about this possibility read the chapter "Avoiding to Copy Message Data" in RT Services Library. A typical implementation of the move function is to simply invoke the move constructor of the type:

Move Function Body

target = new(target) std::string(std::move(*source));