When -cpp11 is specified on the command line, the generated code may use features of the C++ Standard Library such as std::string for character strings and std::list (or another container class) for SEQUENCE OF types. There are a few considerations to keep in mind when using this option.
ASN1C generates code that manages memory using OSCTXT
and rtxMem*
functions. The design of the generated code and memory management is such that the C++ constructors and destructors generally don't need to be invoked. For example, dynamic memory is allocated using rtxMemAllocType
, instead of new
, and initialization can be done by assigning individual fields or else using a generated asn1Init*
function, so that the constructor is never invoked.
The C++ Standard Library classes are more typical C++ classes, and failing to invoke their constructor or destructor, or invoking them more than once, can lead to memory leaks or crashes. This means that when you are using the -cpp11 option, you must take care that the C++ constructors and destructors for the generated classes are invoked exactly once.
Before listing the rules to be followed, a few bits of information will be helpful:
A PDU type class (one derived from ASN1TPDU) has two special features, neither of which are true for non-PDU type classes.
It can have an associated OSCTXT
object, which is set or unset using the
setContext
method.
Its destructor will invoke the corresponding
asn1Free*
function, with or without a context,
depending on whether the object has an associated
OSCTXT
.
The asn1Free*
function can be called
with or without an OSCTXT
. When called without a
context, it will invoke destructors for any dynamically-allocated
objects, but will not free the memory for those objects.
When called with a context, it will both destruct and free such
objetcs. It is safe to to invoke the asn1Free* function multiple
times with a context, but if invoked without a context, it must
not be invoked again.
The purpose for calling the asn1Free*
function without a context is so that any C++ Standard Library
destructors that must be invoked can be invoked while still
allowing memory that is managed by the OSys memory manager to be
freed in bulk, such as by calling rtxMemReset
.
A control class (one derived from ASN1CType) is generated for PDU types. A control class has two notable features:
It has a MemFree
function which
can be used to call the PDU's asn1Free*
function with the control class's context. This is useful
when encoding and that same context was used while
populating the data structures to allocate memory.
It has a DecodeFrom
function
which automatically sets the associated context on the PDU
object.
Follow these rules to avoid problems:
When you dynamically allocate an object, use
rtxMemAlloc*
to allocate the memory, then use a
placement new expression to invoke the constructor.
For any object that is not owned by some other object,
you must call the corresponding asn1Free*
function,
if there is one. This can be done explicitly or, if the type is a
PDU-type, it can be done implicitly for you by the destructor.
If you dynamically allocate an object that is
not owned by some other object, there are
two additional steps to be done, in order, after calling the
asn1Free*
function:
Explicitly invoke the destructor for the object. This will recursively destruct objects contained by the object.
Use rtxMemFreePtr
to release the
memory, unless you will free the memory in bulk
later.
When encoding a PDU type object and you want to free memory immediately afterward, you need to make sure asn1Free is called with a context. You can do this in the following ways:
Explicitly call asn1Free*
with a
context.
For a PDU type, set the context on the object
before asn1Free*
is implicitly called by a
destructor.
Use the control class MemFree
function.
When encoding a PDU type object and you want to free
memory in bulk later, you need to make sure asn1Free is called
without a context and is never called with a context. If you are
calling asn1Free*
expliclitly, don't pass a context;
if implicitly via the destructor, be sure to not set a context
on the PDU object. Do not use the control class
MemFree
function.
When decoding a PDU type object and you want to free
memory in bulk later, the situation is the same as above, for
encoding, except that you will need to clear the context on the
PDU object before asn1Free*
is implicitly called by
the destructor.
If you repeatedly use the same object to encode records, use the asn1Init* method with free=TRUE to free previously allocated data before repopulating the object. If you repeatedly decode into the same object, invoke the Decode method with free=TRUE.
Even if you are using ASN1CType.memReset(), you must still implicitly or explicitly call the asn1Free* methods or else memory allocated by the C++ standard library will not be freed. If you use ASN1CType.memReset(), you must ensure that asn1Free* will not be implicitly or explicitly called afterward; a segmentation fault would likely result when dangling pointers are followed.
A few code examples are given below.
EXAMPLE 1, Using a local variable:
//A local variable's constructor & destructor fire automatically. You only need to //make sure asn1Free* is invoked. //This example assume ASN1T_PDU is a PDU type (extends ASN1TPDU). ASN1T_PDU msgData; ASN1C_PDU controlPDU (encodeBuffer, msgData); // Populate structure of generated type ... // Encode ... // When msgData goes out of scope, its destructor will fire, which will invoke // asn1Free_PDU, but it won't have a context. If this were not a PDU type, // the destructor would not do this and you would have to invoke asn1Free_PDU // yourself. // If I want memory to be freed now, I need asn1Free_PDU to be invoked with // a context, which I can do as follows: controlPDU.MemFree();
EXAMPLE 2, Assigning a dynamically allocated std::string for a choice type:
OSCTXT* pctxt; ... //set the selector indicating which alternative is chosen pvalue->myChoice.t = 1; //allocate memory for the chosen alternative pvalue->myChoice.u.message = rtxMemAllocTypeZ (pctxt, std::string); //invoke the constructor using placement new expression new (pvalue->myChoice.u.message) std::string(); //Assign the contents of the string. *pvalue->myChoice.u.message = "Happy Birthday!";
EXAMPLE 3, Dynamically allocating and freeing an object:
OSCTXT* pctxt; ... //dynamically allocate and construct the object ASN1T_StringsInSequence* pvalue = rtxMemAllocType (pctxt, ASN1T_StringsInSequence); if (pvalue == NULL) return LOG_RTERR (pctxt, RTERR_NOMEM); new (pvalue) ASN1T_StringsInSequence(); //do some work, maybe decode into pvalue ... //invoke asn1Free*, destruct, and free memory asn1Free_StringsInSequence (pctxt, pvalue); pvalue->~ASN1T_StringsInSequence(); rtxMemFreePtr (pctxt, (void*)pvalue);