]>
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_assert Asserting and data types] | |
9 | ||
10 | The VMD macros for identifying data types work best when the macro logic can take different | |
11 | paths depending on the type of data being passed for a macro parameter. But occasionally | |
12 | the preprocessor metaprogrammer wants to simply verify that the macro parameter data is of | |
13 | the correct data type, else a preprocessing error should be generated to notify the programmer | |
14 | invoking the macro that the data passed is the incorrect type. | |
15 | ||
16 | [heading Using BOOST_VMD_ASSERT] | |
17 | ||
18 | The Boost PP library has a macro which produces a preprocessing error when the condition | |
19 | passed to it is 0. This macro is called BOOST_PP_ASSERT. The macro produces a preprocessor | |
20 | error by forcing a call to an internal macro with the wrong number of arguments. According | |
21 | to the C++ standard this should always cause an immediate preprocessing error for conforming | |
22 | compilers. | |
23 | ||
24 | Unfortunately VC++ will only produce a warning when the wrong number of arguments are passed | |
25 | to a macro. Therefore the BOOST_PP_ASSERT macro does not produce a preprocessing error using | |
26 | VC++. Amazingly enough there appears to be no other way in which VC++ can be forced to | |
27 | issue a preprocessing error by invoking a macro ( if you find one please tell me about it ). | |
28 | However one can create invalid C++ as the output from a macro invocation which causes VC++ | |
29 | to produce a compiler error when the VC++ compiler later encounters the construct. | |
30 | ||
31 | This is what the macro BOOST_VMD_ASSERT does. It takes the same conditional argument as | |
32 | BOOST_PP_ASSERT and it calls BOOST_PP_ASSERT when not used with VC++, otherwise if the | |
33 | condition is 0 it generates a compiler error by generating invalid C++ when used with VC++. | |
34 | The compiler error is generated by producing invalid C++ whose form is: | |
35 | ||
36 | typedef char BOOST_VMD_ASSERT_ERROR[-1]; | |
37 | ||
38 | By passing a second optional argument, whose form is a preprocessing identifier, | |
39 | to BOOST_VMD_ASSERT you can generate the invalid C++ for VC++, if the first | |
40 | argument is 0, of the form: | |
41 | ||
42 | typedef char optional_argument[-1]; | |
43 | ||
44 | instead. This may give a little more clarity, if desired, to the C++ error generated. | |
45 | ||
46 | If the first conditional argument is not 0, BOOST_VMD_ASSERT produces no output. | |
47 | ||
48 | [heading BOOST_VMD_ASSERT Usage] | |
49 | ||
50 | To use the BOOST_VMD_ASSERT macro either include the general header: | |
51 | ||
52 | #include <boost/vmd/vmd.hpp> | |
53 | ||
54 | or include the specific header: | |
55 | ||
56 | #include <boost/vmd/assert.hpp> | |
57 | ||
58 | [heading Assertions for data types ] | |
59 | ||
60 | The data types have their own assertion macros. These are largely just shortcuts for | |
61 | passing the result of the identifying macros to BOOST_VMD_ASSERT. These assertion | |
62 | macros are: | |
63 | ||
64 | * emptiness, BOOST_VMD_ASSERT_IS_EMPTY | |
65 | * identifier, BOOST_VMD_ASSERT_IS_IDENTIFIER | |
66 | * number, BOOST_VMD_ASSERT_IS_NUMBER | |
67 | * array, BOOST_VMD_ASSERT_IS_ARRAY | |
68 | * list, BOOST_VMD_ASSERT_IS_LIST | |
69 | * seq, BOOST_VMD_ASSERT_IS_SEQ | |
70 | * tuple, BOOST_VMD_ASSERT_IS_TUPLE | |
71 | * type, BOOST_VMD_ASSERT_IS_TYPE | |
72 | ||
73 | Each of these macros take as parameters the exact same argument as their | |
74 | corresponding identifying macros. But instead of returning non-zero or 0, each of these | |
75 | macros produce a compiler error if the type of the input is not correct. | |
76 | ||
77 | Each of these macros only check for its assertion when the macro BOOST_VMD_ASSERT_DATA | |
78 | is set to 1. By default BOOST_VMD_ASSERT_DATA is only set to 1 in compiler debug mode. | |
79 | The programmer can manually set BOOST_VMD_ASSERT_DATA to 1 prior to using one | |
80 | the data types assert macros if he wishes. | |
81 | ||
82 | [heading BOOST_VMD_ASSERT_... Usage] | |
83 | ||
84 | To use the individual BOOST_VMD_ASSERT_... macros either include the general header: | |
85 | ||
86 | #include <boost/vmd/vmd.hpp> | |
87 | ||
88 | or include the specific header: | |
89 | ||
90 | #include <boost/vmd/assert_is_empty.hpp> // BOOST_VMD_ASSERT_IS_EMPTY | |
91 | #include <boost/vmd/assert_is_identifier.hpp> // BOOST_VMD_ASSERT_IS_IDENTIFIER | |
92 | #include <boost/vmd/assert_is_number.hpp> // BOOST_VMD_ASSERT_IS_NUMBER | |
93 | #include <boost/vmd/assert_is_array.hpp> // BOOST_VMD_ASSERT_IS_ARRAY | |
94 | #include <boost/vmd/assert_is_list.hpp> // BOOST_VMD_ASSERT_IS_LIST | |
95 | #include <boost/vmd/assert_is_seq.hpp> // BOOST_VMD_ASSERT_IS_SEQ | |
96 | #include <boost/vmd/assert_is_tuple.hpp> // BOOST_VMD_ASSERT_IS_TUPLE | |
97 | #include <boost/vmd/assert_is_type.hpp> // BOOST_VMD_ASSERT_IS_TYPE | |
98 | ||
99 | [heading Assertions and VC++ ] | |
100 | ||
101 | The VC++ compiler has a quirk when dealing with BOOST_VMD_ASSERT and the | |
102 | data type assert macros. If you invoke one of the assert macros within another | |
103 | macro which would normally generate output preprocessor tokens, it is necessary when using | |
104 | VC++ to concatenate the result of the assert macro to whatever other preprocessor data | |
105 | is being generated, even if the assert macro does not generate an error. | |
106 | ||
107 | As a simple example let us suppose we have a macro expecting a tuple and generating 1 | |
108 | if the tuple has more than 2 elements, otherwise it generates 0. Ordinarily we could | |
109 | write: | |
110 | ||
111 | #include <boost/preprocessor/comparison/greater.hpp> | |
112 | #include <boost/preprocessor/control/iif.hpp> | |
113 | #include <boost/preprocessor/tuple/size.hpp> | |
114 | #include <boost/vmd/assert_is_tuple.hpp> | |
115 | ||
116 | #define AMACRO(atuple) \ | |
117 | BOOST_VMD_ASSERT_IS_TUPLE(atuple) \ | |
118 | BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(atuple), 2),1,0) | |
119 | ||
120 | but for VC++ we must write | |
121 | ||
122 | #include <boost/preprocessor/cat.hpp> | |
123 | #include <boost/preprocessor/comparison/greater.hpp> | |
124 | #include <boost/preprocessor/control/iif.hpp> | |
125 | #include <boost/preprocessor/tuple/size.hpp> | |
126 | #include <boost/vmd/assert_is_tuple.hpp> | |
127 | ||
128 | #define AMACRO(atuple) \ | |
129 | BOOST_PP_CAT \ | |
130 | ( \ | |
131 | BOOST_VMD_ASSERT_IS_TUPLE(atuple), \ | |
132 | BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(atuple), 2),1,0) \ | |
133 | ) | |
134 | ||
135 | VC++ does not work correctly in the first instance, erroneously getting confused as far as | |
136 | compiler output is concerned. But by using BOOST_PP_CAT in the second condition VC++ will | |
137 | work correctly with VMD assertions. | |
138 | ||
139 | [endsect] |