]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | (C) Copyright Edward Diener 2011-2015 | |
3 | Distributed under the Boost Software License, Version 1.0. | |
4 | (See accompanying file LICENSE_1_0.txt or copy at | |
5 | http://www.boost.org/LICENSE_1_0.txt). | |
6 | ] | |
7 | ||
8 | [section:vmd_identifier_subtype Identifier subtypes] | |
9 | ||
10 | Identifiers are the low-level data types which macro programmers | |
11 | use to pass preprocessing data most often. As we have seen VMD | |
12 | has a system for registering and detecting identifiers so that | |
13 | they can be parsed as part of preprocessor data. This system also | |
14 | includes comparing identifiers for equality or inequality using | |
15 | BOOST_VMD_EQUAL/BOOST_VMD_NOT_EQUAL and matching identifiers | |
16 | using identifier modifiers in BOOST_VMD_IS_IDENTIFIER and | |
17 | BOOST_VMD_ELEM. Together these facilities provide a rich set | |
18 | of functionality for handling identifiers in macros. | |
19 | ||
20 | Both numbers and v-types are subtypes of identifiers, and can | |
21 | both be individually recognized as data types of their own or | |
22 | worked with as identifiers using the identifier facilities | |
23 | already mentioned. Numbers, in particular, also have a rich set | |
24 | of functionality within the Boost PP library. As subtypes numbers | |
25 | and v-types can be used as filter modifiers and can be returned | |
26 | as specific types either when invoking BOOST_VMD_GET_TYPE | |
27 | or when using return type modifiers. Furthermore VMD recognizes | |
28 | their individual v-types, BOOST_VMD_TYPE_NUMBER and | |
29 | BOOST_VMD_TYPE_TYPE, as VMD data when parsing sequences. | |
30 | ||
31 | It is possible for the end-user to define his own identifier subtype. | |
32 | This is called a "user-defined subtype". Once a user-define subtype is | |
33 | created all the generic type facilities of VMD which subtypes such as | |
34 | a number or a v-type possess is automatically available for that | |
35 | user-defined subtype. | |
36 | ||
37 | [heading Defining a subtype] | |
38 | ||
39 | In order to define a user-defined subtype a number of steps need to be followed. | |
40 | These steps will be explained in detail further below: | |
41 | ||
42 | # Register and pre-detect all identifiers of that subtype. | |
43 | # Register and pre-detect a v-type name for that subtype. | |
44 | # Subset register all identifiers of the subtype. | |
45 | # Subset register the v-type name for the subtype. | |
46 | ||
47 | When we do the above, it is best to put all the macros in a single | |
48 | header file and always include that header file when we work generically | |
49 | with our user-defined subtype. | |
50 | ||
51 | [heading Register and pre-detect all identifiers of that subtype] | |
52 | ||
53 | Registering and pre-detecting all of the identifiers of that subtype | |
54 | is exactly the same as registering and pre-detecting any identifier. | |
55 | ||
56 | Let's create some identifiers based for use in the mythical "udef" library. | |
57 | We will put all our macros in the header file udef_vmd_macros.hpp. | |
58 | ||
59 | We will need distinct names for the identifiers in our library, so we will | |
60 | append UDEF_ to our identifier names to make them unique. Our udef library | |
61 | deals in geometrical shapes so we will create a user-defined subtype which | |
62 | consists of identifiers for the various shapes our udef library can | |
63 | manipulate in their macros. So our identifier registrations and pre-detections | |
64 | placed in our header file will be: | |
65 | ||
66 | #define BOOST_VMD_REGISTER_UDEF_CIRCLE (UDEF_CIRCLE) | |
67 | #define BOOST_VMD_REGISTER_UDEF_SQUARE (UDEF_SQUARE) | |
68 | #define BOOST_VMD_REGISTER_UDEF_TRIANGLE (UDEF_TRIANGLE) | |
69 | #define BOOST_VMD_REGISTER_UDEF_HEXAGON (UDEF_HEXAGON) | |
70 | ||
71 | #define BOOST_VMD_DETECT_UDEF_CIRCLE_UDEF_CIRCLE | |
72 | #define BOOST_VMD_DETECT_UDEF_SQUARE_UDEF_SQUARE | |
73 | #define BOOST_VMD_DETECT_UDEF_TRIANGLE_UDEF_TRIANGLE | |
74 | #define BOOST_VMD_DETECT_UDEF_HEXAGON_UDEF_HEXAGON | |
75 | ||
76 | [heading Register and pre-detect a v-type name for that subtype] | |
77 | ||
78 | We need to create a unique v-type name for our user-defined subtype. | |
79 | The name does not have to begin with BOOST_VMD_TYPE_ but it should be | |
80 | unique. Since BOOST_VMD_TYPE_ is the common beginning of all v-types | |
81 | we will use it for consistency but will append to it UDEF_SHAPES to | |
82 | give it a uniqueness which should not be duplicated: | |
83 | ||
84 | #define BOOST_VMD_REGISTER_BOOST_VMD_TYPE_UDEF_SHAPES (BOOST_VMD_TYPE_UDEF_SHAPES) | |
85 | ||
86 | #define BOOST_VMD_DETECT_BOOST_VMD_TYPE_UDEF_SHAPES_BOOST_VMD_TYPE_UDEF_SHAPES | |
87 | ||
88 | [heading Subtype register all identifiers of the subtype] | |
89 | ||
90 | The macro to register an identifier subset starts with BOOST_VMD_SUBTYPE_REGISTER_ | |
91 | and you append to it each identifier in the subset. This is very much like the | |
92 | way you use the BOOST_VMD_REGISTER_ macro. The difference is that unlike the | |
93 | BOOST_VMD_REGISTER_ macro, which expands to a tuple whose single element is the | |
94 | identifier, the BOOST_VMD_SUBTYPE_REGISTER_ expands to a tuple of two elements | |
95 | where the first element is the subtype v-type and the second element is the identifier. | |
96 | ||
97 | For our udef user-defined subtype this would be: | |
98 | ||
99 | #define BOOST_VMD_SUBTYPE_REGISTER_UDEF_CIRCLE (BOOST_VMD_TYPE_UDEF_SHAPES,UDEF_CIRCLE) | |
100 | #define BOOST_VMD_SUBTYPE_REGISTER_UDEF_SQUARE (BOOST_VMD_TYPE_UDEF_SHAPES,UDEF_SQUARE) | |
101 | #define BOOST_VMD_SUBTYPE_REGISTER_UDEF_TRIANGLE (BOOST_VMD_TYPE_UDEF_SHAPES,UDEF_TRIANGLE) | |
102 | #define BOOST_VMD_SUBTYPE_REGISTER_UDEF_HEXAGON (BOOST_VMD_TYPE_UDEF_SHAPES,UDEF_HEXAGON) | |
103 | ||
104 | [heading Subtype register the v-type name for the subtype] | |
105 | ||
106 | Doing a subset register of the actual udef v-type is fairly easy once we understand | |
107 | how to register an identifier subset. The only particular thing to realize is | |
108 | that the type of any v-type is the v-type BOOST_VMD_TYPE_TYPE. So our subset | |
109 | register of our new v-type BOOST_VMD_TYPE_UDEF_SHAPES is: | |
110 | ||
111 | #define BOOST_VMD_SUBTYPE_REGISTER_BOOST_VMD_TYPE_UDEF_SHAPES (BOOST_VMD_TYPE_TYPE,BOOST_VMD_TYPE_UDEF_SHAPES) | |
112 | ||
113 | [heading Using our identifier subset] | |
114 | ||
115 | Once we have added all of the above object-like macros for defining our user-defined | |
116 | subtype to the udef_vmd_macros.hpp header file we have a new data type which we can | |
117 | use generically just like we can use numbers or v-types generically. It is important | |
118 | to include the header udef_vmd_macros.hpp in some translation unit whenever we need | |
119 | the VMD functionality for our new data type. So in our examples we will assume that | |
120 | an '#include udef_vmd_macros.hpp' precedes each example. | |
121 | ||
122 | #include <boost/vmd/get_type.hpp> | |
123 | ||
124 | #define A_SEQUENCE UDEF_SQUARE | |
125 | #define A_SEQUENCE2 217 | |
126 | #define A_SEQUENCE3 BOOST_VMD_TYPE_UDEF_SHAPES | |
127 | #define A_SEQUENCE4 BOOST_VMD_TYPE_NUMBER | |
128 | ||
129 | BOOST_VMD_GET_TYPE(A_SEQUENCE) will return 'BOOST_VMD_TYPE_UDEF_SHAPES' | |
130 | BOOST_VMD_GET_TYPE(A_SEQUENCE2) will return 'BOOST_VMD_TYPE_NUMBER' | |
131 | BOOST_VMD_GET_TYPE(A_SEQUENCE3) will return 'BOOST_VMD_TYPE_TYPE' | |
132 | BOOST_VMD_GET_TYPE(A_SEQUENCE4) will return 'BOOST_VMD_TYPE_TYPE' | |
133 | ||
134 | Here we see that when we use our BOOST_VMD_GET_TYPE macro on a single-element | |
135 | sequence which is one of our user-defined subtype values we correctly get back | |
136 | our user-defined subtype's v-type, just like we do when we ask for the type of a number. Also | |
137 | when we use our BOOST_VMD_GET_TYPE macro on our user-defined subtype's v-type itself we correctly | |
138 | get back the type of all v-types, which is BOOST_VMD_TYPE_TYPE, just like we do | |
139 | when we ask for the type of the v-type of a number. | |
140 | ||
141 | #include <boost/vmd/elem.hpp> | |
142 | ||
143 | #define A_SEQUENCE5 (1,2) UDEF_TRIANGLE | |
144 | ||
145 | BOOST_VMD_ELEM(1,A_SEQUENCE5,BOOST_VMD_RETURN_TYPE) will return '(BOOST_VMD_TYPE_UDEF_SHAPES,UDEF_TRIANGLE)' | |
146 | BOOST_VMD_ELEM(0,A_SEQUENCE5,BOOST_VMD_RETURN_TYPE) will return '(BOOST_VMD_TYPE_TUPLE,(1,2))' | |
147 | ||
148 | Here we see that we can use the return type modifier to get back both the type | |
149 | and the value in a two-element tuple for our user-defined subtype just as we so | |
150 | for any other type. | |
151 | ||
152 | #include <boost/vmd/equal.hpp> | |
153 | ||
154 | #define A_SEQUENCE6 UDEF_CIRCLE | |
155 | #define A_SEQUENCE7 168 | |
156 | ||
157 | BOOST_VMD_EQUAL(A_SEQUENCE6,UDEF_CIRCLE,BOOST_VMD_TYPE_UDEF_SHAPES) will return '1' | |
158 | BOOST_VMD_EQUAL(A_SEQUENCE6,UDEF_CIRCLE,BOOST_VMD_TYPE_LIST) will return '0' | |
159 | BOOST_VMD_EQUAL(A_SEQUENCE7,168,BOOST_VMD_TYPE_NUMBER) will return '1' | |
160 | BOOST_VMD_EQUAL(A_SEQUENCE7,168,BOOST_VMD_TYPE_SEQ) will return '0' | |
161 | ||
162 | Here we can see that we can use the filter modifier with our user-defined subtype's v-type | |
163 | just as we can do with any other v-type, such as the number v-type. | |
164 | ||
165 | In all respects once we define our subtype and provide those definitions in | |
166 | a header file, our user-defined subtype acts like any other v-type in our system. | |
167 | Since VMD functionality is largely based on being able to recognize the type of | |
168 | data in macro input being able to define another 'type', as an identifier subtype, | |
169 | which VMD understands has value for the macro programmer. | |
170 | ||
171 | [heading Uniqueness of identifier subtype values and v-type] | |
172 | ||
173 | When we define a new identifier subtype we need to be careful that | |
174 | the values of that subtype and its actual v-type are unique identifiers | |
175 | within any translation unit. This is the main difference between just | |
176 | defining identifiers and defining an identifier subtype. | |
177 | ||
178 | Recall that when we just register and pre-detect identifiers we will have | |
179 | no problems if the same identifiers already have been registered and pre-detected | |
180 | within the same translation unit. This is because we are just redefining the | |
181 | exact same macro if this is the case. | |
182 | ||
183 | But with identifier subtypes, when we use the BOOST_VMD_SUBTYPE_REGISTER_ macro | |
184 | to associate our subtype's v-type with our subtype identifiers, we will have | |
185 | problems if someone else has also defined an identifier subtype using the same | |
186 | identifiers as we use since we will be redefining the same object-like macro name | |
187 | with a different expansion. Even if someone else has registered/pre-detected an | |
188 | identifier we are using for out subtype without defining a subtype based on that | |
189 | identifier we will be causing a problem defining our subtype because VMD macros which | |
190 | generically return the type of a sequence or sequence element will return our | |
191 | subtype as the type rather than just BOOST_VMD_TYPE_IDENTIFIER which some programmer | |
192 | might expect. | |
193 | ||
194 | The gist of this is that if we define a user-defined subtype its identifiers need | |
195 | to be unique within a given translation unit, and yet unique names make it harder | |
196 | for an end-user to use macros more naturally. In our given example with the mythical | |
197 | udef library we used identifiers such as 'UDEF_CIRCLE' etc. instead of the more natural | |
198 | sounding CIRCLE. So with user-defined identifier subtypes we have a tradeoff; we need | |
199 | unique identifier names both for our subtype identifiers and the v-type for our | |
200 | subtype identifiers so as not to conflict with others who might be using identifier | |
201 | subtypes, but those unique names might make using macros less "natural" On the other | |
202 | hand, just registering/pre-detecting identifiers has no such problem. This is an | |
203 | issue of which any user, looking to create his own data type using VMD by defining | |
204 | user-defined subtypes, should be aware. | |
205 | ||
206 | [endsect] |