The ASN.1 CHOICE type is converted into a C or C++ structured type containing an
integer for the choice tag value (t
) followed by a union (u
)
of all of the equivalent types that make up the CHOICE elements.
The tag value is simply a sequential number starting at one for each alternative in
the CHOICE. A #define
constant is generated for each of these values. The
format of this constant is T_<name>_<element-name>
where
<name>
is the name of the ASN.1 production and
<element-name>
is the name of the CHOICE alternative. If a CHOICE
alternative is not given an explicit name, then <element-name>
is
automatically generated by taking the type name and making the first letter lowercase
(this is the same as was done for the ASN.1 SEQUENCE type with unnamed elements). If the
generated name is not unique, a sequential number is appended to make it unique.
The union of choice alternatives is made of the equivalent C or C++ type definition followed by the element name for each of the elements. The rules for element generation are essentially the same as was described for SEQUENCE above. Constructed types or elements that map to C structured types are pulled out and temporary types are created. Unnamed elements names are automatically generated from the type name by making the first character of the name lowercase.
One difference between temporary types used in a SEQUENCE and in a CHOICE is that a pointer variable will be generated for use within the CHOICE union construct.
ASN.1 production:
<name> ::= CHOICE { <element1-name> <element1-type>, <element2-name> <element2-type>, ... }
Generated C code:
#define T_<name>_<element1-name> 1 #define T_<name>_<element2-name> 2 ... typedef struct { int t; union { <type1> <element1-name>; <type2> <element2-name>; ... } u; } <name>;
- or -
typedef struct { ... } <tempName1>; typedef struct { ... } <tempName2>; typedef struct { int t; union { <tempName1>* <element1-name>; <tempName2>* <element2-name>; ... } u; } <name>;
If the -static command line option or
<storage> static </storage>
configuration variable is set for the
given production, then pointers will not be used for the variable declarations.
This is true for the C case only; for C++, pointers must be used due to the fact that the generated code will not compile if constructors are used in a non-pointer variable within a union construct.
The C++ mapping is the same with the exception that the ASN1T_
prefix is
added to the generated type name.
<type1>
and <type2>
are the equivalent C types
representing the ASN.1 types <element1-type>
and
<element2-type>
respectively. <tempName1>
and
<tempName2>
represent the names of temporary types that may have been
generated as the result of using nested constructed types within the definition.
Choice alternatives may be unnamed, in which case <element-name>
is
derived from <element-type>
by making the first letter lowercase. One
needs to be careful when nesting CHOICE structures at different levels within other
nested ASN.1 structures (SEQUENCEs, SETs, or other CHOICEs). A problem arises when
CHOICE element names at different levels are not unique (this is likely when elements
are unnamed). The problem is that generated tag constants are not guaranteed to be
unique since only the production and end element names are used.
The compiler gets around this problem by checking for duplicates. If the generated name is not unique, a sequential number is appended to make it unique. The compiler outputs an informational message when it does this.
An example of this can be found in the following production:
C ::= CHOICE { [0] INTEGER, [1] CHOICE { [0] INTEGER, [1] BOOLEAN } }
This will produce the following C code:
#define T_C_aInt 1 #define T_C_aChoice 2 #define T_C_aInt_1 1 #define T_C_aBool 2 typedef struct { int t; union { OSINT32 aInt; struct { int t; union { OSINT32 aInt; OSBOOL aBool; } u; } aChoice; } C;
Note that _1
was appended to the second instance of
T_C_aInt
. Developers must take care to ensure they are using the correct
tag constant value when this happens.
Populating generated CHOICE structures is more complex then for other generated types due to the use of pointers within the union construct. As previously mentioned, the use of pointers with C can be prevented by using the -static command line option. If this is done, the elements within the union construct will be standard inline variable declarations and can be populated directly. Otherwise, the methods listed below can be used to populate the variables.
The recommended way to populate the pointer elements is to declare variables of the embedded type to be used on the stack prior to populating the CHOICE structure. The embedded variable would then be populated with the data to be encoded and then the address of this variable would be plugged into the CHOICE union pointer field.
Consider the following definitions:
AsciiString ::= [PRIVATE 28] OCTET STRING EBCDICString ::= [PRIVATE 29] OCTET STRING String ::= CHOICE { AsciiString, EBCDICString }
This would result in the following type definitions:
typedef OSDynOctStr AsciiString; typedef OSDynOctStr EBCDICString; typedef struct String { int t; union { /* t = 1 */ AsciiString *asciiString; /* t = 2 */ EBCDICString *eBCDICString; } u; } String;
To set the AsciiString choice value, one would first declare an AsciiString variable, populate it, and then plug the address into a variable of type String structure as follows:
AsciiString asciiString; String string; asciiString = "Hello!"; string.t = T_String_AsciiString; string.u.asciiString = &asciiString;
It is also possible to allocate dynamic memory for the CHOICE union option variable; but one must be careful to release this memory when done with the structure. If the built in memory-management functions/macros are used (rtxMem), all memory used for the variables is automatically released when rtxMemFree is called.