Considerations When Using C++ Standard Library
Prev Chapter 6. Generated C/C++ Source Code Next

Considerations When Using C++ Standard Library

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.

    1. It can have an associated OSCTXT object, which is set or unset using the setContext method.

    2. 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:

    1. 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.

    2. 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:

    1. Explicitly invoke the destructor for the object. This will recursively destruct objects contained by the object.

    2. 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);

Prev Up Next
Code Generation for Partial Decoding Home Generated Build Files

AltStyle によって変換されたページ (->オリジナル) /