Studio:Scl union

From STRIDE Wiki
Revision as of 00:13, 21 August 2009 by Timd (talk | contribs) (Text replace - 'Category: SCL' to 'Category:Studio:SCL')
Jump to navigation Jump to search

The scl_union pragma

Unions are C language constructs that have a set of members, of which at most one can be stored in the union object at any time. The stored member is called the "active" member. The scl_union pragma identifies which union member is the active member when a union is part of a payload.

Syntax

#pragma scl_union(union-name, active-index)

#pragma scl_union(union-name, discriminant-specification)

#pragma scl_union(parent-name, union-name, active-index)

#pragma scl_union(parent-name, union-name, discriminant-specification)
Parameters Type Description
parent-name String Name of structure encapsulating the union
union-name String Name of the union
active-index Integer Zero-based index for the active overlay
discriminant-specification String Field name indicating the active overlay

Notes

For each union that is part of a message or function, there must be a means to determine which member is active. There are two basic methods for identification of the active member:

  • One union member is designated as always active. The union will be treated as if this member is permanently active, there is no discriminant, nor any way to change the active member.
  • A secondary field is designated as the discriminant and its value determines which (if any) of the union members are active.

Discriminants

Unions within the source code are easily identified by the "union" keyword. As discriminants are not easy to identify, the scl_union() pragma is necessary to identify them. The scl_union_activate() pragma is optionally used to define a mapping between discriminant values and union members.

Within a discriminated union, the discriminant value determines the active member of the union, resulting in a mapping between discriminant values and union members. A number of mapping choices are supported:

  • In the simplest mapping, a discriminant value of n directly identifies the nth union member as active. In other words, a value of 0 would indicate the first union member, a value of 1 would indicate the second, etc.
  • If the discriminant is an enumerated type, it is possible to set up an association between enumeration constants and active members such that the nth enumeration constant (in declared order, not value order) maps to the nth union member.
  • It is also possible to create an explicit map between discriminant value sets and active members by specifying that a particular value or set of values maps to a specific member.

The following constraints are enforced for the mapping between discriminant values and union members:

  • A particular discriminant value may map only to a single union member.
  • A particular field may act as a discriminant for more than one union
  • A single union may only have a single discriminant field.
  • The type of the discriminant field must be an integer or enumerated type or must have been cast (using scl_cast()) to an integer or enumerated type.
  • Any scl_values() pragma applied to the discriminant field affects the default mapping between discriminant values and union members.

Default Mapping

As mentioned previously, the scl_union() pragma is used to identify the discriminant for a union and scl_union_activate() is used to map discriminant values to union members. If there are no scl_union_activate() pragmas for a particular discriminated union, then the mapping between the discriminant and members is said to be default. The default mapping depends on the type of the discriminant:

  • If the discriminant is one of the standard integer types, or has been cast to one of the standard integer types using scl_cast(), then the value of the discriminant identifies the position of the active member; i.e., a value of 0 indicates the first union member, a value of 1 the second, etc.
  • If the discriminant is an enumerated type or has been cast to an enumerated type, or has had a set of constant values prescribed using scl_values(), then each constant has both a value and a position within the list. It is the position, rather than the value, that identifies the active member. When the discriminant takes on the value of the constant from position n, the nth union member is active. In the case of two constants from the same list have the same value, but different positions, an error is recognized.

Explicit Mappings

A union that has at least one scl_union_activate() pragma applied to it is said to have an explicit mapping. When a union has an explicit mapping, there is no default mapping; rather all mapping between discriminant values and union members is prescribed by the set of scl_union_activate() pragmas for the unions. For more details on using scl_union_activate(), click here.

Internal and External Discriminants

A union’s discriminant is either internal or external. A union has an external discriminant if the discriminant field is not contained within the union.

An internal discriminant is one that is located inside the union. If a union has an internal discriminant the following must be true:

  • Every member of the union must have a field that corresponds to the internal discriminant. All such fields must be of exactly the same type, or they must have had exactly the same scl_cast() or scl_values() specifications applied. Furthermore, all such fields must be positioned in exactly the same memory location within the union. If they are located in a payload block other than the one containing the union, then the expression "path" leading to each must be the same in the sense that all corresponding pointers across all the members have exactly the same offsets.

Examples

The following examples illustrate how various discriminant values map to the active member for discriminated unions:

Example 1: Default mapping

Discriminant is an integral type. A value of n maps to the nth union member. Members are numbered beginning with zero (0).

typedef struct {
  int disc;
  union {
    int i;
    float f;
  } u;
} S1t;
 
#pragma scl_union(S1t.u, S1t.disc)

Example 2: Enum-based

Discriminant is an enumerated type. The nth enumeration constant (in declared order, not value order) maps to the nth union member.

typedef enum {
  IDX_FIRST  = 20,
  IDX_SECOND = 4
} e_t;
 
typedef struct {
  e_t disc;
  union {
    int i;
    float f;
  } u;
} S2t;
 
#pragma scl_union(S2t.u, S2t.disc)

Example 3: Explicit mapping

Discriminant values are explicitly mapped to active union members using scl_union_activate().

// In the following union:
//     discriminant == 4 means 'i' is active
//     discriminant == 5 means 'f' is active
typedef struct {
  int discriminant;
  union {
    int i;
    float f;
  } u;
} S;
 
#pragma scl_union (S.u, S.discriminant)
#pragma scl_union_activate (S.u, i, 4)
#pragma scl_union_activate(S.u, f, 5)

Example 4: Discriminant values constrained to enumeration types

An integer-based discriminant is constrained (using scl_values) to an enumerated type. This sets up an enum-based mapping as described in Example 2.

// In the following union:
//     discriminant == RED means 'i' is active
//     discriminant == GREEN means 'f' is active
//     discriminant == BLUE means 'd' is active
typedef enum {
  RED,
  GREEN,
  BLUE
} COLOR;
 
typedef struct {
  int discriminant;
  union {
    int i;
    float f;
    double d;
  } u;
} S;
 
#pragma scl_union (S.u, S.discriminant)
#pragma scl_values (S.discriminant, COLOR)

Example 5: Default undiscriminated union

The union does not have a discriminant. The first member is always assumed to be active.

//In the following union:
//   'i' is always treated as active
typedef struct {
  union {
    int i;
    float f;
  } u;
} S6t;

Example 6: Explicitly prescribed fixed active member for undiscriminated union

The union does not have a discriminant. scl_union is used to prescribe a specific member as always active.

typedef struct {
  union {
    int i;
    float f;
    char bytes[4];
  } u;
} S6t;
 
/* Select field "bytes" as the default member */
#pragma scl_union(S6t.u, 2)

Example 7: Internal discriminant

An internal discriminant is located inside the union.

// Union with internal discriminant:
//     discriminant == 0     'um0' is active member
//     discriminant == 1     'um1' is active member
//     discriminant == 2     'um2' is active member
typedef struct {
  int discriminant;
} HEADER;
 
typedef struct {
  HEADER h;
  int x;
} UNION_MEMBER_0;
 
typedef struct {
  HEADER h;
  float f;
} UNION_MEMBER_1;
 
typedef struct {
  HEADER h;
  double d;
} UNION_MEMBER_2;
 
typedef union {
  UNION_MEMBER_0   um0;
  UNION_MEMBER_1   um1;
  UNION_MEMBER_2   um2;
} U;
 
#pragma scl_union(U, U.um0.h.discriminant)

Example 8: Explicit mapping of multiple discriminant values to a single active member

typedef struct {
  int a;
  int b;
} type1_t;
    
typedef struct {
  float x;
  float y;
} type2_t;
    
typedef struct {
  char c1;
  char c2;
} type3_t;
    
typedef enum {
  ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE
} discriminant_t;
    
typedef struct {
  discriminant_t d;
  int e;
  union {
    type1_t m1;    // active when d == ONE or d == TWO or d == THREE
    type2_t m2;    // active when d == FOUR or d == SIX or d == EIGHT
    type3_t m3;    // active when d == FIVE or d == SEVEN
  } u_main;
} s_t;

#pragma scl_union (s_t.u_main, s_t.d)
#pragma scl_union_activate(s_t.u_main, m1, ONE, TWO, THREE)
#pragma scl_union_activate(s_t.u_main, m2, FOUR, SIX, EIGHT)
#pragma scl_union_activate(s_t.u_main, m3, FIVE, SEVEN)

Example 9: Unmapped discriminant values indicating no member is active

//Union unmapped discriminant values
//
//     discriminant == ZERO                  'i' is active member
//     discriminant == ONE                   'f' is active member
//     discriminant == TWO                   'd' is active member
//     discriminant == THREE or FOUR
//                     FIVE or SIX or SEVEN      no active member
typedef enum {
  ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN
} ENUM_TYPE;
    
typedef struct {
  ENUM_TYPE discriminant;
  union {
    int    i;
    float  f;
    double d;
  } u;
} S;

#pragma scl_union (S.u, S.discriminant)
#pragma scl_union_activate(S.u, i, ZERO)
#pragma scl_union_activate(S.u, f, ONE)
#pragma scl_union_activate(S.u, d, TWO)

Example 10: Discriminant values defined by macros

//One field discriminating multiple unions 
//
//     discriminant == 1                   'i' is active member
//     discriminant == 2                   'f' is active member
//     discriminant == 3                   'd' is active member
//  
#define ONE   1
#define TWO   2
#define THREE 3
    
typedef struct {
  int discriminant;
  union {
    int    i;
    float  f;
    double d;
  } u;
} S;
    
#pragma scl_union (S.u, S.discriminant)        // uses 2-parameter form of scl_union
#pragma scl_union_activate (S.u, i, ONE)       // the first parameter identifies the union, the second a member in it. 
#pragma scl_union_activate (S.u, f, TWO)
#pragma scl_union_activate (S.u, d, THREE)

See Also

  • For additional information on the scl_union pragma, including constraints, refer to the section on scl_union in the SCL Reference Guide.