4.2. Integer Wrappers and OperationsWe've already used MPL's int_ wrapper in our dimensional analysis example (see section 3.1). Now we can examine it in more detail, starting with its definition: template< int N > struct int_ { static const int value = N; typedef int_<N> type; typedef int value_type; typedef mpl::int_<N+1> next; typedef mpl::int_<N-1> prior; operator int() const { return N; } }; As you can see, int_ is very similar to bool_; in fact, the only major difference is the presence of its ::next and ::prior members. We'll explain their purpose later in this chapter. The library supplies similar numeric wrappers for long and std::size_t, known as long_ and size_t respectively. To represent values of any other integral type, the library provides a generic wrapper defined this way: template<class T, T N> struct integral_c { static const T value = N; typedef integral_c<T,N> type; typedef T value_type; typedef mpl::integral_c<T,N+1> next; typedef mpl::integral_c<T,N-1> prior; operator T() const { return N; } }; Integral sequence wrappers, like the vector_c template we used to implement dimensional analysis in Chapter 3 take an initial type parameter T, which is used to form their contained integral_c<T, ...> specializations. If the existence of both int_<...> and integral_c<int,...> is causing you a raised eyebrow, we can hardly blame you. After all, two otherwise equivalent integer wrappers can be different types. If we try to compare two integer wrappers this way: boost::is_same<mpl::integral_c<int,3>, mpl::int_<3> >::value the result (false) may be a little bit surprising. It's perhaps a little less surprising that the following is also false: boost::is_same<mpl::long_<3>, mpl::int_<3> >::value Whatever your reaction to these two examples may be, however, it should be clear by now that there's more to value equality of integral constant wrappers than simple type matching. The MPL metafunction for testing value equality is called equal_to, and is defined simply: template<class N1, class N2> struct equal_to : mpl::bool_<(N1::value == N2::value)> {}; It's important not to confuse equal_to with equal, which compares the elements of two sequences. The names of these two metafunctions were taken from those of similar components in the STL. 4.2.1. Integral OperatorsMPL supplies a whole suite of metafunctions for operating on integral constant wrappers, of which you've already seen a few (e.g., plus and minus). Before we get into the details, a word about naming conventions: When the metafunction corresponds to a built-in C++ operator for which the language has a textual alternative token name, like &&/and, the MPL metafunction is named for the alternative token followed by an underscore, thus mpl::and_. Otherwise, the MPL metafunction takes its name from the corresponding STL function object, thus mpl::equal_to. The operators fall into four groups. In the tables below, n = 5 by default. See the Configuration Macros section of the MPL reference manual for information about how to change n. 4.2.1.1 Boolean-Valued OperatorsThe metafunctions in this group all have bool constant results. We've already covered the logical operators, but they're included here for completeness (see Table 4.1).
Table 4.2 lists value comparison operators.
4.2.1.2 Integral-Valued OperatorsThe operators section all have integral constant results whose type is the same as the type of the expression they evaluate (see Tables 4.3 and 4.4). In other words, since the type of 3+2L is long, mpl::plus<mpl::int_<3>, mpl::long_<2> >::type::value_type is also long.
The next and prior metafunctions are somewhat analogous to the C++ unary operators ++ and --. Since metadata is immutable, though, next and prior can't modify their arguments. As a matter of fact, mpl::next and mpl::prior are precisely analogous to two runtime functions declared in namespace boost that simply return incremented and decremented versions of their arguments: namespace boost { template <class T> inline T next(T x) { return ++x; } template <class T> inline T prior(T x) { return --x; } } You might find it curious that mpl::next<X> and mpl::prior<X> are not simply defined to return wrappers for X::value+1 and X::value-1, respectively, even though they function that way when used on integral constant wrappers. The reasons should become clear in the next chapter, when we discuss the use of next and prior for sequence iteration. 4.2.2. The _c Integral ShorthandOccasionally we find ourselves in a situation where the need to explicitly build wrapper types becomes an inconvenience. It happened in our dimensional analysis code (Chapter 3), where the use of mpl::vector_c<int, ...> instead of mpl::vector<...> eliminated the need to write int_ specializations for each of seven powers of fundamental units. We actually sidestepped another such circumstance while working on the param_type metafunction earlier in this chapter. Before mpl::or_ came along to save our bacon, we were stuck with this ugly definition: template <class T> struct param_type : mpl::eval_if< mpl::bool_< boost::is_scalar<T>::value || boost::is_reference<T>::value > , mpl::identity<T> , add_reference<T const> > {}; With MPL's eval_if_c, also supplied by <boost/mpl/eval_if.hpp>, we might have written: template <class T> struct param_type : mpl::eval_if_c< boost::is_scalar<T>::value || boost::is_reference<T>::value , mpl::identity<T> , add_reference<T const> > {}; By now you've probably begun to notice some commonality in the use of _c: it always adorns templates that take raw integral constants, instead of wrappers, as parameters. The _c suffix can be thought of as an abbreviation for "constant" or "of integral constants." |