9. Selector tokens
The use of selector tokens is the primary method of adding member selectors to compound type tokens. (The only other method is to define the compound type token to be a particular structure or union type.) The introduction of new selector tokens can occur at any point in a program and they can thus be used to add new member selectors to existing compound types.
The syntax for introducing member selector tokens as follows:
selector-token: MEMBER selector-type-name : type-name : selector-type-name: type-nametype-name % constant-expression
The selector-type-name specifies the type of the object selected by the selector token. If the selector-type-name is a plain type-name, the member selector token has that type. If the selector-type-name consists of a type-name and a constant-expression separated by a %
sign, the member selector token refers to a bitfield of type type-name and width constant-expression. The second type-name gives the compound type to which the member selector belongs. For example:
#pragma token STRUCT TAG s_t# #pragma token MEMBER char*: struct s_t:s_t_mem#
introduces a compound token type, s_t
, which has a member selector, s_t_mem
, which selects an object of type char *
.
Internal identifiers of member selector tokens can only reside in the member name space of the compound type to which they belong. Clearly this is also the default name space for such identifiers.
When structure or union types are declared, according to the ISO C standard there is an implied ordering on the member selectors. In particular this means that:
-
during initialisation with an initialiser-list the identified members of a structure are initialised in the order in which they were declared. The first identified member of a union is initialised;
-
the addresses of structure members will increase in the order in which they were declared.
The member selectors introduced as selector tokens are not related to any other member selectors until they are defined. There is thus no ordering on the undefined tokenised member selectors of a compound type. If a compound type has only undefined token selectors, it cannot be initialised with an initialiser-list. There will be an ordering on the defined members of a compound type and in this case, the compound type can be initialised automatically.
The decision to allow unordered member selectors has been taken deliberately in order to separate the decision of which members belong to a structure from that of where such member components lie within the structure. This makes it possible to represent extensions to APIs which require extra member selectors to be added to existing compound types.
As an example of the use of token member selectors, consider the structure lconv
specified in the ISO C Standard library (section 7.4.3.1). The standard does not specify all the members of struct lconv
or the order in which they appear. This type cannot be represented naturally by existing C types, but can be described by the token syntax.
There are two methods for defining selector tokens, one explicit and one implicit. As selector token identifiers do not reside in the macro name space they cannot be defined using #define statements.
Suppose A
is an undefined compound token type and mem
is an undefined selector token for A
. If A
is later defined to be the compound type B
and B
has a member selector with identifier mem
then A.mem
is defined to be B.mem
providing the type of A.mem
can be resolved to the type of B.mem
. This is known as implicit selector token definition.
In the program shown below the redefinition of the compound type s_t
causes the token for the selector mem_x
to be implicitly defined to be the second member of struct s_tag
. The consequential type resolution leads to the token type t_t
being defined to be int.
#pragma token TYPE t_t# #pragma token STRUCT s_t# #pragma token MEMBER t_t : s_t : mem_x# #pragma token MEMBER t_t : s_t : mem_y# struct s_tag { int a, mem_x, b; } typedef struct s_tag s_t;
Explicit selector token definition takes place using the pragma:
#pragma DEFINE MEMBER type-nameidentifier : member-designator member-designator: identifier identifier . member-designator
The type-name specifies the compound type to which the selector belongs.
The identifier provides the identification of the member selector within that compound type.
The member-designator provides the definition of the selector token. It must identify a selector of a compound type.
If the member-designator is an identifier, then the identifier must be a member of the compound type specified by the type-name. If the member-designator is an identifier, id say, followed by a further member-designator, M say, then:
-
the identifier id must be a member identifying a selector of the compound type specified by type-name;
-
the type of the selector identified by id must have compound type, C say;
-
the
member-designator
M must identify a member selector of the compound type C.
As with implicit selector token definitions, the type of the selector token must be resolved to the type of the selector identified by the member-designator.
In the example shown below, the selector token mem is defined to be the second member of struct s which in turn is the second member of struct s_t.
#pragma token STRUCT s_t# #pragma token MEMBER int : s_t : mem# typedef struct {int x; struct {char y; int z;} s; } s_t; #pragma DEFINE MEMBER s_t : mem s.z