vmd_identifier_subtyping.qbk 10 KB

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