]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | [/ Copyright (C) 2009-2012 Lorenzo Caminiti ] | |
3 | [/ Distributed under the Boost Software License, Version 1.0 ] | |
4 | [/ (see accompanying file LICENSE_1_0.txt or a copy at ] | |
5 | [/ http://www.boost.org/LICENSE_1_0.txt) ] | |
6 | [/ Home at http://www.boost.org/libs/utility/identity_type ] | |
7 | ||
8 | [library Boost.Utility/IdentityType | |
9 | [quickbook 1.5] | |
10 | [version 1.0.0] | |
11 | [copyright 2009-2012 Lorenzo Caminiti] | |
12 | [purpose wraps types with round parenthesis] | |
13 | [license | |
14 | Distributed under the Boost Software License, Version 1.0 | |
15 | (see accompanying file LICENSE_1_0.txt or a copy at | |
16 | [@http://www.boost.org/LICENSE_1_0.txt]) | |
17 | ] | |
18 | [authors [Caminiti <email>lorcaminiti@gmail.com</email>, Lorenzo]] | |
19 | [category Utilities] | |
20 | ] | |
21 | ||
22 | This library allows to wrap types within round parenthesis so they can always be passed as macro parameters. | |
23 | ||
24 | [import ../test/var_error.cpp] | |
25 | [import ../test/var.cpp] | |
26 | [import ../test/template.cpp] | |
27 | [import ../test/abstract.cpp] | |
28 | [import ../test/paren.cpp] | |
29 | ||
30 | [section Motivation] | |
31 | ||
32 | Consider the following macro which declares a variable named `var`[^['n]] with the specified [^['type]] (see also [@../../test/var_error.cpp =var_error.cpp=]): | |
33 | ||
34 | [var_error] | |
35 | ||
36 | The first macro invocation works correctly declaring a variable named `var1` of type `int`. | |
37 | However, the second macro invocation fails generating a preprocessor error similar to the following: | |
38 | ||
39 | [pre | |
40 | error: macro "VAR" passed 3 arguments, but takes just 2 | |
41 | ] | |
42 | ||
43 | That is because the `std::map` type passed as the first macro parameter contains a comma `,` not wrapped by round parenthesis `()`. | |
44 | The preprocessor interprets that unwrapped comma as a separation between macro parameters concluding that a total of three (and not two) parameters are passed to the macro in the following order: | |
45 | ||
46 | # `std::map<int` | |
47 | # `char>` | |
48 | # `2` | |
49 | ||
50 | Note that, differently from the compiler, the preprocessor only recognizes round parenthesis `()`. | |
51 | Angular `<>` and squared `[]` parenthesis are not recognized by the preprocessor when parsing macro parameters. | |
52 | ||
53 | [endsect] | |
54 | ||
55 | [section Solution] | |
56 | ||
57 | In some cases, it might be possible to workaround this issue by avoiding to pass the type expression to the macro all together. | |
58 | For example, in the case above a `typedef` could have been used to specify the type expression with the commas outside the macro (see also [@../../test/var.cpp =var.cpp=]): | |
59 | ||
60 | [var_typedef] | |
61 | ||
62 | When this is neither possible nor desired (e.g., see the function template `f` in the section below), this library header [headerref boost/utility/identity_type.hpp] defines a macro [macroref BOOST_IDENTITY_TYPE] which can be used to workaround the issue while keeping the type expression as one of the macro parameters (see also [@../../test/var.cpp =var.cpp=]). | |
63 | ||
64 | [var_ok] | |
65 | ||
66 | The [macroref BOOST_IDENTITY_TYPE] macro expands to an expression that evaluates (at compile-time) to the specified type. | |
67 | The specified type is never split into multiple macro parameters because it is always wrapped by a set of extra round parenthesis `()`. | |
68 | In fact, a total of two sets of round parenthesis must be used: The parenthesis to invoke the macro `BOOST_IDENTITY_TYPE(...)` plus the inner parenthesis to wrap the type passed to the macro `BOOST_IDENTITY_TYPE((...))`. | |
69 | ||
70 | This macro works on any [@http://www.open-std.org/JTC1/SC22/WG21/docs/standards C++03] compiler (and it does not use [@http://en.wikipedia.org/wiki/Variadic_macro variadic macros]). | |
71 | [footnote | |
72 | Using variadic macros, it would be possible to require a single set of extra parenthesis `BOOST_IDENTITY_TYPE(`[^['type]]`)` instead of two `BOOST_IDENTITY_TYPE((`[^['type]]`))` but variadic macros are not part of C++03 (even if nowadays they are supported by most modern compilers and they are also part of C++11). | |
73 | ] | |
74 | The authors originally developed and tested this library using GNU Compiler Collection (GCC) C++ 4.5.3 (with and without C++11 features enabled `-std=c++0x`) on Cygwin and Miscrosoft Visual C++ (MSVC) 8.0 on Windows 7. | |
75 | See the library [@http://www.boost.org/development/tests/release/developer/utility-identity_type.html regressions test results] for more information on supported compilers and platforms. | |
76 | ||
77 | [endsect] | |
78 | ||
79 | [section Templates] | |
80 | ||
81 | This macro must be prefixed by `typename` when used within templates. | |
82 | For example, let's program a macro that declares a function parameter named `arg`[^['n]] with the specified [^['type]] (see also [@../../test/template.cpp =template.cpp=]): | |
83 | ||
84 | [template_f_decl] | |
85 | [template_f_call] | |
86 | ||
87 | However, note that the template parameter `char` must be manually specified when invoking the function as in `f<char>(a)`. | |
88 | In fact, when the [macroref BOOST_IDENTITY_TYPE] macro is used to wrap a function template parameter, the template parameter can no longer be automatically deduced by the compiler form the function call as `f(a)` would have done. | |
89 | [footnote | |
90 | This is because the implementation of [macroref BOOST_IDENTITY_TYPE] wraps the specified type within a meta-function. | |
91 | ] | |
92 | (This limitation does not apply to class templates because class template parameters must always be explicitly specified.) | |
93 | In other words, without using the [macroref BOOST_IDENTITY_TYPE] macro, C++ would normally be able to automatically deduce the function template parameter as shown below: | |
94 | ||
95 | [template_g_decl] | |
96 | [template_g_call] | |
97 | ||
98 | [endsect] | |
99 | ||
100 | [section Abstract Types] | |
101 | ||
102 | On some compilers (e.g., GCC), using this macro on abstract types (i.e., classes with one or more pure virtual functions) generates a compiler error. | |
103 | This can be avoided by manipulating the type adding and removing a reference to it. | |
104 | ||
105 | Let's program a macro that performs a static assertion on a [@http://en.wikipedia.org/wiki/Template_metaprogramming Template Meta-Programming] (TMP) meta-function (similarly to Boost.MPL [@http://www.boost.org/doc/libs/1_36_0/libs/mpl/doc/refmanual/assert.html `BOOST_MPL_ASSERT`]). | |
106 | The [macroref BOOST_IDENTITY_TYPE] macro can be used to pass a meta-function with multiple template parameters to the assert macro (so to handle the commas separating the template parameters). | |
107 | In this case, if the meta-function is an abstract type, it needs to be manipulated adding and removing a reference to it (see also [@../../test/abstract.cpp =abstract.cpp=]): | |
108 | ||
109 | [abstract] | |
110 | ||
111 | [endsect] | |
112 | ||
113 | [section Annex: Usage] | |
114 | ||
115 | The [macroref BOOST_IDENTITY_TYPE] macro can be used either when calling a user-defined macro (as shown by the examples so far), or internally when implementing a user-defined macro (as shown below). | |
116 | When [macroref BOOST_IDENTITY_TYPE] is used in the implementation of the user-defined macro, the caller of the user macro will have to specify the extra parenthesis (see also [@../../test/paren.cpp =paren.cpp=]): | |
117 | ||
118 | [paren] | |
119 | ||
120 | However, note that the caller will /always/ have to specify the extra parenthesis even when the macro parameters contain no comma: | |
121 | ||
122 | [paren_always] | |
123 | ||
124 | In some cases, using [macroref BOOST_IDENTITY_TYPE] in the implementation of the user-defined macro might provide the best syntax for the caller. | |
125 | For example, this is the case for `BOOST_MPL_ASSERT` because the majority of template meta-programming expressions contain unwrapped commas so it is less confusing for the user to always specify the extra parenthesis `((...))` instead of using [macroref BOOST_IDENTITY_TYPE]: | |
126 | ||
127 | BOOST_MPL_ASSERT(( // Natural syntax. | |
128 | boost::mpl::and_< | |
129 | boost::is_const<T> | |
130 | , boost::is_reference<T> | |
131 | > | |
132 | )); | |
133 | ||
134 | However, in other situations it might be preferable to not require the extra parenthesis in the common cases and handle commas as special cases using [macroref BOOST_IDENTITY_TYPE]. | |
135 | For example, this is the case for [@http://www.boost.org/libs/local_function `BOOST_LOCAL_FUNCTION`] for which always requiring the extra parenthesis `((...))` around the types would lead to an unnatural syntax for the local function signature: | |
136 | ||
137 | int BOOST_LOCAL_FUNCTION( ((int&)) x, ((int&)) y ) { // Unnatural syntax. | |
138 | return x + y; | |
139 | } BOOST_LOCAL_FUNCTION_NAME(add) | |
140 | ||
141 | Instead requiring the user to specify [macroref BOOST_IDENTITY_TYPE] only when needed allows for the more natural syntax `BOOST_LOCAL_FUNCTION(int& x, int& y)` in the common cases when the parameter types contain no comma (while still allowing to specify parameter types with commas as special cases using `BOOST_LOCAL_FUNCTION(BOOST_IDENTITY_TYPE((std::map<int, char>))& x, int& y)`). | |
142 | ||
143 | [endsect] | |
144 | ||
145 | [section Annex: Implementation] | |
146 | ||
147 | The implementation of this library macro is equivalent to the following: | |
148 | [footnote | |
149 | There is absolutely no guarantee that the macro is actually implemented using the code listed in this documentation. | |
150 | The listed code is for explanatory purposes only. | |
151 | ] | |
152 | ||
153 | #include <boost/type_traits/function_traits.hpp> | |
154 | ||
155 | #define BOOST_IDENTITY_TYPE(parenthesized_type) \ | |
156 | boost::function_traits<void parenthesized_type>::arg1_type | |
157 | ||
158 | Essentially, the type is wrapped between round parenthesis `(std::map<int, char>)` so it can be passed as a single macro parameter even if it contains commas. | |
159 | Then the parenthesized type is transformed into the type of a function returning `void` and with the specified type as the type of the first and only argument `void (std::map<int, char>)`. | |
160 | Finally, the type of the first argument `arg1_type` is extracted at compile-time using the `function_traits` meta-function therefore obtaining the original type from the parenthesized type (effectively stripping the extra parenthesis from around the specified type). | |
161 | ||
162 | [endsect] | |
163 | ||
164 | [xinclude reference.xml] | |
165 |