mirror of
https://github.com/boostorg/utility.git
synced 2025-05-11 13:24:02 +00:00
Added new type traits files.
[SVN r9238]
This commit is contained in:
parent
8b92c8a085
commit
393e79c1fd
@ -1,424 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (c) 1999
|
|
||||||
* Dr John Maddock
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, distribute and sell this software
|
|
||||||
* and its documentation for any purpose is hereby granted without fee,
|
|
||||||
* provided that the above copyright notice appear in all copies and
|
|
||||||
* that both that copyright notice and this permission notice appear
|
|
||||||
* in supporting documentation. Dr John Maddock makes no representations
|
|
||||||
* about the suitability of this software for any purpose.
|
|
||||||
* It is provided "as is" without express or implied warranty.
|
|
||||||
*
|
|
||||||
* This file provides some example of type_traits usage -
|
|
||||||
* by "optimising" various algorithms:
|
|
||||||
*
|
|
||||||
* opt::copy - optimised for trivial copy (cf std::copy)
|
|
||||||
* opt::fill - optimised for trivial copy/small types (cf std::fill)
|
|
||||||
* opt::destroy_array - an example of optimisation based upon omitted destructor calls
|
|
||||||
* opt::iter_swap - uses type_traits to determine whether the iterator is a proxy
|
|
||||||
* in which case it uses a "safe" approach, otherwise calls swap
|
|
||||||
* on the assumption that swap may be specialised for the pointed-to type.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Release notes:
|
|
||||||
23rd July 2000:
|
|
||||||
Added explicit failure for broken compilers that don't support these examples.
|
|
||||||
Fixed broken gcc support (broken using directive).
|
|
||||||
Reordered tests slightly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <typeinfo>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <boost/timer.hpp>
|
|
||||||
#include <boost/type_traits.hpp>
|
|
||||||
#include <boost/call_traits.hpp>
|
|
||||||
|
|
||||||
using std::cout;
|
|
||||||
using std::endl;
|
|
||||||
using std::cin;
|
|
||||||
|
|
||||||
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
|
|
||||||
#error "Sorry, without template partial specialisation support there isn't anything to test here..."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace opt{
|
|
||||||
|
|
||||||
//
|
|
||||||
// algorithm destroy_array:
|
|
||||||
// The reverse of std::unitialized_copy, takes a block of
|
|
||||||
// unitialized memory and calls destructors on all objects therein.
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace detail{
|
|
||||||
|
|
||||||
template <bool>
|
|
||||||
struct array_destroyer
|
|
||||||
{
|
|
||||||
template <class T>
|
|
||||||
static void destroy_array(T* i, T* j){ do_destroy_array(i, j); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct array_destroyer<true>
|
|
||||||
{
|
|
||||||
template <class T>
|
|
||||||
static void destroy_array(T*, T*){}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void do_destroy_array(T* first, T* last)
|
|
||||||
{
|
|
||||||
while(first != last)
|
|
||||||
{
|
|
||||||
first->~T();
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace detail
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline void destroy_array(T* p1, T* p2)
|
|
||||||
{
|
|
||||||
detail::array_destroyer<boost::has_trivial_destructor<T>::value>::destroy_array(p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// unoptimised versions of destroy_array:
|
|
||||||
//
|
|
||||||
template <class T>
|
|
||||||
void destroy_array1(T* first, T* last)
|
|
||||||
{
|
|
||||||
while(first != last)
|
|
||||||
{
|
|
||||||
first->~T();
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
void destroy_array2(T* first, T* last)
|
|
||||||
{
|
|
||||||
for(; first != last; ++first) first->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// opt::copy
|
|
||||||
// same semantics as std::copy
|
|
||||||
// calls memcpy where appropiate.
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace detail{
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
struct copier
|
|
||||||
{
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
static I2 do_copy(I1 first, I1 last, I2 out);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
I2 copier<b>::do_copy(I1 first, I1 last, I2 out)
|
|
||||||
{
|
|
||||||
while(first != last)
|
|
||||||
{
|
|
||||||
*out = *first;
|
|
||||||
++out;
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct copier<true>
|
|
||||||
{
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
static I2* do_copy(I1* first, I1* last, I2* out)
|
|
||||||
{
|
|
||||||
memcpy(out, first, (last-first)*sizeof(I2));
|
|
||||||
return out+(last-first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
inline I2 copy(I1 first, I1 last, I2 out)
|
|
||||||
{
|
|
||||||
typedef typename boost::remove_cv<typename std::iterator_traits<I1>::value_type>::type v1_t;
|
|
||||||
typedef typename boost::remove_cv<typename std::iterator_traits<I2>::value_type>::type v2_t;
|
|
||||||
enum{ can_opt = boost::is_same<v1_t, v2_t>::value
|
|
||||||
&& boost::is_pointer<I1>::value
|
|
||||||
&& boost::is_pointer<I2>::value
|
|
||||||
&& boost::has_trivial_assign<v1_t>::value };
|
|
||||||
return detail::copier<can_opt>::do_copy(first, last, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// fill
|
|
||||||
// same as std::fill, uses memset where appropriate, along with call_traits
|
|
||||||
// to "optimise" parameter passing.
|
|
||||||
//
|
|
||||||
namespace detail{
|
|
||||||
|
|
||||||
template <bool opt>
|
|
||||||
struct filler
|
|
||||||
{
|
|
||||||
template <typename I, typename T>
|
|
||||||
static void do_fill(I first, I last, typename boost::call_traits<T>::param_type val);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
template <typename I, typename T>
|
|
||||||
void filler<b>::do_fill(I first, I last, typename boost::call_traits<T>::param_type val)
|
|
||||||
{
|
|
||||||
while(first != last)
|
|
||||||
{
|
|
||||||
*first = val;
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct filler<true>
|
|
||||||
{
|
|
||||||
template <typename I, typename T>
|
|
||||||
static void do_fill(I first, I last, T val)
|
|
||||||
{
|
|
||||||
std::memset(first, val, last-first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class I, class T>
|
|
||||||
inline void fill(I first, I last, const T& val)
|
|
||||||
{
|
|
||||||
enum{ can_opt = boost::is_pointer<I>::value
|
|
||||||
&& boost::is_arithmetic<T>::value
|
|
||||||
&& (sizeof(T) == 1) };
|
|
||||||
typedef detail::filler<can_opt> filler_t;
|
|
||||||
filler_t::template do_fill<I,T>(first, last, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// iter_swap:
|
|
||||||
// tests whether iterator is a proxying iterator or not, and
|
|
||||||
// uses optimal form accordingly:
|
|
||||||
//
|
|
||||||
namespace detail{
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
struct swapper
|
|
||||||
{
|
|
||||||
template <typename I>
|
|
||||||
static void do_swap(I one, I two)
|
|
||||||
{
|
|
||||||
typedef typename std::iterator_traits<I>::value_type v_t;
|
|
||||||
v_t v = *one;
|
|
||||||
*one = *two;
|
|
||||||
*two = v;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
using std::swap;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct swapper<true>
|
|
||||||
{
|
|
||||||
template <typename I>
|
|
||||||
static void do_swap(I one, I two)
|
|
||||||
{
|
|
||||||
using std::swap;
|
|
||||||
swap(*one, *two);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename I1, typename I2>
|
|
||||||
inline void iter_swap(I1 one, I2 two)
|
|
||||||
{
|
|
||||||
typedef typename std::iterator_traits<I1>::reference r1_t;
|
|
||||||
typedef typename std::iterator_traits<I2>::reference r2_t;
|
|
||||||
enum{ can_opt = boost::is_reference<r1_t>::value && boost::is_reference<r2_t>::value && boost::is_same<r1_t, r2_t>::value };
|
|
||||||
detail::swapper<can_opt>::do_swap(one, two);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}; // namespace opt
|
|
||||||
|
|
||||||
//
|
|
||||||
// define some global data:
|
|
||||||
//
|
|
||||||
const int array_size = 1000;
|
|
||||||
int i_array[array_size] = {0,};
|
|
||||||
const int ci_array[array_size] = {0,};
|
|
||||||
char c_array[array_size] = {0,};
|
|
||||||
const char cc_array[array_size] = { 0,};
|
|
||||||
|
|
||||||
const int iter_count = 1000000;
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// test destroy_array,
|
|
||||||
// compare destruction time of an array of ints
|
|
||||||
// with unoptimised form.
|
|
||||||
//
|
|
||||||
cout << "Measuring times in micro-seconds per 1000 elements processed" << endl << endl;
|
|
||||||
cout << "testing destroy_array...\n"
|
|
||||||
"[Some compilers may be able to optimise the \"unoptimised\"\n versions as well as type_traits does.]" << endl;
|
|
||||||
/*cache load*/ opt::destroy_array(i_array, i_array + array_size);
|
|
||||||
boost::timer t;
|
|
||||||
double result;
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::destroy_array(i_array, i_array + array_size);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "destroy_array<int>: " << result << endl;
|
|
||||||
/*cache load*/ opt::destroy_array1(i_array, i_array + array_size);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::destroy_array1(i_array, i_array + array_size);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "destroy_array<int>(unoptimised#1): " << result << endl;
|
|
||||||
/*cache load*/ opt::destroy_array2(i_array, i_array + array_size);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::destroy_array2(i_array, i_array + array_size);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "destroy_array<int>(unoptimised#2): " << result << endl << endl;
|
|
||||||
|
|
||||||
cout << "testing fill(char)...\n"
|
|
||||||
"[Some standard library versions may already perform this optimisation.]" << endl;
|
|
||||||
/*cache load*/ opt::fill<char*, char>(c_array, c_array + array_size, (char)3);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::fill<char*, char>(c_array, c_array + array_size, (char)3);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "opt::fill<char*, char>: " << result << endl;
|
|
||||||
/*cache load*/ std::fill(c_array, c_array + array_size, (char)3);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
std::fill(c_array, c_array + array_size, (char)3);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "std::fill<char*, char>: " << result << endl << endl;
|
|
||||||
|
|
||||||
cout << "testing fill(int)...\n"
|
|
||||||
"[Tests the effect of call_traits pass-by-value optimisation -\nthe value of this optimisation may depend upon hardware characteristics.]" << endl;
|
|
||||||
/*cache load*/ opt::fill<int*, int>(i_array, i_array + array_size, 3);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::fill<int*, int>(i_array, i_array + array_size, 3);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "opt::fill<int*, int>: " << result << endl;
|
|
||||||
/*cache load*/ std::fill(i_array, i_array + array_size, 3);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
std::fill(i_array, i_array + array_size, 3);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "std::fill<int*, int>: " << result << endl << endl;
|
|
||||||
|
|
||||||
cout << "testing copy...\n"
|
|
||||||
"[Some standard library versions may already perform this optimisation.]" << endl;
|
|
||||||
/*cache load*/ opt::copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "opt::copy<const int*, int*>: " << result << endl;
|
|
||||||
/*cache load*/ std::copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
std::copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "std::copy<const int*, int*>: " << result << endl;
|
|
||||||
/*cache load*/ opt::detail::copier<false>::template do_copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::detail::copier<false>::template do_copy<const int*, int*>(ci_array, ci_array + array_size, i_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "standard \"unoptimised\" copy: " << result << endl << endl;
|
|
||||||
|
|
||||||
/*cache load*/ opt::copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "opt::copy<const char*, char*>: " << result << endl;
|
|
||||||
/*cache load*/ std::copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
std::copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "std::copy<const char*, char*>: " << result << endl;
|
|
||||||
/*cache load*/ opt::detail::copier<false>::template do_copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
t.restart();
|
|
||||||
for(i = 0; i < iter_count; ++i)
|
|
||||||
{
|
|
||||||
opt::detail::copier<false>::template do_copy<const char*, char*>(cc_array, cc_array + array_size, c_array);
|
|
||||||
}
|
|
||||||
result = t.elapsed();
|
|
||||||
cout << "standard \"unoptimised\" copy: " << result << endl << endl;
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// testing iter_swap
|
|
||||||
// really just a check that it does in fact compile...
|
|
||||||
std::vector<int> v1;
|
|
||||||
v1.push_back(0);
|
|
||||||
v1.push_back(1);
|
|
||||||
std::vector<bool> v2;
|
|
||||||
v2.push_back(0);
|
|
||||||
v2.push_back(1);
|
|
||||||
opt::iter_swap(v1.begin(), v1.begin()+1);
|
|
||||||
opt::iter_swap(v2.begin(), v2.begin()+1);
|
|
||||||
|
|
||||||
cout << "Press any key to exit...";
|
|
||||||
cin.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,489 +0,0 @@
|
|||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
||||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
|
|
||||||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
|
||||||
<title>C++ Type traits</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080">
|
|
||||||
|
|
||||||
<h2 align="center">C++ Type traits</h2>
|
|
||||||
<p align="center"><em>by John Maddock and Steve Cleary</em></p>
|
|
||||||
<p align="center"><em>This is a draft of an article that will appear in a future
|
|
||||||
issue of </em><a href="http://www.ddj.com"><em>Dr Dobb's Journal</em></a></p>
|
|
||||||
<p>Generic programming (writing code which works with any data type meeting a
|
|
||||||
set of requirements) has become the method of choice for providing reusable
|
|
||||||
code. However, there are times in generic programming when "generic"
|
|
||||||
just isn't good enough - sometimes the differences between types are too large
|
|
||||||
for an efficient generic implementation. This is when the traits technique
|
|
||||||
becomes important - by encapsulating those properties that need to be considered
|
|
||||||
on a type by type basis inside a traits class, we can minimise the amount of
|
|
||||||
code that has to differ from one type to another, and maximise the amount of
|
|
||||||
generic code.</p>
|
|
||||||
<p>Consider an example: when working with character strings, one common
|
|
||||||
operation is to determine the length of a null terminated string. Clearly it's
|
|
||||||
possible to write generic code that can do this, but it turns out that there are
|
|
||||||
much more efficient methods available: for example, the C library functions <font size="2" face="Courier New">strlen</font>
|
|
||||||
and <font size="2" face="Courier New">wcslen</font> are usually written in
|
|
||||||
assembler, and with suitable hardware support can be considerably faster than a
|
|
||||||
generic version written in C++. The authors of the C++ standard library realised
|
|
||||||
this, and abstracted the properties of <font size="2" face="Courier New">char</font>
|
|
||||||
and <font size="2" face="Courier New">wchar_t</font> into the class <font size="2" face="Courier New">char_traits</font>.
|
|
||||||
Generic code that works with character strings can simply use <font size="2" face="Courier New">char_traits<>::length</font>
|
|
||||||
to determine the length of a null terminated string, safe in the knowledge that
|
|
||||||
specialisations of <font size="2" face="Courier New">char_traits</font> will use
|
|
||||||
the most appropriate method available to them.</p>
|
|
||||||
<h4>Type traits</h4>
|
|
||||||
<p>Class <font size="2" face="Courier New">char_traits</font> is a classic
|
|
||||||
example of a collection of type specific properties wrapped up in a single class
|
|
||||||
- what Nathan Myers termed a <i>baggage class</i>[1]. In the Boost type-traits
|
|
||||||
library, we[2] have written a set of very specific traits classes, each of which
|
|
||||||
encapsulate a single trait from the C++ type system; for example, is a type a
|
|
||||||
pointer or a reference type? Or does a type have a trivial constructor, or a
|
|
||||||
const-qualifier? The type-traits classes share a unified design: each class has
|
|
||||||
a single member <i>value</i>, a compile-time constant that is true if the type
|
|
||||||
has the specified property, and false otherwise. As we will show, these classes
|
|
||||||
can be used in generic programming to determine the properties of a given type
|
|
||||||
and introduce optimisations that are appropriate for that case.</p>
|
|
||||||
<p>The type-traits library also contains a set of classes that perform a
|
|
||||||
specific transformation on a type; for example, they can remove a top-level
|
|
||||||
const or volatile qualifier from a type. Each class that performs a
|
|
||||||
transformation defines a single typedef-member <i>type</i> that is the result of
|
|
||||||
the transformation. All of the type-traits classes are defined inside namespace <font size="2" face="Courier New">boost</font>;
|
|
||||||
for brevity, namespace-qualification is omitted in most of the code samples
|
|
||||||
given.</p>
|
|
||||||
<h4>Implementation</h4>
|
|
||||||
<p>There are far too many separate classes contained in the type-traits library
|
|
||||||
to give a full implementation here - see the source code in the Boost library
|
|
||||||
for the full details - however, most of the implementation is fairly repetitive
|
|
||||||
anyway, so here we will just give you a flavour for how some of the classes are
|
|
||||||
implemented. Beginning with possibly the simplest class in the library, is_void<T>
|
|
||||||
has a member <i>value</i> that is true only if T is void.</p>
|
|
||||||
<pre>template <typename T>
|
|
||||||
struct is_void
|
|
||||||
{ static const bool value = false; };
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct is_void<void>
|
|
||||||
{ static const bool value = true; };</pre>
|
|
||||||
<p>Here we define a primary version of the template class <font size="2" face="Courier New">is_void</font>,
|
|
||||||
and provide a full-specialisation when T is void. While full specialisation of a
|
|
||||||
template class is an important technique, sometimes we need a solution that is
|
|
||||||
halfway between a fully generic solution, and a full specialisation. This is
|
|
||||||
exactly the situation for which the standards committee defined partial
|
|
||||||
template-class specialisation. As an example, consider the class
|
|
||||||
boost::is_pointer<T>: here we needed a primary version that handles all
|
|
||||||
the cases where T is not a pointer, and a partial specialisation to handle all
|
|
||||||
the cases where T is a pointer:</p>
|
|
||||||
<pre>template <typename T>
|
|
||||||
struct is_pointer
|
|
||||||
{ static const bool value = false; };
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_pointer<T*>
|
|
||||||
{ static const bool value = true; };</pre>
|
|
||||||
<p>The syntax for partial specialisation is somewhat arcane and could easily
|
|
||||||
occupy an article in its own right; like full specialisation, in order to write
|
|
||||||
a partial specialisation for a class, you must first declare the primary
|
|
||||||
template. The partial specialisation contains an extra <…> after the
|
|
||||||
class name that contains the partial specialisation parameters; these define the
|
|
||||||
types that will bind to that partial specialisation rather than the default
|
|
||||||
template. The rules for what can appear in a partial specialisation are somewhat
|
|
||||||
convoluted, but as a rule of thumb if you can legally write two function
|
|
||||||
overloads of the form:</p>
|
|
||||||
<pre>void foo(T);
|
|
||||||
void foo(U);</pre>
|
|
||||||
<p>Then you can also write a partial specialisation of the form:</p>
|
|
||||||
<pre>template <typename T>
|
|
||||||
class c{ /*details*/ };
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
|
|
||||||
class c<U>{ /*details*/ };</pre>
|
|
||||||
<p>This rule is by no means foolproof, but it is reasonably simple to remember
|
|
||||||
and close enough to the actual rule to be useful for everyday use.</p>
|
|
||||||
<p>As a more complex example of partial specialisation consider the class
|
|
||||||
remove_bounds<T>. This class defines a single typedef-member <i>type</i>
|
|
||||||
that is the same type as T but with any top-level array bounds removed; this is
|
|
||||||
an example of a traits class that performs a transformation on a type:</p>
|
|
||||||
<pre>template <typename T>
|
|
||||||
struct remove_bounds
|
|
||||||
{ typedef T type; };
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct remove_bounds<T[N]>
|
|
||||||
{ typedef T type; };</pre>
|
|
||||||
<p>The aim of remove_bounds is this: imagine a generic algorithm that is passed
|
|
||||||
an array type as a template parameter, <font size="2" face="Courier New">remove_bounds</font>
|
|
||||||
provides a means of determining the underlying type of the array. For example <code>remove_bounds<int[4][5]>::type</code>
|
|
||||||
would evaluate to the type <code>int[5]</code>. This example also shows that the
|
|
||||||
number of template parameters in a partial specialisation does not have to match
|
|
||||||
the number in the default template. However, the number of parameters that
|
|
||||||
appear after the class name do have to match the number and type of the
|
|
||||||
parameters in the default template.</p>
|
|
||||||
<h4>Optimised copy</h4>
|
|
||||||
<p>As an example of how the type traits classes can be used, consider the
|
|
||||||
standard library algorithm copy:</p>
|
|
||||||
<pre>template<typename Iter1, typename Iter2>
|
|
||||||
Iter2 copy(Iter1 first, Iter1 last, Iter2 out);</pre>
|
|
||||||
<p>Obviously, there's no problem writing a generic version of copy that works
|
|
||||||
for all iterator types Iter1 and Iter2; however, there are some circumstances
|
|
||||||
when the copy operation can best be performed by a call to <font size="2" face="Courier New">memcpy</font>.
|
|
||||||
In order to implement copy in terms of <font size="2" face="Courier New">memcpy</font>
|
|
||||||
all of the following conditions need to be met:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Both of the iterator types Iter1 and Iter2 must be pointers.</li>
|
|
||||||
<li>Both Iter1 and Iter2 must point to the same type - excluding <font size="2" face="Courier New">const</font>
|
|
||||||
and <font size="2" face="Courier New">volatile</font>-qualifiers.</li>
|
|
||||||
<li>The type pointed to by Iter1 must have a trivial assignment operator.</li>
|
|
||||||
</ul>
|
|
||||||
<p>By trivial assignment operator we mean that the type is either a scalar
|
|
||||||
type[3] or:</p>
|
|
||||||
<ul>
|
|
||||||
<li>The type has no user defined assignment operator.</li>
|
|
||||||
<li>The type does not have any data members that are references.</li>
|
|
||||||
<li>All base classes, and all data member objects must have trivial assignment
|
|
||||||
operators.</li>
|
|
||||||
</ul>
|
|
||||||
<p>If all these conditions are met then a type can be copied using <font size="2" face="Courier New">memcpy</font>
|
|
||||||
rather than using a compiler generated assignment operator. The type-traits
|
|
||||||
library provides a class <i>has_trivial_assign</i>, such that <code>has_trivial_assign<T>::value</code>
|
|
||||||
is true only if T has a trivial assignment operator. This class "just
|
|
||||||
works" for scalar types, but has to be explicitly specialised for
|
|
||||||
class/struct types that also happen to have a trivial assignment operator. In
|
|
||||||
other words if <i>has_trivial_assign</i> gives the wrong answer, it will give
|
|
||||||
the "safe" wrong answer - that trivial assignment is not allowable.</p>
|
|
||||||
<p>The code for an optimised version of copy that uses <font size="2" face="Courier New">memcpy</font>
|
|
||||||
where appropriate is given in listing 1. The code begins by defining a template
|
|
||||||
class <i>copier</i>, that takes a single Boolean template parameter, and has a
|
|
||||||
static template member function <font size="2" face="Courier New">do_copy</font>
|
|
||||||
which performs the generic version of <font size="2">copy</font> (in other words
|
|
||||||
the "slow but safe version"). Following that there is a specialisation
|
|
||||||
for <i>copier<true></i>: again this defines a static template member
|
|
||||||
function <font size="2" face="Courier New">do_copy</font>, but this version uses
|
|
||||||
memcpy to perform an "optimised" copy.</p>
|
|
||||||
<p>In order to complete the implementation, what we need now is a version of
|
|
||||||
copy, that calls <code>copier<true>::do_copy</code> if it is safe to use <font size="2" face="Courier New">memcpy</font>,
|
|
||||||
and otherwise calls <code>copier<false>::do_copy</code> to do a
|
|
||||||
"generic" copy. This is what the version in listing 1 does. To
|
|
||||||
understand how the code works look at the code for <font size="2" face="Courier New">copy</font>
|
|
||||||
and consider first the two typedefs <i>v1_t</i> and <i>v2_t</i>. These use <code>std::iterator_traits<Iter1>::value_type</code>
|
|
||||||
to determine what type the two iterators point to, and then feed the result into
|
|
||||||
another type-traits class <i>remove_cv</i> that removes the top-level
|
|
||||||
const-volatile-qualifiers: this will allow copy to compare the two types without
|
|
||||||
regard to const- or volatile-qualifiers. Next, <font size="2" face="Courier New">copy</font>
|
|
||||||
declares an enumerated value <i>can_opt</i> that will become the template
|
|
||||||
parameter to copier - declaring this here as a constant is really just a
|
|
||||||
convenience - the value could be passed directly to class <font size="2" face="Courier New">copier</font>.
|
|
||||||
The value of <i>can_opt</i> is computed by verifying that all of the following
|
|
||||||
are true:</p>
|
|
||||||
<ul>
|
|
||||||
<li>first that the two iterators point to the same type by using a type-traits
|
|
||||||
class <i>is_same</i>.</li>
|
|
||||||
<li>Then that both iterators are real pointers - using the class <i>is_pointer</i>
|
|
||||||
described above.</li>
|
|
||||||
<li>Finally that the pointed-to types have a trivial assignment operator using
|
|
||||||
<i>has_trivial_assign</i>.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Finally we can use the value of <i>can_opt</i> as the template argument to
|
|
||||||
copier - this version of copy will now adapt to whatever parameters are passed
|
|
||||||
to it, if its possible to use <font size="2" face="Courier New">memcpy</font>,
|
|
||||||
then it will do so, otherwise it will use a generic copy.</p>
|
|
||||||
<h4>Was it worth it?</h4>
|
|
||||||
<p>It has often been repeated in these columns that "premature optimisation
|
|
||||||
is the root of all evil" [4]. So the question must be asked: was our
|
|
||||||
optimisation premature? To put this in perspective the timings for our version
|
|
||||||
of copy compared a conventional generic copy[5] are shown in table 1.</p>
|
|
||||||
<p>Clearly the optimisation makes a difference in this case; but, to be fair,
|
|
||||||
the timings are loaded to exclude cache miss effects - without this accurate
|
|
||||||
comparison between algorithms becomes difficult. However, perhaps we can add a
|
|
||||||
couple of caveats to the premature optimisation rule:</p>
|
|
||||||
<ul>
|
|
||||||
<li>If you use the right algorithm for the job in the first place then
|
|
||||||
optimisation will not be required; in some cases, <font size="2" face="Courier New">memcpy</font>
|
|
||||||
is the right algorithm.</li>
|
|
||||||
<li>If a component is going to be reused in many places by many people then
|
|
||||||
optimisations may well be worthwhile where they would not be so for a single
|
|
||||||
case - in other words, the likelihood that the optimisation will be
|
|
||||||
absolutely necessary somewhere, sometime is that much higher. Just as
|
|
||||||
importantly the perceived value of the stock implementation will be higher:
|
|
||||||
there is no point standardising an algorithm if users reject it on the
|
|
||||||
grounds that there are better, more heavily optimised versions available.</li>
|
|
||||||
</ul>
|
|
||||||
<h4>Table 1: Time taken to copy 1000 elements using copy<const T*, T*>
|
|
||||||
(times in micro-seconds)</h4>
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="529">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="33%">
|
|
||||||
<p align="center">Version</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%">
|
|
||||||
<p align="center">T</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%">
|
|
||||||
<p align="center">Time</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="33%">"Optimised" copy</td>
|
|
||||||
<td valign="top" width="33%">char</td>
|
|
||||||
<td valign="top" width="33%">0.99</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="33%">Conventional copy</td>
|
|
||||||
<td valign="top" width="33%">char</td>
|
|
||||||
<td valign="top" width="33%">8.07</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="33%">"Optimised" copy</td>
|
|
||||||
<td valign="top" width="33%">int</td>
|
|
||||||
<td valign="top" width="33%">2.52</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="33%">Conventional copy</td>
|
|
||||||
<td valign="top" width="33%">int</td>
|
|
||||||
<td valign="top" width="33%">8.02</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<p> </p>
|
|
||||||
<h4>Pair of References</h4>
|
|
||||||
<p>The optimised copy example shows how type traits may be used to perform
|
|
||||||
optimisation decisions at compile-time. Another important usage of type traits
|
|
||||||
is to allow code to compile that otherwise would not do so unless excessive
|
|
||||||
partial specialization is used. This is possible by delegating partial
|
|
||||||
specialization to the type traits classes. Our example for this form of usage is
|
|
||||||
a pair that can hold references [6].</p>
|
|
||||||
<p>First, let us examine the definition of "std::pair", omitting the
|
|
||||||
comparision operators, default constructor, and template copy constructor for
|
|
||||||
simplicity:</p>
|
|
||||||
<pre>template <typename T1, typename T2>
|
|
||||||
struct pair
|
|
||||||
{
|
|
||||||
typedef T1 first_type;
|
|
||||||
typedef T2 second_type;
|
|
||||||
|
|
||||||
T1 first;
|
|
||||||
T2 second;
|
|
||||||
|
|
||||||
pair(const T1 & nfirst, const T2 & nsecond)
|
|
||||||
:first(nfirst), second(nsecond) { }
|
|
||||||
};</pre>
|
|
||||||
<p>Now, this "pair" cannot hold references as it currently stands,
|
|
||||||
because the constructor would require taking a reference to a reference, which
|
|
||||||
is currently illegal [7]. Let us consider what the constructor's parameters
|
|
||||||
would have to be in order to allow "pair" to hold non-reference types,
|
|
||||||
references, and constant references:</p>
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="638">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%">Type of "T1"</td>
|
|
||||||
<td valign="top" width="50%">Type of parameter to initializing constructor</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>T</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>T &</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<p>A little familiarity with the type traits classes allows us to construct a
|
|
||||||
single mapping that allows us to determine the type of parameter from the type
|
|
||||||
of the contained class. The type traits classes provide a transformation "add_reference",
|
|
||||||
which adds a reference to its type, unless it is already a reference.</p>
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="580">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="21%">Type of "T1"</td>
|
|
||||||
<td valign="top" width="27%">Type of "const T1"</td>
|
|
||||||
<td valign="top" width="53%">Type of "add_reference<const
|
|
||||||
T1>::type"</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="21%">
|
|
||||||
<pre>T</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="27%">
|
|
||||||
<pre>const T</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="53%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="21%">
|
|
||||||
<pre>T &</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="27%">
|
|
||||||
<pre>T & [8]</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="53%">
|
|
||||||
<pre>T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="21%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="27%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="53%">
|
|
||||||
<pre>const T &</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<p>This allows us to build a primary template definition for "pair"
|
|
||||||
that can contain non-reference types, reference types, and constant reference
|
|
||||||
types:</p>
|
|
||||||
<pre>template <typename T1, typename T2>
|
|
||||||
struct pair
|
|
||||||
{
|
|
||||||
typedef T1 first_type;
|
|
||||||
typedef T2 second_type;
|
|
||||||
|
|
||||||
T1 first;
|
|
||||||
T2 second;
|
|
||||||
|
|
||||||
pair(boost::add_reference<const T1>::type nfirst,
|
|
||||||
boost::add_reference<const T2>::type nsecond)
|
|
||||||
:first(nfirst), second(nsecond) { }
|
|
||||||
};</pre>
|
|
||||||
<p>Add back in the standard comparision operators, default constructor, and
|
|
||||||
template copy constructor (which are all the same), and you have a std::pair
|
|
||||||
that can hold reference types!</p>
|
|
||||||
<p>This same extension <i>could</i> have been done using partial template
|
|
||||||
specialization of "pair", but to specialize "pair" in this
|
|
||||||
way would require three partial specializations, plus the primary template. Type
|
|
||||||
traits allows us to define a single primary template that adjusts itself
|
|
||||||
auto-magically to any of these partial specializations, instead of a brute-force
|
|
||||||
partial specialization approach. Using type traits in this fashion allows
|
|
||||||
programmers to delegate partial specialization to the type traits classes,
|
|
||||||
resulting in code that is easier to maintain and easier to understand.</p>
|
|
||||||
<h4>Conclusion</h4>
|
|
||||||
<p>We hope that in this article we have been able to give you some idea of what
|
|
||||||
type-traits are all about. A more complete listing of the available classes are
|
|
||||||
in the boost documentation, along with further examples using type traits.
|
|
||||||
Templates have enabled C++ uses to take the advantage of the code reuse that
|
|
||||||
generic programming brings; hopefully this article has shown that generic
|
|
||||||
programming does not have to sink to the lowest common denominator, and that
|
|
||||||
templates can be optimal as well as generic.</p>
|
|
||||||
<h4>Acknowledgements</h4>
|
|
||||||
<p>The authors would like to thank Beman Dawes and Howard Hinnant for their
|
|
||||||
helpful comments when preparing this article.</p>
|
|
||||||
<h4>References</h4>
|
|
||||||
<ol>
|
|
||||||
<li>Nathan C. Myers, C++ Report, June 1995.</li>
|
|
||||||
<li>The type traits library is based upon contributions by Steve Cleary, Beman
|
|
||||||
Dawes, Howard Hinnant and John Maddock: it can be found at www.boost.org.</li>
|
|
||||||
<li>A scalar type is an arithmetic type (i.e. a built-in integer or floating
|
|
||||||
point type), an enumeration type, a pointer, a pointer to member, or a
|
|
||||||
const- or volatile-qualified version of one of these types.</li>
|
|
||||||
<li>This quote is from Donald Knuth, ACM Computing Surveys, December 1974, pg
|
|
||||||
268.</li>
|
|
||||||
<li>The test code is available as part of the boost utility library (see
|
|
||||||
algo_opt_examples.cpp), the code was compiled with gcc 2.95 with all
|
|
||||||
optimisations turned on, tests were conducted on a 400MHz Pentium II machine
|
|
||||||
running Microsoft Windows 98.</li>
|
|
||||||
<li>John Maddock and Howard Hinnant have submitted a "compressed_pair"
|
|
||||||
library to Boost, which uses a technique similar to the one described here
|
|
||||||
to hold references. Their pair also uses type traits to determine if any of
|
|
||||||
the types are empty, and will derive instead of contain to conserve space --
|
|
||||||
hence the name "compressed".</li>
|
|
||||||
<li>This is actually an issue with the C++ Core Language Working Group (issue
|
|
||||||
#106), submitted by Bjarne Stroustrup. The tentative resolution is to allow
|
|
||||||
a "reference to a reference to T" to mean the same thing as a
|
|
||||||
"reference to T", but only in template instantiation, in a method
|
|
||||||
similar to multiple cv-qualifiers.</li>
|
|
||||||
<li>For those of you who are wondering why this shouldn't be const-qualified,
|
|
||||||
remember that references are always implicitly constant (for example, you
|
|
||||||
can't re-assign a reference). Remember also that "const T &"
|
|
||||||
is something completely different. For this reason, cv-qualifiers on
|
|
||||||
template type arguments that are references are ignored.</li>
|
|
||||||
</ol>
|
|
||||||
<h2>Listing 1</h2>
|
|
||||||
<pre>namespace detail{
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
struct copier
|
|
||||||
{
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
static I2 do_copy(I1 first,
|
|
||||||
I1 last, I2 out);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <bool b>
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
I2 copier<b>::do_copy(I1 first,
|
|
||||||
I1 last,
|
|
||||||
I2 out)
|
|
||||||
{
|
|
||||||
while(first != last)
|
|
||||||
{
|
|
||||||
*out = *first;
|
|
||||||
++out;
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct copier<true>
|
|
||||||
{
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
static I2* do_copy(I1* first, I1* last, I2* out)
|
|
||||||
{
|
|
||||||
memcpy(out, first, (last-first)*sizeof(I2));
|
|
||||||
return out+(last-first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename I1, typename I2>
|
|
||||||
inline I2 copy(I1 first, I1 last, I2 out)
|
|
||||||
{
|
|
||||||
typedef typename
|
|
||||||
boost::remove_cv<
|
|
||||||
typename std::iterator_traits<I1>
|
|
||||||
::value_type>::type v1_t;
|
|
||||||
|
|
||||||
typedef typename
|
|
||||||
boost::remove_cv<
|
|
||||||
typename std::iterator_traits<I2>
|
|
||||||
::value_type>::type v2_t;
|
|
||||||
|
|
||||||
enum{ can_opt =
|
|
||||||
boost::is_same<v1_t, v2_t>::value
|
|
||||||
&& boost::is_pointer<I1>::value
|
|
||||||
&& boost::is_pointer<I2>::value
|
|
||||||
&& boost::
|
|
||||||
has_trivial_assign<v1_t>::value
|
|
||||||
};
|
|
||||||
|
|
||||||
return detail::copier<can_opt>::
|
|
||||||
do_copy(first, last, out);
|
|
||||||
}</pre>
|
|
||||||
<hr>
|
|
||||||
<p>© Copyright John Maddock and Steve Cleary, 2000</p>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
620
type_traits.htm
620
type_traits.htm
@ -1,620 +0,0 @@
|
|||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type"
|
|
||||||
content="text/html; charset=iso-8859-1">
|
|
||||||
<meta name="Template"
|
|
||||||
content="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
|
|
||||||
<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
|
|
||||||
<title>Type Traits</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080">
|
|
||||||
|
|
||||||
<h1><img src="../../c++boost.gif" width="276" height="86">Header
|
|
||||||
<<a href="../../boost/detail/type_traits.hpp">boost/type_traits.hpp</a>></h1>
|
|
||||||
|
|
||||||
<p>The contents of <boost/type_traits.hpp> are declared in
|
|
||||||
namespace boost.</p>
|
|
||||||
|
|
||||||
<p>The file <<a href="../../boost/detail/type_traits.hpp">boost/type_traits.hpp</a>>
|
|
||||||
contains various template classes that describe the fundamental
|
|
||||||
properties of a type; each class represents a single type
|
|
||||||
property or a single type transformation. This documentation is
|
|
||||||
divided up into the following sections:</p>
|
|
||||||
|
|
||||||
<pre><a href="#fop">Fundamental type operations</a>
|
|
||||||
<a href="#fp">Fundamental type properties</a>
|
|
||||||
<a href="#misc">Miscellaneous</a>
|
|
||||||
<code> </code><a href="#cv">cv-Qualifiers</a>
|
|
||||||
<code> </code><a href="#ft">Fundamental Types</a>
|
|
||||||
<code> </code><a href="#ct">Compound Types</a>
|
|
||||||
<code> </code><a href="#ot">Object/Scalar Types</a>
|
|
||||||
<a href="#cs">Compiler Support Information</a>
|
|
||||||
<a href="#ec">Example Code</a></pre>
|
|
||||||
|
|
||||||
<h2><a name="fop"></a>Fundamental type operations</h2>
|
|
||||||
|
|
||||||
<p>Usage: "class_name<T>::type" performs
|
|
||||||
indicated transformation on type T.</p>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><p align="center">Expression.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="45%"><p align="center">Description.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">Compiler.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>remove_volatile<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">Creates a type the same as T
|
|
||||||
but with any top level volatile qualifier removed. For
|
|
||||||
example "volatile int" would become "int".</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>remove_const<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">Creates a type the same as T
|
|
||||||
but with any top level const qualifier removed. For
|
|
||||||
example "const int" would become "int".</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>remove_cv<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">Creates a type the same as T
|
|
||||||
but with any top level cv-qualifiers removed. For example
|
|
||||||
"const int" would become "int", and
|
|
||||||
"volatile double" would become "double".</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>remove_reference<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">If T is a reference type
|
|
||||||
then removes the reference, otherwise leaves T unchanged.
|
|
||||||
For example "int&" becomes "int"
|
|
||||||
but "int*" remains unchanged.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>add_reference<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">If T is a reference type
|
|
||||||
then leaves T unchanged, otherwise converts T to a
|
|
||||||
reference type. For example "int&" remains
|
|
||||||
unchanged, but "double" becomes "double&".</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>remove_bounds<T>::type</code></td>
|
|
||||||
<td valign="top" width="45%">If T is an array type then
|
|
||||||
removes the top level array qualifier from T, otherwise
|
|
||||||
leaves T unchanged. For example "int[2][3]"
|
|
||||||
becomes "int[3]".</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h2><a name="fp"></a>Fundamental type properties</h2>
|
|
||||||
|
|
||||||
<p>Usage: "class_name<T>::value" is true if
|
|
||||||
indicated property is true, false otherwise. (Note that class_name<T>::value
|
|
||||||
is always defined as a compile time constant).</p>
|
|
||||||
|
|
||||||
<h3><a name="misc"></a>Miscellaneous</h3>
|
|
||||||
|
|
||||||
<table border="1" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td width="37%"><p align="center">Expression</p>
|
|
||||||
</td>
|
|
||||||
<td width="36%"><p align="center">Description</p>
|
|
||||||
</td>
|
|
||||||
<td width="27%"><p align="center">Compiler</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td width="37%"><div align="center"><center><pre><code>is_same<T,U>::value</code></pre>
|
|
||||||
</center></div></td>
|
|
||||||
<td width="36%"><p align="center">True if T and U are the
|
|
||||||
same type.</p>
|
|
||||||
</td>
|
|
||||||
<td width="27%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td width="37%"><div align="center"><center><pre>is_convertible<T,U>::value</pre>
|
|
||||||
</center></div></td>
|
|
||||||
<td width="36%"><p align="center">True if type T is
|
|
||||||
convertible to type U.</p>
|
|
||||||
</td>
|
|
||||||
<td width="27%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td width="37%"><div align="center"><center><pre>alignment_of<T>::value</pre>
|
|
||||||
</center></div></td>
|
|
||||||
<td width="36%"><p align="center">An integral value
|
|
||||||
representing the minimum alignment requirements of type T
|
|
||||||
(strictly speaking defines a multiple of the type's
|
|
||||||
alignment requirement; for all compilers tested so far
|
|
||||||
however it does return the actual alignment).</p>
|
|
||||||
</td>
|
|
||||||
<td width="27%"> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h3><a name="cv"></a>cv-Qualifiers</h3>
|
|
||||||
|
|
||||||
<p>The following classes determine what cv-qualifiers are present
|
|
||||||
on a type (see 3.93).</p>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="37%"><p align="center">Expression.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="37%"><p align="center">Description.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="27%"><p align="center">Compiler.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="37%"><code>is_const<T>::value</code></td>
|
|
||||||
<td valign="top" width="37%">True if type T is top-level
|
|
||||||
const qualified.</td>
|
|
||||||
<td valign="top" width="27%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="37%"><code>is_volatile<T>::value</code></td>
|
|
||||||
<td valign="top" width="37%">True if type T is top-level
|
|
||||||
volatile qualified.</td>
|
|
||||||
<td valign="top" width="27%"> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h3><a name="ft"></a>Fundamental Types</h3>
|
|
||||||
|
|
||||||
<p>The following will only ever be true for cv-unqualified types;
|
|
||||||
these are closely based on the section 3.9 of the C++ Standard.</p>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><p align="center">Expression.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="45%"><p align="center">Description.</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">Compiler.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_void<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True only if T is void.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_unsigned_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True only if T is one of the
|
|
||||||
standard unsigned integral types (3.9.1 p3) - unsigned
|
|
||||||
char, unsigned short, unsigned int, and unsigned long.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_signed_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True only if T is one of the
|
|
||||||
standard signed integral types (3.9.1 p2) - signed char,
|
|
||||||
short, int, and long.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a standard
|
|
||||||
integral type(3.9.1 p7) - T is either char, wchar_t, bool
|
|
||||||
or either is_standard_signed_integral<T>::value or
|
|
||||||
is_standard_integral<T>::value is true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_float<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is one of the
|
|
||||||
standard floating point types(3.9.1 p8) - float, double
|
|
||||||
or long double.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_arithmetic<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a standard
|
|
||||||
arithmetic type(3.9.1 p8) - implies is_standard_integral
|
|
||||||
or is_standard_float is true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_fundamental<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a standard
|
|
||||||
arithmetic type or if T is void.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_unsigned_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True for compiler specific
|
|
||||||
unsigned integral types.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_signed_integral<T>>:value</code></td>
|
|
||||||
<td valign="top" width="45%">True for compiler specific
|
|
||||||
signed integral types.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_extension_unsigned_integral<T>::value
|
|
||||||
or is_extension_signed_integral<T>::value is true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_float<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True for compiler specific
|
|
||||||
floating point types.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_arithmetic<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_extension_integral<T>::value
|
|
||||||
or is_extension_float<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code> is_extension_fundamental<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_extension_arithmetic<T>::value
|
|
||||||
or is_void<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code> is_unsigned_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_standard_unsigned_integral<T>::value
|
|
||||||
or is_extention_unsigned_integral<T>::value are
|
|
||||||
true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_signed_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_standard_signed_integral<T>::value
|
|
||||||
or is_extention_signed_integral<T>>::value are
|
|
||||||
true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_integral<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_standard_integral<T>::value
|
|
||||||
or is_extention_integral<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_float<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_standard_float<T>::value
|
|
||||||
or is_extention_float<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_arithmetic<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_integral<T>::value
|
|
||||||
or is_float<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_fundamental<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if either is_arithmetic<T>::value
|
|
||||||
or is_void<T>::value are true.</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h3><a name="ct"></a>Compound Types</h3>
|
|
||||||
|
|
||||||
<p>The following will only ever be true for cv-unqualified types,
|
|
||||||
as defined by the Standard. </p>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><p align="center">Expression</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="45%"><p align="center">Description</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">Compiler</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_array<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is an array type.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_pointer<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a regular
|
|
||||||
pointer type - including function pointers - but
|
|
||||||
excluding pointers to member functions (3.9.2 p1 and 8.3.1).</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_member_pointer<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a pointer to a
|
|
||||||
non-static class member (3.9.2 p1 and 8.3.1).</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_reference<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a reference
|
|
||||||
type (3.9.2 p1 and 8.3.2).</td>
|
|
||||||
<td valign="top" width="33%"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_class<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a class or
|
|
||||||
struct type.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_union<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a union type.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">C</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_enum<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is an enumerator
|
|
||||||
type.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">C</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_compound<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is any of the
|
|
||||||
above compound types.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h3><a name="ot"></a>Object/Scalar Types</h3>
|
|
||||||
|
|
||||||
<p>The following ignore any top level cv-qualifiers: if <code>class_name<T>::value</code>
|
|
||||||
is true then <code>class_name<cv-qualified-T>::value</code>
|
|
||||||
will also be true.</p>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="7" cellspacing="1" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><p align="center">Expression</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="45%"><p align="center">Description</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">Compiler</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_object<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is not a reference
|
|
||||||
type, or a (possibly cv-qualified) void type.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_standard_scalar<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a standard
|
|
||||||
arithmetic type, an enumerated type, a pointer or a
|
|
||||||
member pointer.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_extension_scalar<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is an extentions
|
|
||||||
arithmetic type, an enumerated type, a pointer or a
|
|
||||||
member pointer.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_scalar<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is an arithmetic
|
|
||||||
type, an enumerated type, a pointer or a member pointer.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_POD<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is a "Plain
|
|
||||||
Old Data" type (see 3.9 p2&p3). Note that
|
|
||||||
although this requires compiler support to be correct in
|
|
||||||
all cases, if T is a scalar or an array of scalars then
|
|
||||||
we can correctly define T as a POD.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PC</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>is_empty<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T is an empty struct
|
|
||||||
or class. If the compiler implements the "zero sized
|
|
||||||
empty base classes" optimisation, then is_empty will
|
|
||||||
correctly guess whether T is empty. Relies upon is_class
|
|
||||||
to determine whether T is a class type. Screens out enum
|
|
||||||
types by using is_convertible<T,int>, this means
|
|
||||||
that empty classes that overload operator int(), will not
|
|
||||||
be classified as empty.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PCD</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>has_trivial_constructor<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T has a trivial
|
|
||||||
default constructor - that is T() is equivalent to memset.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PC</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>has_trivial_copy<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T has a trivial copy
|
|
||||||
constructor - that is T(const T&) is equivalent to
|
|
||||||
memcpy.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PC</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>has_trivial_assign<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T has a trivial
|
|
||||||
assignment operator - that is if T::operator=(const T&)
|
|
||||||
is equivalent to memcpy.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PC</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="45%"><code>has_trivial_destructor<T>::value</code></td>
|
|
||||||
<td valign="top" width="45%">True if T has a trivial
|
|
||||||
destructor - that is if T::~T() has no effect.</td>
|
|
||||||
<td valign="top" width="33%"><p align="center">PC</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<h2><a name="cs"></a>Compiler Support Information</h2>
|
|
||||||
|
|
||||||
<p>The legends used in the tables above have the following
|
|
||||||
meanings:</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="7" cellspacing="0" width="480">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><p align="center">P</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="90%">Denotes that the class
|
|
||||||
requires support for partial specialisation of class
|
|
||||||
templates to work correctly.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><p align="center">C</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="90%">Denotes that direct compiler
|
|
||||||
support for that traits class is required.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><p align="center">D</p>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="90%">Denotes that the traits
|
|
||||||
class is dependent upon a class that requires direct
|
|
||||||
compiler support.</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<p>For those classes that are marked with a D or C, if compiler
|
|
||||||
support is not provided, this type trait may return "false"
|
|
||||||
when the correct value is actually "true". The single
|
|
||||||
exception to this rule is "is_class", which attempts to
|
|
||||||
guess whether or not T is really a class, and may return "true"
|
|
||||||
when the correct value is actually "false". This can
|
|
||||||
happen if: T is a union, T is an enum, or T is a compiler-supplied
|
|
||||||
scalar type that is not specialised for in these type traits.</p>
|
|
||||||
|
|
||||||
<p><i>If there is no compiler support</i>, to ensure that these
|
|
||||||
traits <i>always</i> return the correct values, specialise 'is_enum'
|
|
||||||
for each user-defined enumeration type, 'is_union' for each user-defined
|
|
||||||
union type, 'is_empty' for each user-defined empty composite type,
|
|
||||||
and 'is_POD' for each user-defined POD type. The 'has_*' traits
|
|
||||||
should also be specialized if the user-defined type has those
|
|
||||||
traits and is <i>not</i> a POD.</p>
|
|
||||||
|
|
||||||
<p>The following rules are automatically enforced:</p>
|
|
||||||
|
|
||||||
<p>is_enum implies is_POD</p>
|
|
||||||
|
|
||||||
<p>is_POD implies has_*</p>
|
|
||||||
|
|
||||||
<p>This means, for example, if you have an empty POD-struct, just
|
|
||||||
specialize is_empty and is_POD, which will cause all the has_* to
|
|
||||||
also return true.</p>
|
|
||||||
|
|
||||||
<h2><a name="ec"></a>Example code</h2>
|
|
||||||
|
|
||||||
<p>Type-traits comes with two sample programs: <a
|
|
||||||
href="type_traits_test.cpp">type_traits_test.cpp</a> tests the
|
|
||||||
type traits classes - mostly this is a test of your compiler's
|
|
||||||
support for the concepts used in the type traits implementation,
|
|
||||||
while <a href="algo_opt_examples.cpp">algo_opt_examples.cpp</a>
|
|
||||||
uses the type traits classes to "optimise" some
|
|
||||||
familiar standard library algorithms.</p>
|
|
||||||
|
|
||||||
<p>There are four algorithm examples in algo_opt_examples.cpp:</p>
|
|
||||||
|
|
||||||
<table border="0" cellpadding="7" cellspacing="0" width="638">
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><pre>opt::copy</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">If the copy operation can be
|
|
||||||
performed using memcpy then does so, otherwise uses a
|
|
||||||
regular element by element copy (<i>c.f.</i> std::copy).</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><pre>opt::fill</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">If the fill operation can be
|
|
||||||
performed by memset, then does so, otherwise uses a
|
|
||||||
regular element by element assign. Also uses call_traits
|
|
||||||
to optimise how the parameters can be passed (<i>c.f.</i>
|
|
||||||
std::fill).</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><pre>opt::destroy_array</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">If the type in the array has
|
|
||||||
a trivial destructor then does nothing, otherwise calls
|
|
||||||
destructors for all elements in the array - this
|
|
||||||
algorithm is the reverse of std::uninitialized_copy / std::uninitialized_fill.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td valign="top" width="50%"><pre>opt::iter_swap</pre>
|
|
||||||
</td>
|
|
||||||
<td valign="top" width="50%">Determines whether the
|
|
||||||
iterator is a proxy-iterator: if it is then does a "slow
|
|
||||||
and safe" swap, otherwise calls std::swap on the
|
|
||||||
assumption that std::swap may be specialised for the
|
|
||||||
iterated type.</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<p>Revised 01 September 2000</p>
|
|
||||||
|
|
||||||
<p>© Copyright boost.org 2000. Permission to copy, use, modify,
|
|
||||||
sell and distribute this document is granted provided this
|
|
||||||
copyright notice appears in all copies. This document is provided
|
|
||||||
"as is" without express or implied warranty, and with
|
|
||||||
no claim as to its suitability for any purpose.</p>
|
|
||||||
|
|
||||||
<p>Based on contributions by Steve Cleary, Beman Dawes, Howard
|
|
||||||
Hinnant and John Maddock.</p>
|
|
||||||
|
|
||||||
<p>Maintained by <a href="mailto:John_Maddock@compuserve.com">John
|
|
||||||
Maddock</a>, the latest version of this file can be found at <a
|
|
||||||
href="http://www.boost.org/">www.boost.org</a>, and the boost
|
|
||||||
discussion list at <a href="http://www.egroups.com/list/boost">www.egroups.com/list/boost</a>.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,672 +0,0 @@
|
|||||||
// (C) Copyright Steve Cleary, Beman Dawes, Howard Hinnant & John Maddock 2000.
|
|
||||||
// Permission to copy, use, modify, sell and
|
|
||||||
// distribute this software is granted provided this copyright notice appears
|
|
||||||
// in all copies. This software is provided "as is" without express or implied
|
|
||||||
// warranty, and with no claim as to its suitability for any purpose.
|
|
||||||
|
|
||||||
// standalone test program for <boost/type_traits.hpp>
|
|
||||||
|
|
||||||
/* Release notes:
|
|
||||||
31 Jan 2001:
|
|
||||||
Added a test for is_array using a const array and a test for
|
|
||||||
is_convertible with a user-defined implicit conversion. Changed
|
|
||||||
signature of main() so that this program will link under
|
|
||||||
MSVC. (Jeremy Siek)
|
|
||||||
20 Jan 2001:
|
|
||||||
Suppress an expected warning for MSVC
|
|
||||||
Added a test to prove that we can use void with is_same<>
|
|
||||||
Removed "press any key to exit" as it interferes with testing in large
|
|
||||||
batches.
|
|
||||||
(David Abahams)
|
|
||||||
31st July 2000:
|
|
||||||
Added extra tests for is_empty, is_convertible, alignment_of.
|
|
||||||
23rd July 2000:
|
|
||||||
Removed all call_traits tests to call_traits_test.cpp
|
|
||||||
Removed all compressed_pair tests to compressed_pair_tests.cpp
|
|
||||||
Improved tests macros
|
|
||||||
Tidied up specialistions of type_types classes for test cases. */
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#include <boost/type_traits.hpp>
|
|
||||||
#include <boost/utility.hpp>
|
|
||||||
#include "type_traits_test.hpp"
|
|
||||||
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
// Since there is no compiler support, we should specialize:
|
|
||||||
// is_enum for all enumerations (is_enum implies is_POD)
|
|
||||||
// is_union for all unions
|
|
||||||
// is_empty for all empty composites
|
|
||||||
// is_POD for all PODs (except enums) (is_POD implies has_*)
|
|
||||||
// has_* for any UDT that has that trait and is not POD
|
|
||||||
|
|
||||||
enum enum_UDT{ one, two, three };
|
|
||||||
struct UDT
|
|
||||||
{
|
|
||||||
UDT();
|
|
||||||
~UDT();
|
|
||||||
UDT(const UDT&);
|
|
||||||
UDT& operator=(const UDT&);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
void f1();
|
|
||||||
int f2();
|
|
||||||
int f3(int);
|
|
||||||
int f4(int, float);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct POD_UDT { int x; };
|
|
||||||
struct empty_UDT{ ~empty_UDT(){}; };
|
|
||||||
struct empty_POD_UDT{};
|
|
||||||
union union_UDT
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
double y;
|
|
||||||
~union_UDT();
|
|
||||||
};
|
|
||||||
union POD_union_UDT
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
double y;
|
|
||||||
};
|
|
||||||
union empty_union_UDT
|
|
||||||
{
|
|
||||||
~empty_union_UDT();
|
|
||||||
};
|
|
||||||
union empty_POD_union_UDT{};
|
|
||||||
#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
|
|
||||||
namespace boost {
|
|
||||||
template <> struct is_enum<enum_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_POD<POD_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
// this type is not POD, so we have to specialize the has_* individually
|
|
||||||
template <> struct has_trivial_constructor<empty_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct has_trivial_copy<empty_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct has_trivial_assign<empty_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_POD<empty_POD_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_union<union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_union<POD_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_POD<POD_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_union<empty_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
// this type is not POD, so we have to specialize the has_* individually
|
|
||||||
template <> struct has_trivial_constructor<empty_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct has_trivial_copy<empty_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct has_trivial_assign<empty_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_union<empty_POD_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
template <> struct is_POD<empty_POD_union_UDT>
|
|
||||||
{ static const bool value = true; };
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
namespace boost {
|
|
||||||
template <> struct is_enum<enum_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_POD<POD_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
// this type is not POD, so we have to specialize the has_* individually
|
|
||||||
template <> struct has_trivial_constructor<empty_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct has_trivial_copy<empty_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct has_trivial_assign<empty_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_POD<empty_POD_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_union<union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_union<POD_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_POD<POD_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_union<empty_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
// this type is not POD, so we have to specialize the has_* individually
|
|
||||||
template <> struct has_trivial_constructor<empty_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct has_trivial_copy<empty_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct has_trivial_assign<empty_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_union<empty_POD_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
template <> struct is_POD<empty_POD_union_UDT>
|
|
||||||
{ enum{ value = true }; };
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Base { };
|
|
||||||
|
|
||||||
class Derived : public Base { };
|
|
||||||
|
|
||||||
class NonDerived { };
|
|
||||||
|
|
||||||
enum enum1
|
|
||||||
{
|
|
||||||
one_,two_
|
|
||||||
};
|
|
||||||
|
|
||||||
enum enum2
|
|
||||||
{
|
|
||||||
three_,four_
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VB
|
|
||||||
{
|
|
||||||
virtual ~VB(){};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VD : VB
|
|
||||||
{
|
|
||||||
~VD(){};
|
|
||||||
};
|
|
||||||
//
|
|
||||||
// struct non_pointer:
|
|
||||||
// used to verify that is_pointer does not return
|
|
||||||
// true for class types that implement operator void*()
|
|
||||||
//
|
|
||||||
struct non_pointer
|
|
||||||
{
|
|
||||||
operator void*(){return this;}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
// struct non_empty:
|
|
||||||
// used to verify that is_empty does not emit
|
|
||||||
// spurious warnings or errors.
|
|
||||||
//
|
|
||||||
struct non_empty : boost::noncopyable
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct implicitly_convertible_to_int {
|
|
||||||
operator int() { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Steve: All comments that I (Steve Cleary) have added below are prefixed with
|
|
||||||
// "Steve:" The failures that BCB4 has on the tests are due to Borland's
|
|
||||||
// not considering cv-qual's as a part of the type -- they are considered
|
|
||||||
// compiler hints only. These failures should be fixed before long.
|
|
||||||
|
|
||||||
int main(int, char*[])
|
|
||||||
{
|
|
||||||
std::cout << "Checking type operations..." << std::endl << std::endl;
|
|
||||||
|
|
||||||
// cv-qualifiers applied to reference types should have no effect
|
|
||||||
// declare these here for later use with is_reference and remove_reference:
|
|
||||||
typedef int& r_type;
|
|
||||||
#ifdef BOOST_MSVC
|
|
||||||
# pragma warning(push)
|
|
||||||
# pragma warning(disable:4181) // qualifier applied to reference type ignored
|
|
||||||
#endif
|
|
||||||
typedef const r_type cr_type;
|
|
||||||
#ifdef BOOST_MSVC
|
|
||||||
# pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
type_test(int, remove_reference<int>::type)
|
|
||||||
type_test(const int, remove_reference<const int>::type)
|
|
||||||
type_test(int, remove_reference<int&>::type)
|
|
||||||
type_test(const int, remove_reference<const int&>::type)
|
|
||||||
type_test(volatile int, remove_reference<volatile int&>::type)
|
|
||||||
type_test(int, remove_reference<cr_type>::type)
|
|
||||||
|
|
||||||
type_test(int, remove_const<const int>::type)
|
|
||||||
// Steve: fails on BCB4
|
|
||||||
type_test(volatile int, remove_const<volatile int>::type)
|
|
||||||
// Steve: fails on BCB4
|
|
||||||
type_test(volatile int, remove_const<const volatile int>::type)
|
|
||||||
type_test(int, remove_const<int>::type)
|
|
||||||
type_test(int*, remove_const<int* const>::type)
|
|
||||||
type_test(int, remove_volatile<volatile int>::type)
|
|
||||||
// Steve: fails on BCB4
|
|
||||||
type_test(const int, remove_volatile<const int>::type)
|
|
||||||
// Steve: fails on BCB4
|
|
||||||
type_test(const int, remove_volatile<const volatile int>::type)
|
|
||||||
type_test(int, remove_volatile<int>::type)
|
|
||||||
type_test(int*, remove_volatile<int* volatile>::type)
|
|
||||||
type_test(int, remove_cv<volatile int>::type)
|
|
||||||
type_test(int, remove_cv<const int>::type)
|
|
||||||
type_test(int, remove_cv<const volatile int>::type)
|
|
||||||
type_test(int, remove_cv<int>::type)
|
|
||||||
type_test(int*, remove_cv<int* volatile>::type)
|
|
||||||
type_test(int*, remove_cv<int* const>::type)
|
|
||||||
type_test(int*, remove_cv<int* const volatile>::type)
|
|
||||||
type_test(const int *, remove_cv<const int * const>::type)
|
|
||||||
type_test(int, remove_bounds<int>::type)
|
|
||||||
type_test(int*, remove_bounds<int*>::type)
|
|
||||||
type_test(int, remove_bounds<int[3]>::type)
|
|
||||||
type_test(int[3], remove_bounds<int[2][3]>::type)
|
|
||||||
|
|
||||||
std::cout << std::endl << "Checking type properties..." << std::endl << std::endl;
|
|
||||||
|
|
||||||
value_test(true, (is_same<void, void>::value))
|
|
||||||
value_test(false, (is_same<int, void>::value))
|
|
||||||
value_test(false, (is_same<void, int>::value))
|
|
||||||
value_test(true, (is_same<int, int>::value))
|
|
||||||
value_test(false, (is_same<int, const int>::value))
|
|
||||||
value_test(false, (is_same<int, int&>::value))
|
|
||||||
value_test(false, (is_same<int*, const int*>::value))
|
|
||||||
value_test(false, (is_same<int*, int*const>::value))
|
|
||||||
value_test(false, (is_same<int, int[2]>::value))
|
|
||||||
value_test(false, (is_same<int*, int[2]>::value))
|
|
||||||
value_test(false, (is_same<int[4], int[2]>::value))
|
|
||||||
|
|
||||||
value_test(false, is_const<int>::value)
|
|
||||||
value_test(true, is_const<const int>::value)
|
|
||||||
value_test(false, is_const<volatile int>::value)
|
|
||||||
value_test(true, is_const<const volatile int>::value)
|
|
||||||
|
|
||||||
value_test(false, is_volatile<int>::value)
|
|
||||||
value_test(false, is_volatile<const int>::value)
|
|
||||||
value_test(true, is_volatile<volatile int>::value)
|
|
||||||
value_test(true, is_volatile<const volatile int>::value)
|
|
||||||
|
|
||||||
value_test(true, is_void<void>::value)
|
|
||||||
// Steve: fails on BCB4
|
|
||||||
// JM: but looks as though it should according to [3.9.3p1]?
|
|
||||||
//value_test(false, is_void<const void>::value)
|
|
||||||
value_test(false, is_void<int>::value)
|
|
||||||
|
|
||||||
value_test(false, is_standard_unsigned_integral<UDT>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<void>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<bool>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<char>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<signed char>::value)
|
|
||||||
value_test(true, is_standard_unsigned_integral<unsigned char>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<wchar_t>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<short>::value)
|
|
||||||
value_test(true, is_standard_unsigned_integral<unsigned short>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<int>::value)
|
|
||||||
value_test(true, is_standard_unsigned_integral<unsigned int>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<long>::value)
|
|
||||||
value_test(true, is_standard_unsigned_integral<unsigned long>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<float>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<double>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<long double>::value)
|
|
||||||
#ifdef ULLONG_MAX
|
|
||||||
value_test(false, is_standard_unsigned_integral<long long>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<unsigned long long>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
|
||||||
value_test(false, is_standard_unsigned_integral<__int64>::value)
|
|
||||||
value_test(false, is_standard_unsigned_integral<unsigned __int64>::value)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
value_test(false, is_standard_signed_integral<UDT>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<void>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<bool>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<char>::value)
|
|
||||||
value_test(true, is_standard_signed_integral<signed char>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned char>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<wchar_t>::value)
|
|
||||||
value_test(true, is_standard_signed_integral<short>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned short>::value)
|
|
||||||
value_test(true, is_standard_signed_integral<int>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned int>::value)
|
|
||||||
value_test(true, is_standard_signed_integral<long>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned long>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<float>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<double>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<long double>::value)
|
|
||||||
#ifdef ULLONG_MAX
|
|
||||||
value_test(false, is_standard_signed_integral<long long>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned long long>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
|
||||||
value_test(false, is_standard_signed_integral<__int64>::value)
|
|
||||||
value_test(false, is_standard_signed_integral<unsigned __int64>::value)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
value_test(false, is_standard_arithmetic<UDT>::value)
|
|
||||||
value_test(false, is_standard_arithmetic<void>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<bool>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<char>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<signed char>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<unsigned char>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<wchar_t>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<short>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<unsigned short>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<int>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<unsigned int>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<long>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<unsigned long>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<float>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<double>::value)
|
|
||||||
value_test(true, is_standard_arithmetic<long double>::value)
|
|
||||||
#ifdef ULLONG_MAX
|
|
||||||
value_test(false, is_standard_arithmetic<long long>::value)
|
|
||||||
value_test(false, is_standard_arithmetic<unsigned long long>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
|
||||||
value_test(false, is_standard_arithmetic<__int64>::value)
|
|
||||||
value_test(false, is_standard_arithmetic<unsigned __int64>::value)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
value_test(false, is_standard_fundamental<UDT>::value)
|
|
||||||
value_test(true, is_standard_fundamental<void>::value)
|
|
||||||
value_test(true, is_standard_fundamental<bool>::value)
|
|
||||||
value_test(true, is_standard_fundamental<char>::value)
|
|
||||||
value_test(true, is_standard_fundamental<signed char>::value)
|
|
||||||
value_test(true, is_standard_fundamental<unsigned char>::value)
|
|
||||||
value_test(true, is_standard_fundamental<wchar_t>::value)
|
|
||||||
value_test(true, is_standard_fundamental<short>::value)
|
|
||||||
value_test(true, is_standard_fundamental<unsigned short>::value)
|
|
||||||
value_test(true, is_standard_fundamental<int>::value)
|
|
||||||
value_test(true, is_standard_fundamental<unsigned int>::value)
|
|
||||||
value_test(true, is_standard_fundamental<long>::value)
|
|
||||||
value_test(true, is_standard_fundamental<unsigned long>::value)
|
|
||||||
value_test(true, is_standard_fundamental<float>::value)
|
|
||||||
value_test(true, is_standard_fundamental<double>::value)
|
|
||||||
value_test(true, is_standard_fundamental<long double>::value)
|
|
||||||
#ifdef ULLONG_MAX
|
|
||||||
value_test(false, is_standard_fundamental<long long>::value)
|
|
||||||
value_test(false, is_standard_fundamental<unsigned long long>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
|
||||||
value_test(false, is_standard_fundamental<__int64>::value)
|
|
||||||
value_test(false, is_standard_fundamental<unsigned __int64>::value)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
value_test(false, is_arithmetic<UDT>::value)
|
|
||||||
value_test(true, is_arithmetic<char>::value)
|
|
||||||
value_test(true, is_arithmetic<signed char>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned char>::value)
|
|
||||||
value_test(true, is_arithmetic<wchar_t>::value)
|
|
||||||
value_test(true, is_arithmetic<short>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned short>::value)
|
|
||||||
value_test(true, is_arithmetic<int>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned int>::value)
|
|
||||||
value_test(true, is_arithmetic<long>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned long>::value)
|
|
||||||
value_test(true, is_arithmetic<float>::value)
|
|
||||||
value_test(true, is_arithmetic<double>::value)
|
|
||||||
value_test(true, is_arithmetic<long double>::value)
|
|
||||||
value_test(true, is_arithmetic<bool>::value)
|
|
||||||
#ifdef ULLONG_MAX
|
|
||||||
value_test(true, is_arithmetic<long long>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned long long>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
|
||||||
value_test(true, is_arithmetic<__int64>::value)
|
|
||||||
value_test(true, is_arithmetic<unsigned __int64>::value)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
value_test(false, is_array<int>::value)
|
|
||||||
value_test(false, is_array<int*>::value)
|
|
||||||
value_test(false, is_array<const int*>::value)
|
|
||||||
value_test(false, is_array<const volatile int*>::value)
|
|
||||||
value_test(true, is_array<int[2]>::value)
|
|
||||||
value_test(true, is_array<const int[2]>::value)
|
|
||||||
value_test(true, is_array<const volatile int[2]>::value)
|
|
||||||
value_test(true, is_array<int[2][3]>::value)
|
|
||||||
value_test(true, is_array<UDT[2]>::value)
|
|
||||||
value_test(false, is_array<int(&)[2]>::value)
|
|
||||||
value_test(true, is_array<const int[2]>::value)
|
|
||||||
|
|
||||||
typedef void(*f1)();
|
|
||||||
typedef int(*f2)(int);
|
|
||||||
typedef int(*f3)(int, bool);
|
|
||||||
typedef void (UDT::*mf1)();
|
|
||||||
typedef int (UDT::*mf2)();
|
|
||||||
typedef int (UDT::*mf3)(int);
|
|
||||||
typedef int (UDT::*mf4)(int, float);
|
|
||||||
|
|
||||||
value_test(false, is_const<f1>::value)
|
|
||||||
value_test(false, is_reference<f1>::value)
|
|
||||||
value_test(false, is_array<f1>::value)
|
|
||||||
value_test(false, is_pointer<int>::value)
|
|
||||||
value_test(false, is_pointer<int&>::value)
|
|
||||||
value_test(true, is_pointer<int*>::value)
|
|
||||||
value_test(true, is_pointer<const int*>::value)
|
|
||||||
value_test(true, is_pointer<volatile int*>::value)
|
|
||||||
value_test(true, is_pointer<non_pointer*>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3, 3.9.3p1
|
|
||||||
value_test(false, is_pointer<int*const>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3, 3.9.3p1
|
|
||||||
value_test(false, is_pointer<int*volatile>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3, 3.9.3p1
|
|
||||||
value_test(false, is_pointer<int*const volatile>::value)
|
|
||||||
// JM 02 Oct 2000:
|
|
||||||
value_test(false, is_pointer<non_pointer>::value)
|
|
||||||
value_test(false, is_pointer<int*&>::value)
|
|
||||||
value_test(false, is_pointer<int(&)[2]>::value)
|
|
||||||
value_test(false, is_pointer<int[2]>::value)
|
|
||||||
value_test(false, is_pointer<char[sizeof(void*)]>::value)
|
|
||||||
|
|
||||||
value_test(true, is_pointer<f1>::value)
|
|
||||||
value_test(true, is_pointer<f2>::value)
|
|
||||||
value_test(true, is_pointer<f3>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3
|
|
||||||
value_test(false, is_pointer<mf1>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3
|
|
||||||
value_test(false, is_pointer<mf2>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3
|
|
||||||
value_test(false, is_pointer<mf3>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9.2p3
|
|
||||||
value_test(false, is_pointer<mf4>::value)
|
|
||||||
|
|
||||||
value_test(false, is_reference<bool>::value)
|
|
||||||
value_test(true, is_reference<int&>::value)
|
|
||||||
value_test(true, is_reference<const int&>::value)
|
|
||||||
value_test(true, is_reference<volatile int &>::value)
|
|
||||||
value_test(true, is_reference<r_type>::value)
|
|
||||||
value_test(true, is_reference<cr_type>::value)
|
|
||||||
value_test(true, is_reference<const UDT&>::value)
|
|
||||||
|
|
||||||
value_test(false, is_class<int>::value)
|
|
||||||
value_test(false, is_class<const int>::value)
|
|
||||||
value_test(false, is_class<volatile int>::value)
|
|
||||||
value_test(false, is_class<int*>::value)
|
|
||||||
value_test(false, is_class<int* const>::value)
|
|
||||||
value_test(false, is_class<int[2]>::value)
|
|
||||||
value_test(false, is_class<int&>::value)
|
|
||||||
value_test(false, is_class<mf4>::value)
|
|
||||||
value_test(false, is_class<f1>::value)
|
|
||||||
value_test(false, is_class<enum_UDT>::value)
|
|
||||||
value_test(true, is_class<UDT>::value)
|
|
||||||
value_test(true, is_class<UDT const>::value)
|
|
||||||
value_test(true, is_class<UDT volatile>::value)
|
|
||||||
value_test(true, is_class<empty_UDT>::value)
|
|
||||||
value_test(true, is_class<std::iostream>::value)
|
|
||||||
value_test(false, is_class<UDT*>::value)
|
|
||||||
value_test(false, is_class<UDT[2]>::value)
|
|
||||||
value_test(false, is_class<UDT&>::value)
|
|
||||||
|
|
||||||
value_test(true, is_object<int>::value)
|
|
||||||
value_test(true, is_object<UDT>::value)
|
|
||||||
value_test(false, is_object<int&>::value)
|
|
||||||
value_test(false, is_object<void>::value)
|
|
||||||
value_test(true, is_standard_scalar<int>::value)
|
|
||||||
value_test(true, is_extension_scalar<void*>::value)
|
|
||||||
|
|
||||||
value_test(false, is_enum<int>::value)
|
|
||||||
value_test(true, is_enum<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(false, is_member_pointer<f1>::value)
|
|
||||||
value_test(false, is_member_pointer<f2>::value)
|
|
||||||
value_test(false, is_member_pointer<f3>::value)
|
|
||||||
value_test(true, is_member_pointer<mf1>::value)
|
|
||||||
value_test(true, is_member_pointer<mf2>::value)
|
|
||||||
value_test(true, is_member_pointer<mf3>::value)
|
|
||||||
value_test(true, is_member_pointer<mf4>::value)
|
|
||||||
|
|
||||||
value_test(false, is_empty<int>::value)
|
|
||||||
value_test(false, is_empty<int*>::value)
|
|
||||||
value_test(false, is_empty<int&>::value)
|
|
||||||
#if defined(__MWERKS__) || defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
|
|
||||||
// apparent compiler bug causes this to fail to compile:
|
|
||||||
value_fail(false, is_empty<int[2]>::value)
|
|
||||||
#else
|
|
||||||
value_test(false, is_empty<int[2]>::value)
|
|
||||||
#endif
|
|
||||||
#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
|
|
||||||
value_fail(false, is_empty<f1>::value)
|
|
||||||
#else
|
|
||||||
value_test(false, is_empty<f1>::value)
|
|
||||||
#endif
|
|
||||||
value_test(false, is_empty<mf1>::value)
|
|
||||||
value_test(false, is_empty<UDT>::value)
|
|
||||||
value_test(true, is_empty<empty_UDT>::value)
|
|
||||||
value_test(true, is_empty<empty_POD_UDT>::value)
|
|
||||||
#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
|
|
||||||
value_fail(true, is_empty<empty_union_UDT>::value)
|
|
||||||
#else
|
|
||||||
value_test(true, is_empty<empty_union_UDT>::value)
|
|
||||||
#endif
|
|
||||||
value_test(false, is_empty<enum_UDT>::value)
|
|
||||||
value_test(true, is_empty<boost::noncopyable>::value)
|
|
||||||
value_test(false, is_empty<non_empty>::value)
|
|
||||||
|
|
||||||
value_test(true, has_trivial_constructor<int>::value)
|
|
||||||
value_test(true, has_trivial_constructor<int*>::value)
|
|
||||||
value_test(true, has_trivial_constructor<int*const>::value)
|
|
||||||
value_test(true, has_trivial_constructor<const int>::value)
|
|
||||||
value_test(true, has_trivial_constructor<volatile int>::value)
|
|
||||||
value_test(true, has_trivial_constructor<int[2]>::value)
|
|
||||||
value_test(true, has_trivial_constructor<int[3][2]>::value)
|
|
||||||
value_test(true, has_trivial_constructor<int[2][4][5][6][3]>::value)
|
|
||||||
value_test(true, has_trivial_constructor<f1>::value)
|
|
||||||
value_test(true, has_trivial_constructor<mf2>::value)
|
|
||||||
value_test(false, has_trivial_constructor<UDT>::value)
|
|
||||||
value_test(true, has_trivial_constructor<empty_UDT>::value)
|
|
||||||
value_test(true, has_trivial_constructor<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(true, has_trivial_copy<int>::value)
|
|
||||||
value_test(true, has_trivial_copy<int*>::value)
|
|
||||||
value_test(true, has_trivial_copy<int*const>::value)
|
|
||||||
value_test(true, has_trivial_copy<const int>::value)
|
|
||||||
// Steve: was 'false' -- should be 'true' via 3.9p3, 3.9p10
|
|
||||||
value_test(true, has_trivial_copy<volatile int>::value)
|
|
||||||
value_test(true, has_trivial_copy<int[2]>::value)
|
|
||||||
value_test(true, has_trivial_copy<int[3][2]>::value)
|
|
||||||
value_test(true, has_trivial_copy<int[2][4][5][6][3]>::value)
|
|
||||||
value_test(true, has_trivial_copy<f1>::value)
|
|
||||||
value_test(true, has_trivial_copy<mf2>::value)
|
|
||||||
value_test(false, has_trivial_copy<UDT>::value)
|
|
||||||
value_test(true, has_trivial_copy<empty_UDT>::value)
|
|
||||||
value_test(true, has_trivial_copy<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(true, has_trivial_assign<int>::value)
|
|
||||||
value_test(true, has_trivial_assign<int*>::value)
|
|
||||||
value_test(true, has_trivial_assign<int*const>::value)
|
|
||||||
value_test(true, has_trivial_assign<const int>::value)
|
|
||||||
// Steve: was 'false' -- should be 'true' via 3.9p3, 3.9p10
|
|
||||||
value_test(true, has_trivial_assign<volatile int>::value)
|
|
||||||
value_test(true, has_trivial_assign<int[2]>::value)
|
|
||||||
value_test(true, has_trivial_assign<int[3][2]>::value)
|
|
||||||
value_test(true, has_trivial_assign<int[2][4][5][6][3]>::value)
|
|
||||||
value_test(true, has_trivial_assign<f1>::value)
|
|
||||||
value_test(true, has_trivial_assign<mf2>::value)
|
|
||||||
value_test(false, has_trivial_assign<UDT>::value)
|
|
||||||
value_test(true, has_trivial_assign<empty_UDT>::value)
|
|
||||||
value_test(true, has_trivial_assign<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(true, has_trivial_destructor<int>::value)
|
|
||||||
value_test(true, has_trivial_destructor<int*>::value)
|
|
||||||
value_test(true, has_trivial_destructor<int*const>::value)
|
|
||||||
value_test(true, has_trivial_destructor<const int>::value)
|
|
||||||
value_test(true, has_trivial_destructor<volatile int>::value)
|
|
||||||
value_test(true, has_trivial_destructor<int[2]>::value)
|
|
||||||
value_test(true, has_trivial_destructor<int[3][2]>::value)
|
|
||||||
value_test(true, has_trivial_destructor<int[2][4][5][6][3]>::value)
|
|
||||||
value_test(true, has_trivial_destructor<f1>::value)
|
|
||||||
value_test(true, has_trivial_destructor<mf2>::value)
|
|
||||||
value_test(false, has_trivial_destructor<UDT>::value)
|
|
||||||
value_test(false, has_trivial_destructor<empty_UDT>::value)
|
|
||||||
value_test(true, has_trivial_destructor<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(true, is_POD<int>::value)
|
|
||||||
value_test(true, is_POD<int*>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9p10
|
|
||||||
value_test(false, is_POD<int&>::value)
|
|
||||||
value_test(true, is_POD<int*const>::value)
|
|
||||||
value_test(true, is_POD<const int>::value)
|
|
||||||
// Steve: was 'false', should be 'true', via 3.9p10
|
|
||||||
value_test(true, is_POD<volatile int>::value)
|
|
||||||
// Steve: was 'true', should be 'false', via 3.9p10
|
|
||||||
value_test(false, is_POD<const int&>::value)
|
|
||||||
value_test(true, is_POD<int[2]>::value)
|
|
||||||
value_test(true, is_POD<int[3][2]>::value)
|
|
||||||
value_test(true, is_POD<int[2][4][5][6][3]>::value)
|
|
||||||
value_test(true, is_POD<f1>::value)
|
|
||||||
value_test(true, is_POD<mf2>::value)
|
|
||||||
value_test(false, is_POD<UDT>::value)
|
|
||||||
value_test(false, is_POD<empty_UDT>::value)
|
|
||||||
value_test(true, is_POD<enum_UDT>::value)
|
|
||||||
|
|
||||||
value_test(true, (boost::is_convertible<implicitly_convertible_to_int,
|
|
||||||
int>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Derived,Base>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Derived,Derived>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Base,Base>::value));
|
|
||||||
value_test(false, (boost::is_convertible<Base,Derived>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Derived,Derived>::value));
|
|
||||||
value_test(false, (boost::is_convertible<NonDerived,Base>::value));
|
|
||||||
value_test(false, (boost::is_convertible<boost::noncopyable, int>::value));
|
|
||||||
value_test(true, (boost::is_convertible<float,int>::value));
|
|
||||||
#if defined(BOOST_MSVC6_MEMBER_TEMPLATES) || !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
|
|
||||||
value_test(false, (boost::is_convertible<float,void>::value));
|
|
||||||
value_test(false, (boost::is_convertible<void,float>::value));
|
|
||||||
value_test(true, (boost::is_convertible<void,void>::value));
|
|
||||||
#endif
|
|
||||||
value_test(true, (boost::is_convertible<enum1, int>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Derived*, Base*>::value));
|
|
||||||
value_test(false, (boost::is_convertible<Base*, Derived*>::value));
|
|
||||||
value_test(true, (boost::is_convertible<Derived&, Base&>::value));
|
|
||||||
value_test(false, (boost::is_convertible<Base&, Derived&>::value));
|
|
||||||
value_test(true, (boost::is_convertible<const Derived*, const Base*>::value));
|
|
||||||
value_test(false, (boost::is_convertible<const Base*, const Derived*>::value));
|
|
||||||
value_test(true, (boost::is_convertible<const Derived&, const Base&>::value));
|
|
||||||
value_test(false, (boost::is_convertible<const Base&, const Derived&>::value));
|
|
||||||
|
|
||||||
value_test(false, (boost::is_convertible<const int *, int*>::value));
|
|
||||||
value_test(false, (boost::is_convertible<const int&, int&>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int*, int[2]>::value));
|
|
||||||
value_test(false, (boost::is_convertible<const int*, int[3]>::value));
|
|
||||||
value_test(true, (boost::is_convertible<const int&, int>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int(&)[4], const int*>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int(&)(int), int(*)(int)>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int *, const int*>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int&, const int&>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int[2], int*>::value));
|
|
||||||
value_test(true, (boost::is_convertible<int[2], const int*>::value));
|
|
||||||
value_test(false, (boost::is_convertible<const int[2], int*>::value));
|
|
||||||
|
|
||||||
align_test(int);
|
|
||||||
align_test(char);
|
|
||||||
align_test(double);
|
|
||||||
align_test(int[4]);
|
|
||||||
align_test(int(*)(int));
|
|
||||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
|
|
||||||
align_test(char&);
|
|
||||||
align_test(char (&)(int));
|
|
||||||
align_test(char(&)[4]);
|
|
||||||
#endif
|
|
||||||
align_test(int*);
|
|
||||||
//align_test(const int);
|
|
||||||
align_test(VB);
|
|
||||||
align_test(VD);
|
|
||||||
|
|
||||||
std::cout << std::endl << test_count << " tests completed (" << failures << " failures)";
|
|
||||||
return failures;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
|||||||
// boost::compressed_pair test program
|
|
||||||
|
|
||||||
// (C) Copyright John Maddock 2000. Permission to copy, use, modify, sell and
|
|
||||||
// distribute this software is granted provided this copyright notice appears
|
|
||||||
// in all copies. This software is provided "as is" without express or implied
|
|
||||||
// warranty, and with no claim as to its suitability for any purpose.
|
|
||||||
|
|
||||||
// common test code for type_traits_test.cpp/call_traits_test.cpp/compressed_pair_test.cpp
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef BOOST_TYPE_TRAITS_TEST_HPP
|
|
||||||
#define BOOST_TYPE_TRAITS_TEST_HPP
|
|
||||||
|
|
||||||
// Variable declarations must come before test_align due to two-phase lookup
|
|
||||||
unsigned failures = 0;
|
|
||||||
unsigned test_count = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// this one is here just to suppress warnings:
|
|
||||||
//
|
|
||||||
template <class T>
|
|
||||||
bool do_compare(T i, T j)
|
|
||||||
{
|
|
||||||
return i == j;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// this one is to verify that a constant is indeed a
|
|
||||||
// constant-integral-expression:
|
|
||||||
//
|
|
||||||
template <int>
|
|
||||||
struct ct_checker
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
#define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2(X,Y)
|
|
||||||
#define BOOST_DO_JOIN2(X, Y) X##Y
|
|
||||||
#define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y )
|
|
||||||
|
|
||||||
#ifdef BOOST_MSVC
|
|
||||||
#define value_test(v, x) ++test_count;\
|
|
||||||
{typedef ct_checker<(x)> this_is_a_compile_time_check_;}\
|
|
||||||
if(!do_compare((int)v,(int)x)){++failures; std::cout << "checking value of " << #x << "...failed" << std::endl;}
|
|
||||||
#else
|
|
||||||
#define value_test(v, x) ++test_count;\
|
|
||||||
typedef ct_checker<(x)> BOOST_JOIN(this_is_a_compile_time_check_, __LINE__);\
|
|
||||||
if(!do_compare((int)v,(int)x)){++failures; std::cout << "checking value of " << #x << "...failed" << std::endl;}
|
|
||||||
#endif
|
|
||||||
#define value_fail(v, x) ++test_count; ++failures; std::cout << "checking value of " << #x << "...failed" << std::endl;
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
|
|
||||||
#define type_test(v, x) ++test_count;\
|
|
||||||
if(do_compare(boost::is_same<v, x>::value, false)){\
|
|
||||||
++failures; \
|
|
||||||
std::cout << "checking type of " << #x << "...failed" << std::endl; \
|
|
||||||
std::cout << " expected type was " << #v << std::endl; \
|
|
||||||
std::cout << " " << typeid(boost::is_same<v, x>).name() << "::value is false" << std::endl; }
|
|
||||||
#else
|
|
||||||
#define type_test(v, x) ++test_count;\
|
|
||||||
if(typeid(v) != typeid(x)){\
|
|
||||||
++failures; \
|
|
||||||
std::cout << "checking type of " << #x << "...failed" << std::endl; \
|
|
||||||
std::cout << " expected type was " << #v << std::endl; \
|
|
||||||
std::cout << " " << "typeid(" #v ") != typeid(" #x ")" << std::endl; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct test_align
|
|
||||||
{
|
|
||||||
struct padded
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
T t;
|
|
||||||
};
|
|
||||||
static void do_it()
|
|
||||||
{
|
|
||||||
padded p;
|
|
||||||
unsigned a = reinterpret_cast<char*>(&(p.t)) - reinterpret_cast<char*>(&p);
|
|
||||||
value_test(a, boost::alignment_of<T>::value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
|
|
||||||
template <class T>
|
|
||||||
struct test_align<T&>
|
|
||||||
{
|
|
||||||
static void do_it()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// we can't do the usual test because we can't take the address
|
|
||||||
// of a reference, so check that the result is the same as for a
|
|
||||||
// pointer type instead:
|
|
||||||
value_test(boost::alignment_of<T*>::value, boost::alignment_of<T&>::value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define align_test(T) test_align<T>::do_it()
|
|
||||||
|
|
||||||
//
|
|
||||||
// define tests here
|
|
||||||
|
|
||||||
//
|
|
||||||
// turn off some warnings:
|
|
||||||
#ifdef __BORLANDC__
|
|
||||||
#pragma option -w-8004
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BOOST_MSVC
|
|
||||||
#pragma warning (disable: 4018)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif // BOOST_TYPE_TRAITS_TEST_HPP
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user