Section 4.2.  Integer Wrappers and Operations
Team LiB
Previous Section Next Section

4.2. Integer Wrappers and Operations

We'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 Operators

MPL 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 Operators

The 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.1. Logical Operators

Metafunction Specialization

::value and ::type::value

not_<X>

!X::value

and_<T1,T2,...Tn>

T1::value && ... Tn ::value

or_<T1,T2,...Tn>

T1::value || ... Tn ::value


Table 4.2 lists value comparison operators.

Table 4.2. Value Comparison Operators

Metafunction Specialization

::value and ::type::value

equal_to<X,Y>

X::value == Y::value

not_equal_to<X,Y>

X::value != Y::value

greater<X,Y>

X::value > Y::value

greater_equal<X,Y>

X::value >= Y::value

less<X,Y>

X::value < Y::value

less_equal<X,Y>

X::value <= Y::value


4.2.1.2 Integral-Valued Operators

The 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.

Table 4.3. Bitwise Operators

Metafunction Specialization

::value and ::type::value

bitand_<X,Y>

X::value & Y::value

bitor_<X,Y>

X::value | Y::value

bitxor_<X,Y>

X::value ^ Y::value


Table 4.4. Arithmetic Operators

Metafunction Specialization

::value and ::type::value

divides<T1,T2,...Tn>

T1::value / ... Tn ::value

minus<T1,T2,...Tn>

T1::value - ... Tn ::value

multiplies<T1,T2,...Tn>

T1::value * ... Tn ::value

plus<T1,T2,...Tn>

T1::value + ... Tn ::value

modulus<X,Y>

X::value % Y::value

shift_left<X,Y>

X::value << Y::value

shift_right<X,Y>

X::value >> Y::value

next<X>

X::next

prior<X>

X::prior


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 Shorthand

Occasionally 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."

    Team LiB
    Previous Section Next Section