This chapter is a reference guide to the General utility support in the Metrowerks standard libraries.
This chapter consists of utilies for support of non standard headers.
The purpose of this header is to offer a collection of non-standard utilities collected under the namespace Metrowerks. These utilities are of a fundamental nature, and are typically used in other utilities, rather than top level code. Example usage assumes that a using declaration or directive has been previously issued.
NOTE This header is non-standard The classes herein are offered as extensions to the C++ standard. They are marked as such by the namespace Metrowerks.
Concepts and ideas co-developed on Boost
A collection of templated structs which can be used for simple compile-time transformations of types.
Will remove the top level const (if present) from a type.
Prototype:
typedef typename
remove_const<T>::type non_const_type; The resulting "non_const_type" will be the same as the input type T, except that if T is const qualified, that const-ness will be removed.
typedef typename remove_const <const int>::type Int; Int has type int.
Will remove the top level volatile (if present) from a type.
Prototype:
typedef typename
remove_volatile<T>::type non_volatile_type; The resulting "non_volatile_type" will be the same as the input type T, except that if T is volatile qualified, that volatile-ness will be removed.
typedef typename remove_volatile <volatile int>::type Int; Int has type int.
Will remove the top level qualifiers (const and/or volatile, if present) from a type.
Prototype:
typedef typename
remove_cv<T>::type non_qualified_type; The resulting "non_qualified_type" will be the same as the input type T, except that if T is cv qualified, the qualifiers will be removed.
typedef typename remove_cv <const int>::type Int; Int has type int.
If given a pointer, returns the type being pointed to. If given a non-pointer type, simply returns the input.
Prototype:
typedef typename
remove_pointer<T>::type pointed_to_type; typedef typename remove_pointer<const int*volatile*const>::type IntPtr; typedef typename remove_pointer<IntPtr>::type Int;
IntPtr will have type type const int*volatile. Int will have the type const int.
remove_reference
If given a reference, returns the type being referenced. If given
a non-reference, simply returns the input.
Prototype:
typedef typename
remove_reference<T>::type referenced_type;
typedef typename remove_reference<int&>::type Int; typedef typename remove_reference<const int&>::type ConstInt;
Int has the type int, and ConstInt has the type const int.
remove_bounds
If given an array type, will return the type of an element in
the array. If given a non-array type, simply returns the input.
Prototype:
typedef typename remove_bounds<T>::type Element;
typedef int IntArray[4]; typedef typename remove_bounds<IntArray>::type Int;
Int has the type int.
remove_all
This transformation will recursively remove cv qualifiers, pointers,
references and array bounds until the type is a fundamental type,
enum, union, class or member pointer.
Prototype:
typedef typename remove_all<T>::type fundamental_type;
typedef const int** Array[4]; typedef typename remove_all<Array*&>::type Int;
Int has the type int.
Type Query
The following structs perform basic queries on one or more types
and return a bool value.
is_same
This struct can be used to tell if two types are the same type
or not.
Prototype:
bool b = is_same<T, U>::value;
bool b = is_same<const int, int>::value; The resulting value is false. int and const int are two distinct types.
CV Query
is_const
Returns true if type has a top level const qualifier, else false.
Prototype:
bool b = is_const<T>::value;
bool b = is_const<const int>::value; The resulting value is true.
is_volatile
Returns true if type has a top level volatile qualifier, else
false.
Prototype:
bool b = is_volatile<T>::value;
bool b = is_volatile<const int>::value; The resulting value is false.
Type Classification
The following structs implement classification as defined by
section 3.9 in the C++ standard. All types can be classified into
one of ten basic categories:
Top level cv qualifications do not affect type classification.
For example, both const int and int are considered to be of integral
type.
Prototype:
bool b = is_XXX<T>::value;where
XXXis one of the ten basic categories.
bool b = is_integral<volatile int>::value; The value of b is true.
The classifications: is_enum and is_union do not currently work
automatically. Enums and unions will be mistakenly classified
as class type. This can be corrected on a case by case basis by
specializing is_enum_imp or is_union_imp. These specializations
are in the Metrowerks::details namespace.
Example of Metrowerks::details namespace:
enum MyEnum {zero, one, two}; template <> struct Metrowerks::details::is_enum_imp<MyEnum> {static const bool value = true;};
Now MyEnum will be correctly classified as an enum instead of
a class (via the is_enum struct). You do not need to worry about
providing this specialization unless you are explicitly using
the is_enum query and wanting your enumeration to answer to it
correctly.
There are also five "super" categories that are made up of combinations
of the ten basic categories:
1. is_arithmetic - is_integral or is_floating
2. is_fundamental - is_arithmetic or is_void
3. is_scalar - is_arithmetic or is_pointer or is_member_pointer
or is_enum
4. is_compound - not is_fundamental
5. is_object - anything but a void or reference type
is_extension is also provided for those types that we provide
as an extension to the C++ standard. is_extension<T>::value will
be false for all types except for long long and unsigned long
long.
has_extension is a modified form of is_extension that answers
to true if a type either is an extension or contains an extension.
Example of is_extension and has_extension:
is_extension<long long*&>::value; // false has_extension<long long*&>::value; // true
is_signed / is_unsigned
These structs only work on arithmetic types. The type must be
constructable by an int and be less-than comparable.
Example of is_signed and is_unsigned use:
bool b1 = is_signed<char>::value; bool b2 = is_unsigned<char>::value;
NOTE
At the risk of restarting a great debate, bool tests as unsigned
POD classification
Four structs classify types as to whether or not they have trivial
special members as defined in section 12 of the C++ standard:
This library will answer correctly for non-class types. But user
defined class types will always answer false to any of these queries.
If you create a class with trivial special members, and you want
that class to be able to take advantage of any optimizations that
might arise from the assumption of trivial special members, you
can specialize these structs:
Example of specialized structs:
template <>
struct Metrowerks::details::class_has_trivial_default_ctor<MyClass>
{static const bool value = true;};
template <>
struct Metrowerks::details::class_has_trivial_copy_ctor<MyClass>
{static const bool value = true;};
template <>
struct Metrowerks::details::class_has_trivial_assignment<MyClass>
{static const bool value = true;};
template <>
struct Metrowerks::details::class_has_trivial_dtor<MyClass>
{static const bool value = true;};
Note that these specializations need not worry about cv qualifications.
The higher level has_trival_XXX structs do that for you.
Finally there is an is_POD struct that will answer true if a
type answers true on all four of the above queries.
This is a compile time assert. This is a very basic version of
this idea. Can be used to test assumptions at comile time.
Example of compile_assert use:
#include <msl_utility>
template <class T>
T
foo(const T& t)
{
Metrowerks::compile_assert<sizeof(T) >= sizeof(int)> T_Must_Be_At_Least_As_Big_As_int;
//...
return t;
}
int main()
{
int i;
foo(i); // ok
char c;
foo(c); // Error : illegal use of incomplete struct/union/class
// 'Metrowerks::compile_assert<0>'
Given an array type, you can get the size of the array with array_size.
typedef int Array[10]; size_t n = array_size<Array>::value; n has the value of 10.
A simple union of class type and union types.
Prototype:
bool b = can_derive_from<T>::value;
Starting with Pro 4.1 the standard sequences vector, deque and
list implemented the "void* optimization" as described in section
13.5 of Stroustrup's 3rd. In a nutshell, this optimization allows
all Container<T*> to share an implementation with Container<void*>
for the purpose of reducing template code bloat.
Starting with Pro 6 containers will take this idea further and
with more flexibility. Using store_as, one can specify what types
can be stored as alternate types in the containers. For example,
a vector<unsigned char> can be implemented as a vector<char>,
thus saving on the instantiation of vector<unsigned char>. Only
vector uses store_as in Pro 6, but other containers will have
this capability in future releases.
Prototype:
template <> struct store_as<Type_to_be_optimized>
{typedef Implementaiton_Type type;};
For example, to specify that a container<long> be implemented
in terms of a container<unsigned long>:
template <> struct store_as<long>
{typedef unsigned long type;};
If a specialization of store_as does not appear for a type, then
that type will be implemented as itself in containers.
This header has a default table of store_as specializations suitable
for your platform. But if for some reason you are not happy with
the shipping configuration, you can alter the behavior here. Additionally,
this optimization can be turned off by #define'ing _Inhibit_Container_Optimization
in <mslconfig>, or in a prefix file.
The requirements for a type to appear in a store_as specialization
are:
destructor.
the object to be zeroed (if this constructor is used).
sizeof.
User defined types can also take advantage of this optimization
if they meet the above requirements (recommended only for those
that really know what they are doing). Client code must declare
that the user defined type is a POD, and then create a store_as
entry. For example, here is a struct that will share its vector
implementation with vector<unsigned long>:
A struct that shares its vector implementation:
#include <msl_utility>
#include <vector>
struct A
{
int data_;
};
template <> struct Metrowerks::is_POD<A>
{static const bool value = true;};
template <> struct Metrowerks::store_as<A>
{typedef unsigned long type;};
int main()
{
std::vector<A> a(5);
// Shares implementation with vector<unsigned long>
}
Note that this is strictly a code size optimization. Functionality
does not change. And it only saves on code size if you already
have to use a vector<unsigned long> for some other reason (or
if you are using a vector that is stored as a vector<unsigned
long>).
This struct is a collection of typedef's that ease coding of
template classes when the template parameter may be a non-array
object, an array, or a reference. The typedef's specify how to
pass a type into a function, and how to pass it back out either
by value, reference or const reference. The interface is:
call_traits<T>::value_type
call_traits<T>::reference
call_traits<T>::const_reference
call_traits<T>::param_type
The first three types are suggestions on how to return a type
from a function by value, reference or const reference. The fourth
type is a suggestion on how to pass a type into a method.
The call_traits struct is most useful in avoiding references
to a reference which are currently illegal in C++. Another use
is in helping to decay array-type parameters into pointers. In
general, use of call_traits is limited to advanced techniques,
and will not require specializations of call_traits to be made.
For example uses of call_traits see compressed_pair. For an example
specialization see alloc_ptr.
Answers true if the type is a class or union that has no data
members, otherwise answers false. This is a key struct for determining
if the space for an "empty" object can be optimized away or not.
Prototype:
bool b = is_empty<T>::value;
Like std::pair, but attempts to optimize away the space for either
the first or second template parameter if the type is "empty".
And instead of the members being accessible via the public data
members first and second, they are accessible via member methods
first() and second(). compressed_pair handles reference types
as well as other types thanks to the call_traits template. This
is a good example to study if you're wanting to see how to take
advantage of either call_traits or is_empty. To see an example
of how compressed_pair is used see alloc_ptr.
template <class T1, class T2>
class compressed_pair<T1, T2>
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair();
compressed_pair(first_param_type x, second_param_type y);
explicit compressed_pair(first_param_type x);
explicit compressed_pair(second_param_type y);
first_reference first();
first_const_reference first() const;
second_reference second();
second_const_reference second() const;
void swap(compressed_pair& y);
};
template <class T1, class T2> void swap
(compressed_pair<T1, T2>& x, compressed_pair<T1, T2>& y);
Use of the single argument constructors will fail at compile
time (ambiguous call) if first_type and second_type are the same
type.
The swap specialization will call swap on each member if and
only if its size has not been optimized away. The call to swap
on each member will look both in std, and in the member's namespace
for the appropriate swap specialization. Thus clients of compressed_pair
need not put swap specializations into namespace std.
A good use of compressed_pair is in the implementation of a container
that must store a function object. Function objects are typically
zero-sized classes, but are also allowed to be ordinary function
pointers. If the function object is a zero-sized class, then the
container can optimize its space away by using it as a base class.
But if the function object instantiates to a function pointer,
it can not be used as a base class. By putting the function object
into a compressed_pair, the container implementor need not worry
whether it will instantiate to a class or function pointer.
#include <iostream>
#include <functional>
#include <msl_utility>
template <class T, class Compare>
class MyContainer
{
public:
explicit MyContainer(const Compare& c = Compare()) : data_(0, c) {}
T* pointer() {return data_.first();}
const T* pointer() const {return data_.first();}
Compare& compare() {return data_.second();}
const Compare& compare() const {return data_.second();}
void swap(MyContainer& y) {data_.swap(y.data_);}
private:
Metrowerks::compressed_pair<T*, Compare> data_;
};
int main()
{
typedef MyContainer<int, std::less<int> > MyContainer1;
typedef MyContainer<int, bool (*)(int, int)> MyContainer2;
std::cout << sizeof(MyContainer1) << '\n';
std::cout << sizeof(MyContainer2) << '\n';
}
MyContainer1 uses a zero-sized Compare object. On a 32 bit machine,
the sizeof MyContainer1 will be 4 bytes as the space for Compare
is optimized away by compressed_pair. But MyContainer2 instantiates
Compare with an ordinary function pointer which can't be optimized
away. Thus the sizeof MyContainer2 is 8 bytes.
An extension of std::auto_ptr. alloc_ptr will do everything that
auto_ptr will do with the same syntax. Additionally alloc_ptr
will deal with array new/delete:
Alloc_ptr will deal with array new/delete:
alloc_ptr<int, array_deleter<int> > a(new int[4]); // Ok, destructor will use delete[]
By adding the array_deleter<T> template parameter you can enable
alloc_ptr to correctly handle pointers to arrays of elements.
alloc_ptr will also work with allocators which adhere to the
standard interface. This comes in very handy if you are writing
a container that is templated on an allocator type. You can instantiate
an alloc_ptr to work with an allocator with:
alloc_ptr<T, Allocator<T>, typename Allocator<T>::size_type> a;
The third parameter can be omitted if the allocator is always
going to allocate and deallocate items one at a time (e.g. node
based containers).
alloc_ptr takes full advantage of compressed_pair so that it
is as efficient as std::auto_ptr. The sizeof(alloc_ptr<int>) is
only one word. Additionally alloc_ptr will work with a reference
to an allocator instead of an allocator (thanks to call_traits).
This is extremely useful in the implementation of node based containers.
template<class T, class Allocator = single_deleter<T>,
class Size = number<call_traits<Allocator>::value_type::size_type, 1> >
class alloc_ptr
{
public:
typedef T element_type;
typedef typename call_traits<Allocator>::value_type allocator_type;
typedef typename call_traits<Allocator>::param_type allocator_param_type;
typedef typename call_traits<Allocator>::reference allocator_reference;
typedef typename call_traits<Allocator>::const_reference allocator_const_reference;
typedef typename allocator_type::size_type size_type;
typedef typename allocator_type::difference_type difference_type;
typedef typename allocator_type::pointer pointer;
typedef typename allocator_type::const_pointer const_pointer;
typedef typename allocator_type::reference reference;
typedef typename allocator_type::const_reference const_reference;
explicit alloc_ptr(pointer p = 0);
alloc_ptr(pointer p, allocator_param_type alloc, Size sz = Size());
alloc_ptr(alloc_ptr& x);
template<class U>
alloc_ptr(alloc_ptr<U, allocator_type::rebind<U>::other, Size>& x);
~alloc_ptr();
alloc_ptr& operator =(alloc_ptr& x);
template<class U>
alloc_ptr& operator=(alloc_ptr<U, allocator_type::rebind<U>::other, Size>& x);
reference operator*() const;
pointer operator->() const;
reference operator[](difference_type n);
reference operator[](size_type n);
const_reference operator[](difference_type n) const;
const_reference operator[](size_type n) const;
pointer get() const;
pointer release();
void reset(pointer p = 0, Size size);
alloc_ptr(alloc_ptr_ref<T, Allocator, Size> r);
alloc_ptr& operator=(alloc_ptr_ref<T, Allocator, Size> r);
template<class U> operator alloc_ptr_ref<U, allocator_type::rebind<U>::other, Size>();
template<class U> operator alloc_ptr<U, allocator_type::rebind<U>::other, Size>();
allocator_reference allocator();
allocator_const_reference allocator() const;
Size& get_size();
size_type capacity() const;
};
This is essentially the std::auto_ptr interface with a few twists
to accommodate allocators and size parameters.
Last updated: July 21, 2000