diff --git a/algo_opt_examples.cpp b/algo_opt_examples.cpp index 6b795e7..c35595d 100644 --- a/algo_opt_examples.cpp +++ b/algo_opt_examples.cpp @@ -24,6 +24,13 @@ * */ +/* 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 #include #include @@ -39,10 +46,14 @@ 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_arry: +// algorithm destroy_array: // The reverse of std::unitialized_copy, takes a block of // unitialized memory and calls destructors on all objects therein. // @@ -221,6 +232,10 @@ struct swapper } }; +#ifdef __GNUC__ +using std::swap; +#endif + template <> struct swapper { @@ -295,6 +310,44 @@ int main() result = t.elapsed(); cout << "destroy_array(unoptimised#2): " << result << endl << endl; + cout << "testing fill(char)...\n" + "[Some standard library versions may already perform this optimisation.]" << endl; + /*cache load*/ opt::fill(c_array, c_array + array_size, (char)3); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::fill(c_array, c_array + array_size, (char)3); + } + result = t.elapsed(); + cout << "opt::fill: " << 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: " << 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(i_array, i_array + array_size, 3); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::fill(i_array, i_array + array_size, 3); + } + result = t.elapsed(); + cout << "opt::fill: " << 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: " << result << endl << endl; + cout << "testing copy...\n" "[Some standard library versions may already perform this optimisation.]" << endl; /*cache load*/ opt::copy(ci_array, ci_array + array_size, i_array); @@ -347,43 +400,6 @@ int main() result = t.elapsed(); cout << "standard \"unoptimised\" copy: " << result << endl << endl; - cout << "testing fill(char)...\n" - "[Some standard library versions may already perform this optimisation.]" << endl; - /*cache load*/ opt::fill(c_array, c_array + array_size, (char)3); - t.restart(); - for(i = 0; i < iter_count; ++i) - { - opt::fill(c_array, c_array + array_size, (char)3); - } - result = t.elapsed(); - cout << "opt::fill: " << 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: " << 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(i_array, i_array + array_size, 3); - t.restart(); - for(i = 0; i < iter_count; ++i) - { - opt::fill(i_array, i_array + array_size, 3); - } - result = t.elapsed(); - cout << "opt::fill: " << 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: " << result << endl << endl; // // testing iter_swap @@ -404,3 +420,4 @@ int main() + diff --git a/c++_type_traits.htm b/c++_type_traits.htm new file mode 100644 index 0000000..3cbb786 --- /dev/null +++ b/c++_type_traits.htm @@ -0,0 +1,489 @@ + + + + + + +C++ Type traits + + + + +

C++ Type traits

+

by John Maddock and Steve Cleary

+

This is a draft of an article that will appear in a future +issue of Dr Dobb's Journal

+

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.

+

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 strlen +and wcslen 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 char +and wchar_t into the class char_traits. +Generic code that works with character strings can simply use char_traits<>::length +to determine the length of a null terminated string, safe in the knowledge that +specialisations of char_traits will use +the most appropriate method available to them.

+

Type traits

+

Class char_traits is a classic +example of a collection of type specific properties wrapped up in a single class +- what Nathan Myers termed a baggage class[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 value, 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.

+

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 type that is the result of +the transformation. All of the type-traits classes are defined inside namespace boost; +for brevity, namespace-qualification is omitted in most of the code samples +given.

+

Implementation

+

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 value that is true only if T is void.

+
template <typename T> 
+struct is_void
+{ static const bool value = false; };
+
+template <> 
+struct is_void<void>
+{ static const bool value = true; };
+

Here we define a primary version of the template class is_void, +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:

+
template <typename T> 
+struct is_pointer 
+{ static const bool value = false; };
+
+template <typename T> 
+struct is_pointer<T*> 
+{ static const bool value = true; };
+

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:

+
void foo(T);
+void foo(U);
+

Then you can also write a partial specialisation of the form:

+
template <typename T>
+class c{ /*details*/ };
+
+template <typename T>
+
+class c<U>{ /*details*/ };
+

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.

+

As a more complex example of partial specialisation consider the class +remove_bounds<T>. This class defines a single typedef-member type +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:

+
template <typename T> 
+struct remove_bounds
+{ typedef T type; };
+
+template <typename T, std::size_t N> 
+struct remove_bounds<T[N]>
+{ typedef T type; };
+

The aim of remove_bounds is this: imagine a generic algorithm that is passed +an array type as a template parameter, remove_bounds +provides a means of determining the underlying type of the array. For example remove_bounds<int[4][5]>::type +would evaluate to the type int[5]. 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.

+

Optimised copy

+

As an example of how the type traits classes can be used, consider the +standard library algorithm copy:

+
template<typename Iter1, typename Iter2>
+Iter2 copy(Iter1 first, Iter1 last, Iter2 out);
+

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 memcpy. +In order to implement copy in terms of memcpy +all of the following conditions need to be met:

+
    +
  • Both of the iterator types Iter1 and Iter2 must be pointers.
  • +
  • Both Iter1 and Iter2 must point to the same type - excluding const + and volatile-qualifiers.
  • +
  • The type pointed to by Iter1 must have a trivial assignment operator.
  • +
+

By trivial assignment operator we mean that the type is either a scalar +type[3] or:

+
    +
  • The type has no user defined assignment operator.
  • +
  • The type does not have any data members that are references.
  • +
  • All base classes, and all data member objects must have trivial assignment + operators.
  • +
+

If all these conditions are met then a type can be copied using memcpy +rather than using a compiler generated assignment operator. The type-traits +library provides a class has_trivial_assign, such that has_trivial_assign<T>::value +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 has_trivial_assign gives the wrong answer, it will give +the "safe" wrong answer - that trivial assignment is not allowable.

+

The code for an optimised version of copy that uses memcpy +where appropriate is given in listing 1. The code begins by defining a template +class copier, that takes a single Boolean template parameter, and has a +static template member function do_copy +which performs the generic version of copy (in other words +the "slow but safe version"). Following that there is a specialisation +for copier<true>: again this defines a static template member +function do_copy, but this version uses +memcpy to perform an "optimised" copy.

+

In order to complete the implementation, what we need now is a version of +copy, that calls copier<true>::do_copy if it is safe to use memcpy, +and otherwise calls copier<false>::do_copy 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 copy +and consider first the two typedefs v1_t and v2_t. These use std::iterator_traits<Iter1>::value_type +to determine what type the two iterators point to, and then feed the result into +another type-traits class remove_cv 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, copy +declares an enumerated value can_opt 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 copier. +The value of can_opt is computed by verifying that all of the following +are true:

+
    +
  • first that the two iterators point to the same type by using a type-traits + class is_same.
  • +
  • Then that both iterators are real pointers - using the class is_pointer + described above.
  • +
  • Finally that the pointed-to types have a trivial assignment operator using + has_trivial_assign.
  • +
+

Finally we can use the value of can_opt 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 memcpy, +then it will do so, otherwise it will use a generic copy.

+

Was it worth it?

+

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.

+

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:

+
    +
  • If you use the right algorithm for the job in the first place then + optimisation will not be required; in some cases, memcpy + is the right algorithm.
  • +
  • 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.
  • +
+

Table 1: Time taken to copy 1000 elements using copy<const T*, T*> +(times in micro-seconds)

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Version

+
+

T

+
+

Time

+
"Optimised" copychar0.99
Conventional copychar8.07
"Optimised" copyint2.52
Conventional copyint8.02
+

 

+

Pair of References

+

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

+

First, let us examine the definition of "std::pair", omitting the +comparision operators, default constructor, and template copy constructor for +simplicity:

+
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) { }
+};
+

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:

+ + + + + + + + + + + + + + + + + +
Type of "T1"Type of parameter to initializing constructor
+
T
+
+
const T &
+
+
T &
+
+
T &
+
+
const T &
+
+
const T &
+
+

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.

+ + + + + + + + + + + + + + + + + + + + + +
Type of "T1"Type of "const T1"Type of "add_reference<const + T1>::type"
+
T
+
+
const T
+
+
const T &
+
+
T &
+
+
T & [8]
+
+
T &
+
+
const T &
+
+
const T &
+
+
const T &
+
+

This allows us to build a primary template definition for "pair" +that can contain non-reference types, reference types, and constant reference +types:

+
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) { }
+};
+

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!

+

This same extension could 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.

+

Conclusion

+

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.

+

Acknowledgements

+

The authors would like to thank Beman Dawes and Howard Hinnant for their +helpful comments when preparing this article.

+

References

+
    +
  1. Nathan C. Myers, C++ Report, June 1995.
  2. +
  3. 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.
  4. +
  5. 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.
  6. +
  7. This quote is from Donald Knuth, ACM Computing Surveys, December 1974, pg + 268.
  8. +
  9. 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.
  10. +
  11. 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".
  12. +
  13. 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.
  14. +
  15. 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.
  16. +
+

Listing 1

+
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);
+}
+
+

© Copyright John Maddock and Steve Cleary, 2000

+ + + + diff --git a/call_traits.htm b/call_traits.htm new file mode 100644 index 0000000..d0a0fde --- /dev/null +++ b/call_traits.htm @@ -0,0 +1,738 @@ + + + + + + +Call Traits + + + + +

Header +<boost/call_traits.hpp>

+ +

All of the contents of <boost/call_traits.hpp> are +defined inside namespace boost.

+ +

The template class call_traits<T> encapsulates the +"best" method to pass a parameter of some type T to or +from a function, and consists of a collection of typedefs defined +as in the table below. The purpose of call_traits is to ensure +that problems like "references to references" +never occur, and that parameters are passed in the most efficient +manner possible (see examples). In each +case if your existing practice is to use the type defined on the +left, then replace it with the call_traits defined type on the +right. Note that for compilers that do not support partial +specialization, no benefit will occur from using call_traits: the +call_traits defined types will always be the same as the existing +practice in this case.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Existing practice

+

call_traits equivalent

+

Description

+

Notes

+

T
+ (return by value)

+

call_traits<T>::value_type

+
Defines a type that + represents the "value" of type T. Use this for + functions that return by value, or possibly for stored + values of type T.

2

+

T&
+ (return value)

+

call_traits<T>::reference

+
Defines a type that + represents a reference to type T. Use for functions that + would normally return a T&.

1

+

const T&
+ (return value)

+

call_traits<T>::const_reference

+
Defines a type that + represents a constant reference to type T. Use for + functions that would normally return a const T&.

1

+

const T&
+ (function parameter)

+

call_traits<T>::param_type

+
Defines a type that + represents the "best" way to pass a parameter + of type T to a function.

1,3

+
+ +

Notes:

+ +
    +
  1. If T is already reference type, then call_traits is + defined such that references to + references do not occur (requires partial + specialization).
  2. +
  3. If T is an array type, then call_traits defines value_type + as a "constant pointer to type" rather than an + "array of type" (requires partial + specialization). Note that if you are using value_type as + a stored value then this will result in storing a "constant + pointer to an array" rather than the array itself. + This may or may not be a good thing depending upon what + you actually need (in other words take care!).
  4. +
  5. If T is a small built in type or a pointer, then param_type + is defined as T const, instead of T + const&. This can improve the ability of the + compiler to optimize loops in the body of the function if + they depend upon the passed parameter, the semantics of + the passed parameter is otherwise unchanged (requires + partial specialization).
  6. +
+ +

 

+ +

Copy constructibility

+ +

The following table defines which call_traits types can always +be copy-constructed from which other types, those entries marked +with a '?' are true only if and only if T is copy constructible:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 

To:

+
From:

T

+

value_type

+

reference

+

const_reference

+

param_type

+
T

?

+

?

+

Y

+

Y

+

Y

+
value_type

?

+

?

+

N

+

N

+

Y

+
reference

?

+

?

+

Y

+

Y

+

Y

+
const_reference

?

+

N

+

N

+

Y

+

Y

+
param_type

?

+

?

+

N

+

N

+

Y

+
+ +

 

+ +

If T is an assignable type the following assignments are +possible:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 

To:

+
From:

T

+

value_type

+

reference

+

const_reference

+

param_type

+
T

Y

+

Y

+

-

+

-

+

-

+
value_type

Y

+

Y

+

-

+

-

+

-

+
reference

Y

+

Y

+

-

+

-

+

-

+
const_reference

Y

+

Y

+

-

+

-

+

-

+
param_type

Y

+

Y

+

-

+

-

+

-

+
+ +

 

+ +

Examples

+ +

The following table shows the effect that call_traits has on +various types, the table assumes that the compiler supports +partial specialization: if it doesn't then all types behave in +the same way as the entry for "myclass", and call_traits +can not be used with reference or array types.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 

Call_traits type:

+

Original type T

+

value_type

+

reference

+

const_reference

+

param_type

+

Applies to:

+

myclass

+

myclass

+

myclass&

+

const + myclass&

+

myclass + const&

+

All user + defined types.

+

int

+

int

+

int&

+

const int&

+

int const

+

All small + built-in types.

+

int*

+

int*

+

int*&

+

int*const&

+

int* const

+

All + pointer types.

+

int&

+

int&

+

int&

+

const int&

+

int&

+

All + reference types.

+

const int&

+

const int&

+

const int&

+

const int&

+

const int&

+

All + constant-references.

+

int[3]

+

const int*

+

int(&)[3]

+

const int(&)[3]

+

const int* + const

+

All array + types.

+

const int[3]

+

const int*

+

const int(&)[3]

+

const int(&)[3]

+

const int* + const

+

All + constant-array types.

+
+ +

 

+ +

Example 1:

+ +

The following class is a trivial class that stores some type T +by value (see the call_traits_test.cpp +file), the aim is to illustrate how each of the available call_traits +typedefs may be used:

+ +
template <class T>
+struct contained
+{
+   // define our typedefs first, arrays are stored by value
+   // so value_type is not the same as result_type:
+   typedef typename boost::call_traits<T>::param_type       param_type;
+   typedef typename boost::call_traits<T>::reference        reference;
+   typedef typename boost::call_traits<T>::const_reference  const_reference;
+   typedef T                                                value_type;
+   typedef typename boost::call_traits<T>::value_type       result_type;
+
+   // stored value:
+   value_type v_;
+   
+   // constructors:
+   contained() {}
+   contained(param_type p) : v_(p){}
+   // return byval:
+   result_type value() { return v_; }
+   // return by_ref:
+   reference get() { return v_; }
+   const_reference const_get()const { return v_; }
+   // pass value:
+   void call(param_type p){}
+
+};
+ +

Example 2 (the reference to reference +problem):

+ +

Consider the definition of std::binder1st:

+ +
template <class Operation> 
+class binder1st : 
+   public unary_function<Operation::second_argument_type, Operation::result_type> 
+{ 
+protected: 
+   Operation op; 
+   Operation::first_argument_type value; 
+public: 
+   binder1st(const Operation& x, const Operation::first_argument_type& y); 
+   Operation::result_type operator()(const Operation::second_argument_type& x) const; 
+}; 
+ +

Now consider what happens in the relatively common case that +the functor takes its second argument as a reference, that +implies that Operation::second_argument_type is a +reference type, operator() will now end up taking a +reference to a reference as an argument, and that is not +currently legal. The solution here is to modify operator() +to use call_traits:

+ +
Operation::result_type operator()(call_traits<Operation::second_argument_type>::param_type x) const;
+ +

Now in the case that Operation::second_argument_type +is a reference type, the argument is passed as a reference, and +the no "reference to reference" occurs.

+ +

Example 3 (the make_pair problem):

+ +

If we pass the name of an array as one (or both) arguments to std::make_pair, +then template argument deduction deduces the passed parameter as +"const reference to array of T", this also applies to +string literals (which are really array literals). Consequently +instead of returning a pair of pointers, it tries to return a +pair of arrays, and since an array type is not copy-constructible +the code fails to compile. One solution is to explicitly cast the +arguments to make_pair to pointers, but call_traits provides a +better (i.e. automatic) solution (and one that works safely even +in generic code where the cast might do the wrong thing):

+ +
template <class T1, class T2>
+std::pair<
+   typename boost::call_traits<T1>::value_type, 
+   typename boost::call_traits<T2>::value_type> 
+      make_pair(const T1& t1, const T2& t2)
+{
+   return std::pair<
+      typename boost::call_traits<T1>::value_type, 
+      typename boost::call_traits<T2>::value_type>(t1, t2);
+}
+ +

Here, the deduced argument types will be automatically +degraded to pointers if the deduced types are arrays, similar +situations occur in the standard binders and adapters: in +principle in any function that "wraps" a temporary +whose type is deduced.

+ +

Example 4 (optimising fill):

+ +

The call_traits template will "optimize" the passing +of a small built-in type as a function parameter, this mainly has +an effect when the parameter is used within a loop body. In the +following example (see algo_opt_examples.cpp), +a version of std::fill is optimized in two ways: if the type +passed is a single byte built-in type then std::memset is used to +effect the fill, otherwise a conventional C++ implemention is +used, but with the passed parameter "optimized" using +call_traits:

+ +
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);
+   {
+      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)
+   {
+      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);
+}
+ +

Footnote: the reason that this is "optimal" for +small built-in types is that with the value passed as "T +const" instead of "const T&" the compiler is +able to tell both that the value is constant and that it is free +of aliases. With this information the compiler is able to cache +the passed value in a register, unroll the loop, or use +explicitly parallel instructions: if any of these are supported. +Exactly how much mileage you will get from this depends upon your +compiler - we could really use some accurate benchmarking +software as part of boost for cases like this.

+ +

Rationale

+ +

The following notes are intended to briefly describe the +rational behind choices made in call_traits.

+ +

All user-defined types follow "existing practice" +and need no comment.

+ +

Small built-in types (what the standard calls fundamental +types [3.9.1]) differ from existing practice only in the param_type +typedef. In this case passing "T const" is compatible +with existing practice, but may improve performance in some cases +(see Example 4), in any case this should never +be any worse than existing practice.

+ +

Pointers follow the same rational as small built-in types.

+ +

For reference types the rational follows Example +2 - references to references are not allowed, so the call_traits +members must be defined such that these problems do not occur. +There is a proposal to modify the language such that "a +reference to a reference is a reference" (issue #106, +submitted by Bjarne Stroustrup), call_traits<T>::value_type +and call_traits<T>::param_type both provide the same effect +as that proposal, without the need for a language change (in +other words it's a workaround).

+ +

For array types, a function that takes an array as an argument +will degrade the array type to a pointer type: this means that +the type of the actual parameter is different from its declared +type, something that can cause endless problems in template code +that relies on the declared type of a parameter. For example:

+ +
template <class T>
+struct A
+{
+   void foo(T t);
+};
+ +

In this case if we instantiate A<int[2]> +then the declared type of the parameter passed to member function +foo is int[2], but it's actual type is const int*, if we try to +use the type T within the function body, then there is a strong +likelyhood that our code will not compile:

+ +
template <class T>
+void A<T>::foo(T t)
+{
+   T dup(t); // doesn't compile for case that T is an array.
+}
+ +

By using call_traits the degradation from array to pointer is +explicit, and the type of the parameter is the same as it's +declared type:

+ +
template <class T>
+struct A
+{
+   void foo(call_traits<T>::value_type t);
+};
+
+template <class T>
+void A<T>::foo(call_traits<T>::value_type t)
+{
+   call_traits<T>::value_type dup(t); // OK even if T is an array type.
+}
+ +

For value_type (return by value), again only a pointer may be +returned, not a copy of the whole array, and again call_traits +makes the degradation explicit. The value_type member is useful +whenever an array must be explicitly degraded to a pointer - Example 3 provides the test case (Footnote: the +array specialisation for call_traits is the least well understood +of all the call_traits specialisations, if the given semantics +cause specific problems for you, or don't solve a particular +array-related problem, then I would be interested to hear about +it. Most people though will probably never need to use this +specialisation).

+ +
+ +

Revised 18 June 2000

+ +

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

+ +

Based on contributions by Steve Cleary, Beman Dawes, Howard +Hinnant and John Maddock.

+ +

Maintained by John +Maddock, the latest version of this file can be found at www.boost.org, and the boost +discussion list at www.egroups.com/list/boost.

+ +

.

+ +

 

+ +

 

+ + diff --git a/call_traits_test.cpp b/call_traits_test.cpp index c615013..710657a 100644 --- a/call_traits_test.cpp +++ b/call_traits_test.cpp @@ -1,3 +1,11 @@ + // 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. + +// standalone test program for #include #include @@ -6,6 +14,7 @@ #include #include +#include "type_traits_test.hpp" // // struct contained models a type that contains a type (for example std::pair) // arrays are contained by value, and have to be treated as a special case: @@ -28,7 +37,7 @@ struct contained contained() {} contained(param_type p) : v_(p){} // return byval: - result_type value() { return v_; } + result_type value()const { return v_; } // return by_ref: reference get() { return v_; } const_reference const_get()const { return v_; } @@ -37,6 +46,7 @@ struct contained }; +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template struct contained { @@ -53,30 +63,36 @@ struct contained std::copy(p, p+N, v_); } // return byval: - result_type value() { return v_; } + result_type value()const { return v_; } // return by_ref: reference get() { return v_; } const_reference const_get()const { return v_; } void call(param_type p){} }; +#endif template contained::value_type> wrap(const T& t) { - return contained::value_type>(t); + typedef typename boost::call_traits::value_type ct; + return contained(t); } +namespace test{ + template std::pair< - typename boost::call_traits::value_type, - typename boost::call_traits::value_type> + typename boost::call_traits::value_type, + typename boost::call_traits::value_type> make_pair(const T1& t1, const T2& t2) { return std::pair< - typename boost::call_traits::value_type, + typename boost::call_traits::value_type, typename boost::call_traits::value_type>(t1, t2); } +} // namespace test + using namespace std; // @@ -108,51 +124,40 @@ void checker::operator()(param_type p) cout << endl; } +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template struct checker { typedef typename boost::call_traits::param_type param_type; - void operator()(param_type); + void operator()(param_type t) + { + contained c(t); + cout << "checking contained<" << typeid(T[N]).name() << ">..." << endl; + unsigned int i = 0; + for(i = 0; i < N; ++i) + assert(t[i] == c.value()[i]); + for(i = 0; i < N; ++i) + assert(t[i] == c.get()[i]); + for(i = 0; i < N; ++i) + assert(t[i] == c.const_get()[i]); + + cout << "typeof contained<" << typeid(T[N]).name() << ">::v_ is: " << typeid(&contained::v_).name() << endl; + cout << "typeof contained<" << typeid(T[N]).name() << ">::value is: " << typeid(&contained::value).name() << endl; + cout << "typeof contained<" << typeid(T[N]).name() << ">::get is: " << typeid(&contained::get).name() << endl; + cout << "typeof contained<" << typeid(T[N]).name() << ">::const_get is: " << typeid(&contained::const_get).name() << endl; + cout << "typeof contained<" << typeid(T[N]).name() << ">::call is: " << typeid(&contained::call).name() << endl; + cout << endl; + } }; - -template -void checker::operator()(param_type t) -{ - contained c(t); - cout << "checking contained<" << typeid(T[N]).name() << ">..." << endl; - unsigned int i = 0; - for(i = 0; i < N; ++i) - assert(t[i] == c.value()[i]); - for(i = 0; i < N; ++i) - assert(t[i] == c.get()[i]); - for(i = 0; i < N; ++i) - assert(t[i] == c.const_get()[i]); - - cout << "typeof contained<" << typeid(T[N]).name() << ">::v_ is: " << typeid(&contained::v_).name() << endl; - cout << "typeof contained<" << typeid(T[N]).name() << ">::value is: " << typeid(&contained::value).name() << endl; - cout << "typeof contained<" << typeid(T[N]).name() << ">::get is: " << typeid(&contained::get).name() << endl; - cout << "typeof contained<" << typeid(T[N]).name() << ">::const_get is: " << typeid(&contained::const_get).name() << endl; - cout << "typeof contained<" << typeid(T[N]).name() << ">::call is: " << typeid(&contained::call).name() << endl; - cout << endl; -} +#endif // // check_wrap: -// verifies behaviour of "wrap": -// -template -void check_wrap(T c, U u, const V& v) +template +void check_wrap(const contained& w, const U& u) { - cout << "checking contained<" << typeid(T::value_type).name() << ">..." << endl; - assert(c.get() == u); - cout << "typeof deduced argument was: " << typeid(V).name() << endl; - cout << "typeof deduced parameter after adjustment was: " << typeid(v).name() << endl; - cout << "typeof contained<" << typeid(T::value_type).name() << ">::v_ is: " << typeid(&T::v_).name() << endl; - cout << "typeof contained<" << typeid(T::value_type).name() << ">::value is: " << typeid(&T::value).name() << endl; - cout << "typeof contained<" << typeid(T::value_type).name() << ">::get is: " << typeid(&T::get).name() << endl; - cout << "typeof contained<" << typeid(T::value_type).name() << ">::const_get is: " << typeid(&T::const_get).name() << endl; - cout << "typeof contained<" << typeid(T::value_type).name() << ">::call is: " << typeid(&T::call).name() << endl; - cout << endl; + cout << "checking contained<" << typeid(T).name() << ">..." << endl; + assert(w.value() == u); } // @@ -176,7 +181,6 @@ struct UDT bool operator == (const UDT& v){ return v.i_ == i_; } }; - int main() { checker c1; @@ -188,6 +192,7 @@ int main() int* pi = &i; checker c3; c3(pi); +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION checker c4; c4(i); checker c5; @@ -196,13 +201,155 @@ int main() int a[2] = {1,2}; checker c6; c6(a); +#endif - check_wrap(wrap(2), 2, 2); + check_wrap(wrap(2), 2); const char ca[4] = "abc"; // compiler can't deduce this for some reason: - //check_wrap(wrap(ca), ca, ca); - check_wrap(wrap(a), a, a); - check_make_pair(::make_pair(a, a), a, a); + //check_wrap(wrap(ca), ca); +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + check_wrap(wrap(a), a); + check_make_pair(test::make_pair(a, a), a, a); +#endif - return 0; + // 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; + typedef const r_type cr_type; + + type_test(UDT, boost::call_traits::value_type) + type_test(UDT&, boost::call_traits::reference) + type_test(const UDT&, boost::call_traits::const_reference) + type_test(const UDT&, boost::call_traits::param_type) + type_test(int, boost::call_traits::value_type) + type_test(int&, boost::call_traits::reference) + type_test(const int&, boost::call_traits::const_reference) + type_test(const int, boost::call_traits::param_type) + type_test(int*, boost::call_traits::value_type) + type_test(int*&, boost::call_traits::reference) + type_test(int*const&, boost::call_traits::const_reference) + type_test(int*const, boost::call_traits::param_type) +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + type_test(int&, boost::call_traits::value_type) + type_test(int&, boost::call_traits::reference) + type_test(const int&, boost::call_traits::const_reference) + type_test(int&, boost::call_traits::param_type) +#if !(defined(__GNUC__) && (__GNUC__ < 3)) + type_test(int&, boost::call_traits::value_type) + type_test(int&, boost::call_traits::reference) + type_test(const int&, boost::call_traits::const_reference) + type_test(int&, boost::call_traits::param_type) +#else + std::cout << "Your compiler cannot instantiate call_traits, skipping four tests (4 errors)" << std::endl; + failures += 4; + test_count += 4; +#endif + type_test(const int&, boost::call_traits::value_type) + type_test(const int&, boost::call_traits::reference) + type_test(const int&, boost::call_traits::const_reference) + type_test(const int&, boost::call_traits::param_type) + type_test(const int*, boost::call_traits::value_type) + type_test(int(&)[3], boost::call_traits::reference) + type_test(const int(&)[3], boost::call_traits::const_reference) + type_test(const int*const, boost::call_traits::param_type) + type_test(const int*, boost::call_traits::value_type) + type_test(const int(&)[3], boost::call_traits::reference) + type_test(const int(&)[3], boost::call_traits::const_reference) + type_test(const int*const, boost::call_traits::param_type) +#else + std::cout << "You're compiler does not support partial template instantiation, skipping 20 tests (20 errors)" << std::endl; + failures += 20; + test_count += 20; +#endif + + std::cout << std::endl << test_count << " tests completed (" << failures << " failures)... press any key to exit"; + std::cin.get(); + return failures; } + +// +// define call_traits tests to check that the assertions in the docs do actually work +// this is an instantiate only set of tests: +// +template +struct call_traits_test +{ + typedef ::boost::call_traits ct; + typedef typename ct::param_type param_type; + typedef typename ct::reference reference; + typedef typename ct::const_reference const_reference; + typedef typename ct::value_type value_type; + static void assert_construct(param_type val); +}; + +template +void call_traits_test::assert_construct(typename call_traits_test::param_type val) +{ + // + // this is to check that the call_traits assertions are valid: + T t(val); + value_type v(t); + reference r(t); + const_reference cr(t); + param_type p(t); + value_type v2(v); + value_type v3(r); + value_type v4(p); + reference r2(v); + reference r3(r); + const_reference cr2(v); + const_reference cr3(r); + const_reference cr4(cr); + const_reference cr5(p); + param_type p2(v); + param_type p3(r); + param_type p4(p); +} +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template +struct call_traits_test +{ + typedef ::boost::call_traits ct; + typedef typename ct::param_type param_type; + typedef typename ct::reference reference; + typedef typename ct::const_reference const_reference; + typedef typename ct::value_type value_type; + static void assert_construct(param_type val); +}; + +template +void call_traits_test::assert_construct(boost::call_traits::param_type val) +{ + // + // this is to check that the call_traits assertions are valid: + T t; + value_type v(t); + value_type v5(val); + reference r = t; + const_reference cr = t; + reference r2 = r; + #ifndef __BORLANDC__ + // C++ Builder buglet: + const_reference cr2 = r; + #endif + param_type p(t); + value_type v2(v); + const_reference cr3 = cr; + value_type v3(r); + value_type v4(p); + param_type p2(v); + param_type p3(r); + param_type p4(p); +} +#endif //BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +// +// now check call_traits assertions by instantiating call_traits_test: +template struct call_traits_test; +template struct call_traits_test; +template struct call_traits_test; +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template struct call_traits_test; +template struct call_traits_test; +template struct call_traits_test; +#endif + diff --git a/cast.htm b/cast.htm new file mode 100644 index 0000000..2ebb685 --- /dev/null +++ b/cast.htm @@ -0,0 +1,148 @@ + + + + + + +Header boost/cast.hpp Documentation + + + + +

c++boost.gif (8819 bytes)Header +boost/cast.hpp

+

Cast Functions

+

The header boost/cast.hpp +provides polymorphic_cast, polymorphic_downcast, +and numeric_cast template functions designed +to complement the C++ Standard's built-in casts.

+

The program cast_test.cpp can be used to +verify these function templates work as expected.

+

polymorphic_cast was suggested by Bjarne Stroustrup in "The C++ +Programming Language".
+polymorphic_downcast was contributed by Dave +Abrahams.
+numeric_cast
was contributed by Kevlin +Henney.

+

Namespace synopsis

+
+
namespace boost {
+    namespace cast {
+        // all synopsis below included here
+    }
+  using ::boost::cast::polymorphic_cast;
+  using ::boost::cast::polymorphic_downcast;
+  using ::boost::cast::bad_numeric_cast;
+  using ::boost::cast::numeric_cast;
+}
+
+

Polymorphic casts

+

Pointers to polymorphic objects (objects of classes which define at least one +virtual function) are sometimes downcast or crosscast.  Downcasting means +casting from a base class to a derived class.  Crosscasting means casting +across an inheritance hierarchy diagram, such as from one base to the other in a +Y diagram hierarchy.

+

Such casts can be done with old-style casts, but this approach is never to be +recommended.  Old-style casts are sorely lacking in type safety, suffer +poor readability, and are difficult to locate with search tools.

+

The C++ built-in static_cast can be used for efficiently downcasting +pointers to polymorphic objects, but provides no error detection for the case +where the pointer being cast actually points to the wrong derived class. The polymorphic_downcast +template retains the efficiency of static_cast for non-debug +compilations, but for debug compilations adds safety via an assert() that a dynamic_cast +succeeds.  

+

The C++ built-in dynamic_cast can be used for downcasts and crosscasts +of pointers to polymorphic objects, but error notification in the form of a +returned value of 0 is inconvenient to test, or worse yet, easy to forget to +test.  The polymorphic_cast template performs a dynamic_cast, +and throws an exception if the dynamic_cast returns 0.

+

A polymorphic_downcast is preferred when debug-mode tests will cover +100% of the object types possibly cast and when non-debug-mode efficiency is an +issue. If these two conditions are not present, polymorphic_cast is +preferred.  It must also be used for crosscasts.  It does an assert( +dynamic_cast<Derived>(x) == x ) where x is the base pointer, ensuring that +not only is a non-zero pointer returned, but also that it correct in the +presence of multiple inheritance. . Warning:: Because polymorphic_downcast +uses assert(), it violates the One Definition Rule if NDEBUG is inconsistently +defined across translation units.

+

The C++ built-in dynamic_cast must be used to cast references rather +than pointers.  It is also the only cast that can be used to check whether +a given interface is supported; in that case a return of 0 isn't an error +condition.

+

polymorphic_cast and polymorphic_downcast synopsis

+
+
template <class Derived, class Base>
+inline Derived polymorphic_cast(Base* x);
+// Throws: std::bad_cast if ( dynamic_cast<Derived>(x) == 0 )
+// Returns: dynamic_cast<Derived>(x)
+
+template <class Derived, class Base>
+inline Derived polymorphic_downcast(Base* x);
+// Effects: assert( dynamic_cast<Derived>(x) == x );
+// Returns: static_cast<Derived>(x)
+
+

polymorphic_downcast example

+
+
#include <boost/cast.hpp>
+...
+class Fruit { public: virtual ~Fruit(){}; ... };
+class Banana : public Fruit { ... };
+...
+void f( Fruit * fruit ) {
+// ... logic which leads us to believe it is a Banana
+  Banana * banana = boost::polymorphic_downcast<Banana*>(fruit);
+  ...
+
+

numeric_cast

+

A static_cast, implicit_cast or implicit conversion will not +detect failure to preserve range for numeric casts. The numeric_cast +template function are similar to static_cast and certain (dubious) +implicit conversions in this respect, except that they detect loss of numeric +range. An exception is thrown when a runtime value preservation check fails.

+

The requirements on the argument and result types are:

+
+
    +
  • Both argument and result types are CopyConstructible [20.1.3].
  • +
  • Both argument and result types are Numeric, defined by std::numeric_limits<>::is_specialized + being true.
  • +
  • The argument can be converted to the result type using static_cast.
  • +
+
+

numeric_cast synopsis

+
+
class bad_numeric_cast : public std::bad_cast {...};
+
+template<typename Target, typename Source>
+  inline Target numeric_cast(Source arg);
+    // Throws:  bad_numeric_cast unless, in converting arg from Source to Target,
+    //          there is no loss of negative range, and no underflow, and no
+    //          overflow, as determined by std::numeric_limits
+    // Returns: static_cast<Target>(arg)
+
+

numeric_cast example

+
+
#include <boost/cast.hpp>
+using namespace boost::cast;
+
+void ariane(double vx)
+{
+    ...
+    unsigned short dx = numeric_cast<unsigned short>(vx);
+    ...
+}
+
+

numeric_cast rationale

+

The form of the throws condition is specified so that != is not a required +operation.

+
+

Revised  28 June, 2000

+

© Copyright boost.org 1999. 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.

+ + + + diff --git a/compressed_pair.htm b/compressed_pair.htm new file mode 100644 index 0000000..d63af7f --- /dev/null +++ b/compressed_pair.htm @@ -0,0 +1,92 @@ + + + + + + +Header <boost/compressed_pair.hpp> + + + + +

Header +<boost/compressed_pair.hpp>

+ +

All of the contents of <boost/compressed_pair.hpp> are +defined inside namespace boost.

+ +

The class compressed pair is very similar to std::pair, but if +either of the template arguments are empty classes, then the +"empty member optimisation" is applied to compress the +size of the pair.

+ +
template <class T1, class T2>
+class compressed_pair
+{
+public:
+	typedef T1                                                 first_type;
+	typedef T2                                                 second_type;
+	typedef typename call_traits<first_type>::param_type       first_param_type;
+	typedef typename call_traits<second_type>::param_type      second_param_type;
+	typedef typename call_traits<first_type>::reference        first_reference;
+	typedef typename call_traits<second_type>::reference       second_reference;
+	typedef typename call_traits<first_type>::const_reference  first_const_reference;
+	typedef typename call_traits<second_type>::const_reference second_const_reference;
+
+	         compressed_pair() : base() {}
+	         compressed_pair(first_param_type x, second_param_type y);
+	explicit compressed_pair(first_param_type x);
+	explicit compressed_pair(second_param_type y);
+
+	first_reference       first();
+	first_const_reference first() const;
+
+	second_reference       second();
+	second_const_reference second() const;
+
+	void swap(compressed_pair& y);
+};
+ +

The two members of the pair can be accessed using the member +functions first() and second(). Note that not all member +functions can be instantiated for all template parameter types. +In particular compressed_pair can be instantiated for reference +and array types, however in these cases the range of constructors +that can be used are limited. If types T1 and T2 are the same +type, then there is only one version of the single-argument +constructor, and this constructor initialises both values in the +pair to the passed value.

+ +

Note that compressed_pair can not be instantiated if either of +the template arguments is an enumerator type, unless there is +compiler support for boost::is_enum, or if boost::is_enum is +specialised for the enumerator type.

+ +

Finally, compressed_pair requires compiler support for partial +specialisation of class templates - without that support +compressed_pair behaves just like std::pair.

+ +
+ +

Revised 08 March 2000

+ +

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

+ +

Based on contributions by Steve Cleary, Beman Dawes, Howard +Hinnant and John Maddock.

+ +

Maintained by John +Maddock, the latest version of this file can be found at www.boost.org, and the boost +discussion list at www.egroups.com/list/boost.

+ +

 

+ + diff --git a/compressed_pair_test.cpp b/compressed_pair_test.cpp new file mode 100644 index 0000000..ba6d81d --- /dev/null +++ b/compressed_pair_test.cpp @@ -0,0 +1,127 @@ + // 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. + +// standalone test program for + +#include +#include +#include + +#include +#include "type_traits_test.hpp" + +using namespace boost; + +struct empty_POD_UDT{}; +struct empty_UDT +{ + ~empty_UDT(){}; +}; +namespace boost { +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +template <> struct is_empty +{ static const bool value = true; }; +template <> struct is_empty +{ static const bool value = true; }; +template <> struct is_POD +{ static const bool value = true; }; +#else +template <> struct is_empty +{ enum{ value = true }; }; +template <> struct is_empty +{ enum{ value = true }; }; +template <> struct is_POD +{ enum{ value = true }; }; +#endif +} + + +int main() +{ + compressed_pair cp1(1, 1.3); + assert(cp1.first() == 1); + assert(cp1.second() == 1.3); + compressed_pair cp1b(2, 2.3); + assert(cp1b.first() == 2); + assert(cp1b.second() == 2.3); + swap(cp1, cp1b); + assert(cp1b.first() == 1); + assert(cp1b.second() == 1.3); + assert(cp1.first() == 2); + assert(cp1.second() == 2.3); +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + compressed_pair cp2(2); + assert(cp2.second() == 2); +#endif + compressed_pair cp3(1); + assert(cp3.first() ==1); + compressed_pair cp4; + compressed_pair cp5; +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + int i = 0; + compressed_pair cp6(i,i); + assert(cp6.first() == i); + assert(cp6.second() == i); + assert(&cp6.first() == &i); + assert(&cp6.second() == &i); + compressed_pair cp7; + cp7.first(); + double* pd = cp7.second(); +#endif + value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) + value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) + value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) + value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) + value_test(true, (sizeof(compressed_pair >) < sizeof(std::pair >))) + + std::cout << std::endl << test_count << " tests completed (" << failures << " failures)... press any key to exit"; + std::cin.get(); + return failures; +} + +// +// instanciate some compressed pairs: +#ifdef __MWERKS__ +template class compressed_pair; +template class compressed_pair; +template class compressed_pair; +template class compressed_pair; +template class compressed_pair; +template class compressed_pair; +#else +template class boost::compressed_pair; +template class boost::compressed_pair; +template class boost::compressed_pair; +template class boost::compressed_pair; +template class boost::compressed_pair; +template class boost::compressed_pair; +#endif + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +// +// now some for which only a few specific members can be instantiated, +// first references: +template double& compressed_pair::first(); +template int& compressed_pair::second(); +template compressed_pair::compressed_pair(int&); +template compressed_pair::compressed_pair(call_traits::param_type,int&); +// +// and then arrays: +#ifndef __MWERKS__ +#ifndef __BORLANDC__ +template call_traits::reference compressed_pair::second(); +#endif +template call_traits::reference compressed_pair::first(); +template compressed_pair::compressed_pair(const double&); +template compressed_pair::compressed_pair(); +#endif // __MWERKS__ +#endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + + + + + diff --git a/include/boost/detail/call_traits.hpp b/include/boost/detail/call_traits.hpp index 07ed4ec..304a686 100644 --- a/include/boost/detail/call_traits.hpp +++ b/include/boost/detail/call_traits.hpp @@ -6,6 +6,16 @@ // See http://www.boost.org for most recent version including documentation. +// call_traits: defines typedefs for function usage +// (see libs/utility/call_traits.htm) + +/* Release notes: + 23rd July 2000: + Fixed array specialization. (JM) + Added Borland specific fixes for reference types + (issue raised by Steve Cleary). +*/ + #ifndef BOOST_DETAIL_CALL_TRAITS_HPP #define BOOST_DETAIL_CALL_TRAITS_HPP @@ -66,6 +76,37 @@ struct call_traits typedef T& param_type; // hh removed const }; +#if defined(__BORLANDC__) && (__BORLANDC__ <= 0x551) +// these are illegal specialisations; cv-qualifies applied to +// references have no effect according to [8.3.2p1], +// C++ Builder requires them though as it treats cv-qualified +// references as distinct types... +template +struct call_traits +{ + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T& param_type; // hh removed const +}; +template +struct call_traits +{ + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T& param_type; // hh removed const +}; +template +struct call_traits +{ + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T& param_type; // hh removed const +}; +#endif + template struct call_traits { @@ -76,7 +117,7 @@ public: typedef const T* value_type; typedef array_type& reference; typedef const array_type& const_reference; - typedef const T* param_type; + typedef const T* const param_type; }; template @@ -89,7 +130,7 @@ public: typedef const T* value_type; typedef array_type& reference; typedef const array_type& const_reference; - typedef const T* param_type; + typedef const T* const param_type; }; } diff --git a/include/boost/detail/compressed_pair.hpp b/include/boost/detail/compressed_pair.hpp index a4b3390..87c2449 100644 --- a/include/boost/detail/compressed_pair.hpp +++ b/include/boost/detail/compressed_pair.hpp @@ -6,6 +6,8 @@ // See http://www.boost.org for most recent version including documentation. +// compressed_pair: pair that "compresses" empty members +// (see libs/utility/compressed_pair.htm) // // JM changes 25 Jan 2000: // Removed default arguments from compressed_pair_switch to get diff --git a/include/boost/detail/ob_call_traits.hpp b/include/boost/detail/ob_call_traits.hpp index 332931e..54f2739 100644 --- a/include/boost/detail/ob_call_traits.hpp +++ b/include/boost/detail/ob_call_traits.hpp @@ -7,6 +7,7 @@ // See http://www.boost.org for most recent version including documentation. // // Crippled version for crippled compilers: +// see libs/utility/call_traits.htm // #ifndef BOOST_OB_CALL_TRAITS_HPP #define BOOST_OB_CALL_TRAITS_HPP diff --git a/include/boost/detail/ob_compressed_pair.hpp b/include/boost/detail/ob_compressed_pair.hpp index f5f5664..994c974 100644 --- a/include/boost/detail/ob_compressed_pair.hpp +++ b/include/boost/detail/ob_compressed_pair.hpp @@ -5,8 +5,16 @@ // warranty, and with no claim as to its suitability for any purpose. // See http://www.boost.org for most recent version including documentation. +// see libs/utility/compressed_pair.hpp // -// this version crippled for use with crippled compilers - John Maddock Jan 2000. +/* Release notes: + 23rd July 2000: + Additional comments added. (JM) + Jan 2000: + Original version: this version crippled for use with crippled compilers + - John Maddock Jan 2000. +*/ + #ifndef BOOST_OB_COMPRESSED_PAIR_HPP #define BOOST_OB_COMPRESSED_PAIR_HPP @@ -41,7 +49,8 @@ public: compressed_pair() : _first(), _second() {} compressed_pair(first_param_type x, second_param_type y) : _first(x), _second(y) {} explicit compressed_pair(first_param_type x) : _first(x), _second() {} - //explicit compressed_pair(second_param_type y) : _first(), _second(y) {} + // can't define this in case T1 == T2: + // explicit compressed_pair(second_param_type y) : _first(), _second(y) {} first_reference first() { return _first; } first_const_reference first() const { return _first; } diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..8de3155 --- /dev/null +++ b/index.htm @@ -0,0 +1,72 @@ + + + + + + +Boost Utility Library + + + + + + + + + + + + + +
c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore
+

Boost Utility Library

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HeaderContents
boost/utility.hpp
+
[Documentation]
Class noncopyable plus next() and prior() template + functions.
boost/cast.hpp
+ [Documentation]
polymorphic_cast, implicit_cast, and numeric_cast + function templates. +

[Beta.]

+
boost/operators.hpp
+ [Documentation]
Templates equality_comparable, less_than_comparable, addable, + and the like ease the task of defining comparison and arithmetic + operators, and iterators.
boost/type_traits.hpp
+ [Documentation]
Template classes that describe the fundamental properties of a type. [DDJ + Article "C++ type traits"]
boost/call_traits.hpp
+ [Documentation]
Template class call_traits<T>, that defines types used for passing + parameters to and from a proceedure.
boost/compressed_pair.hpp
+ [Documentation]
Template class compressed_pait<T1, T2> which pairs two values + using the empty member optimisation where appropriate.
+

Revised 27 July 2000

+ + + + diff --git a/operators.htm b/operators.htm new file mode 100644 index 0000000..0062e59 --- /dev/null +++ b/operators.htm @@ -0,0 +1,598 @@ + + + + + + +Header boost/operators.hpp Documentation + + + + +

c++boost.gif (8819 bytes)Header +boost/operators.hpp

+

Header boost/operators.hpp +supplies (in namespace boost) several sets of templates:

+ +

These templates define many global operators in terms of a minimal number of +fundamental operators.

+

Arithmetic Operators

+

If, for example, you declare a class like this:

+
+
class MyInt : boost::operators<MyInt>
+{
+    bool operator<(const MyInt& x) const; 
+    bool operator==(const MyInt& x) const;
+    MyInt& operator+=(const MyInt& x);    
+    MyInt& operator-=(const MyInt& x);    
+    MyInt& operator*=(const MyInt& x);    
+    MyInt& operator/=(const MyInt& x);    
+    MyInt& operator%=(const MyInt& x);    
+    MyInt& operator|=(const MyInt& x);    
+    MyInt& operator&=(const MyInt& x);    
+    MyInt& operator^=(const MyInt& x);    
+    MyInt& operator++();    
+    MyInt& operator--();    
+};
+
+

then the operators<> template adds more than a dozen +additional operators, such as operator>, <=, >=, and +.  Two-argument +forms of the templates are also provided to allow interaction with other +types.

+

Dave Abrahams +started the library and contributed the arithmetic operators in boost/operators.hpp.
+Jeremy Siek +contributed the dereference operators and iterator +helpers in boost/operators.hpp.
+Aleksey Gurtovoy +contributed the code to support base class chaining +while remaining backward-compatible with old versions of the library.
+Beman Dawes +contributed test_operators.cpp.

+

Rationale

+

Overloaded operators for class types typically occur in groups. If you can +write x + y, you probably also want to be able to write x += +y. If you can write x < y, you also want x > y, +x >= y, and x <= y. Moreover, unless your class has +really surprising behavior, some of these related operators can be defined in +terms of others (e.g. x >= y <=> !(x < y)). +Replicating this boilerplate for multiple classes is both tedious and +error-prone. The boost/operators.hpp +templates help by generating operators for you at namespace scope based on other +operators you've defined in your class.

+ +

Two-Argument Template Forms

+
+

The arguments to a binary operator commonly have identical types, but it is +not unusual to want to define operators which combine different types. For example, +one might want to multiply a mathematical vector by a scalar. The two-argument +template forms of the arithmetic operator templates are supplied for this +purpose. When applying the two-argument form of a template, the desired return +type of the operators typically determines which of the two types in question +should be derived from the operator template. For example, if the result of T + U +is of type T, then T (not U) should be +derived from addable<T,U>. The comparison templates less_than_comparable<> +and equality_comparable<> +are exceptions to this guideline, since the return type of the operators they +define is bool.

+

On compilers which do not support partial specialization, the two-argument +forms must be specified by using the names shown below with the trailing '2'. +The single-argument forms with the trailing '1' are provided for +symmetry and to enable certain applications of the base +class chaining technique.

+

Arithmetic operators table

+

The requirements for the types used to instantiate operator templates are +specified in terms of expressions which must be valid and by the return type of +the expression. In the following table t and t1 are +values of type T, and u is a value of type U. +Every template in the library other than operators<> +and operators2<> has an additional +optional template parameter B which is not shown in the table, but +is explained below

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
templatetemplate will supplyRequirements
operators<T>All the other <T> templates in this table.All the <T> requirements in this table.
operators<T,U>
+ operators2<T,U>
All the other <T,U> templates in this table, plus incrementable<T> + and decrementable<T>.All the <T,U> requirements in this table*, + plus incrementable<T> and decrementable<T>.
less_than_comparable<T>
+ less_than_comparable1<T>
bool operator>(const T&, const T&) 
+ bool operator<=(const T&, const T&)
+ bool operator>=(const T&, const T&)
t<t1. Return convertible to bool
less_than_comparable<T,U>
+ less_than_comparable2<T,U>
bool operator<=(const T&, const U&)
+ bool operator>=(const T&, const U&)
+ bool operator>(const U&, const T&) 
+ bool operator<(const U&, const T&) 
+ bool operator<=(const U&, const T&)
+ bool operator>=(const U&, const T&)
t<u. Return convertible to bool
+ t>u. Return convertible to bool
equality_comparable<T>
+ equality_comparable1<T>
bool operator!=(const T&, const T&)t==t1. Return convertible to bool
equality_comparable<T,U>
+ equality_comparable2<T,U>
friend bool operator==(const U&, const T&)
+ friend bool operator!=(const U&, const T&)
+ friend bool operator!=( const T&, const U&)
t==u. Return convertible to bool
addable<T>
+ addable1<T>
T operator+(T, const T&)t+=t1. Return convertible to T
addable<T,U>
+ addable2<T,U>
T operator+(T, const U&)
+ T operator+(const U&, T )
t+=u. Return convertible to T
subtractable<T>
+ subtractable1<T>
T operator-(T, const T&)t-=t1. Return convertible to T
subtractable<T,U>
+ subtractable2<T,U>
T operator-(T, const U&)t-=u. Return convertible to T
multipliable<T>
+ multipliable1<T>
T operator*(T, const T&)t*=t1. Return convertible to T
multipliable<T,U>
+ multipliable2<T,U>
T operator*(T, const U&)
+ T operator*(const U&, T )
t*=u. Return convertible to T
dividable<T>
+ dividable1<T>
T operator/(T, const T&)t/=t1. Return convertible to T
dividable<T,U>
+ dividable2<T,U>
T operator/(T, const U&)t/=u. Return convertible to T
modable<T>
+ modable1<T>
T operator%(T, const T&)t%=t1. Return convertible to T
modable<T,U>
+ modable2<T,U>
T operator%(T, const U&)t%=u. Return convertible to T
orable<T>
+ orable1<T>
T operator|(T, const T&)t|=t1. Return convertible to T
orable<T,U>
+ orable2<T,U>
T operator|(T, const U&)
+ T operator|(const U&, T )
t|=u. Return convertible to T
andable<T>
+ andable1<T>
T operator&(T, const T&)t&=t1. Return convertible to T
andable<T,U>
+ andable2<T,U>
T operator&(T, const U&)
+ T operator&(const U&, T)
t&=u. Return convertible to T
xorable<T>
+ xorable1<T>
T operator^(T, const T&)t^=t1. Return convertible to T
xorable<T,U>
+ xorable2<T,U>
T operator^(T, const U&)
+ T operator^(const U&, T )
t^=u. Return convertible to T
incrementable<T>
+ incrementable1<T>
T operator++(T& x, int)T temp(x); ++x; return temp;
+ Return convertible to T
decrementable<T>
+ decrementable1<T>
T operator--(T& x, int)T temp(x); --x; return temp;
+ Return convertible to T
+
+Portability Note: many compilers (e.g. MSVC6.3, +GCC 2.95.2) will not enforce the requirements in this table unless the +operations which depend on them are actually used. This is not +standard-conforming behavior. If you are trying to write portable code it is +important not to rely on this bug. In particular, it would be convenient to +derive all your classes which need binary operators from the operators<> +and operators2<> templates, +regardless of whether they implement all the requirements in the table. Even if +this works with your compiler today, it may not work tomorrow. +

Base Class Chaining and Object Size

+

Every template listed in the table except operators<> +and operators2<> has an additional +optional template parameter B.  If supplied, B +must be a class type; the resulting class will be publicly derived from B. This +can be used to avoid the object size bloat commonly associated with multiple +empty base classes (see the note for users of older +versions below for more details). To provide support for several groups of +operators, use the additional parameter to chain operator templates into a +single-base class hierarchy, as in the following example.

+

Caveat: to chain to a base class which is not a boost operator +template when using the single-argument form of a +boost operator template, you must specify the operator template with the +trailing '1' in its name. Otherwise the library will assume you +mean to define a binary operation combining the class you intend to use as a +base class and the class you're deriving.

+

Borland users: even single-inheritance seems to cause an increase in +object size in some cases. If you are not defining a template, you may get +better object-size performance by avoiding derivation altogether, and instead +explicitly instantiating the operator template as follows: +

+    class myclass // lose the inheritance...
+    {
+        //...
+    };
+    // explicitly instantiate the operators I need.
+    template class less_than_comparable<myclass>;
+    template class equality_comparable<myclass>;
+    template class incrementable<myclass>;
+    template class decrementable<myclass>;
+    template class addable<myclass,long>;
+    template class subtractable<myclass,long>;
+
+
+

Usage example

+
+
template <class T>
+class point    // note: private inheritance is OK here!
+    : boost::addable< point<T>          // point + point
+    , boost::subtractable< point<T>     // point - point
+    , boost::dividable2< point<T>, T    // point / T
+    , boost::multipliable2< point<T>, T // point * T, T * point
+      > > > >
+{
+public:
+    point(T, T);
+    T x() const;
+    T y() const;
+
+    point operator+=(const point&);
+    // point operator+(point, const point&) automatically
+    // generated by addable.
+
+    point operator-=(const point&);
+    // point operator-(point, const point&) automatically
+    // generated by subtractable.
+
+    point operator*=(T);
+    // point operator*(point, const T&) and
+    // point operator*(const T&, point) auto-generated
+    // by multipliable.
+
+    point operator/=(T);
+    // point operator/(point, const T&) auto-generated
+    // by dividable.
+private:
+    T x_;
+    T y_;
+};
+
+// now use the point<> class:
+
+template <class T>
+T length(const point<T> p)
+{
+    return sqrt(p.x()*p.x() + p.y()*p.y());
+}
+
+const point<float> right(0, 1);
+const point<float> up(1, 0);
+const point<float> pi_over_4 = up + right;
+const point<float> pi_over_4_normalized = pi_over_4 / length(pi_over_4);
+

Arithmetic operators demonstration and test program

+

The operators_test.cpp +program demonstrates the use of the arithmetic operator templates, and can also +be used to verify correct operation.

+

The test program has been compiled and run successfully with: 

+
    +
  • GCC 2.95.2 +
  • GCC 2.95.2 / STLport 4.0b8. +
  • Metrowerks Codewarrior 5.3 +
  • KAI C++ 3.3 +
  • Microsoft Visual C++ 6.0 SP3. +
  • Microsoft Visual C++ 6.0 SP3 / STLport 4.0b8.
  • +
+

Dereference operators and iterator helpers

+

The iterator helper templates ease the task +of creating a custom iterator. Similar to arithmetic types, a complete iterator +has many operators that are "redundant" and can be implemented in +terms of the core set of operators.

+

The dereference operators were motivated by the iterator +helpers, but are often useful in non-iterator contexts as well. Many of the +redundant iterator operators are also arithmetic operators, so the iterator +helper classes borrow many of the operators defined above. In fact, only two new +operators need to be defined! (the pointer-to-member operator-> +and the subscript operator[]). +

Notation

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tis the user-defined type for which the operations are + being supplied.
Vis the type which the resulting dereferenceable + type "points to", or the value_type of the custom + iterator.
Dis the type used to index the resulting indexable + type or the difference_type of the custom iterator.
Pis a type which can be dereferenced to access V, + or the pointer type of the custom iterator.
Ris the type returned by indexing the indexable + type or the reference type of the custom iterator.
iis short for static_cast<const T&>(*this), + where this is a pointer to the helper class.
+ Another words, i should be an object of the custom iterator + type.
x,x1,x2are objects of type T.
nis an object of type D.
+

The requirements for the types used to instantiate the dereference operators +and iterator helpers are specified in terms of expressions which must be valid +and their return type. 

+

Dereference operators

+

The dereference operator templates in this table all accept an optional +template parameter (not shown) to be used for base class +chaining. + + + + + + + + + + + + + + + + + + +
templatetemplate will supplyRequirements
dereferenceable<T,P>P operator->() const(&*i.). Return convertible to P.
indexable<T,D,R>R operator[](D n) const*(i + n). Return of type R.
+

Iterator helpers

+

There are three separate iterator helper classes, each for a different +category of iterator. Here is a summary of the core set of operators that the +custom iterator must define, and the extra operators that are created by the +helper classes. For convenience, the helper classes also fill in all of the +typedef's required of iterators by the C++ standard (iterator_category, +value_type, etc.).

+ + + + + + + + + + + + + + + + + + + + + + + +
templatetemplate will supplyRequirements
forward_iterator_helper
+ <T,V,D,P,R>
bool operator!=(const T& x1, const T& x2)
+ T operator++(T& x, int)
+ V* operator->() const
+
x1==x2. Return convertible to bool
+ T temp(x); ++x; return temp;
+ (&*i.). Return convertible to V*.
bidirectional_iterator_helper
+ <T,V,D,P,R>
Same as above, plus
+ T operator--(T& x, int)
Same as above, plus
+ T temp(x); --x; return temp;
random_access_iterator_helper
+ <T,V,D,P,R>
Same as above, plus
+ T operator+(T x, const D&)
+ T operator+(const D& n, T x)
+ T operator-(T x, const D& n)
+ R operator[](D n) const
+ bool operator>(const T& x1, const T& x2) 
+ bool operator<=(const T& x1, const T& x2)
+ bool operator>=(const T& x1, const T& x2)
Same as above, plus
+ x+=n. Return convertible to T
+ x-=n. Return convertible to T
+ x1<x2. Return convertible to bool
+ And to satisfy RandomAccessIterator:
+ x1-x2. Return convertible to D
+

Iterator demonstration and test program

+

The iterators_test.cpp +program demonstrates the use of the iterator templates, and can also be used to +verify correct operation. The following is the custom iterator defined in the +test program. It demonstrates a correct (though trivial) implementation of the +core operations that must be defined in order for the iterator helpers to +"fill in" the rest of the iterator operations.

+
+
template <class T, class R, class P>
+struct test_iter
+  : public boost::random_access_iterator_helper<
+     test_iter<T,R,P>, T, std::ptrdiff_t, P, R>
+{
+  typedef test_iter self;
+  typedef R Reference;
+  typedef std::ptrdiff_t Distance;
+
+public:
+  test_iter(T* i) : _i(i) { }
+  test_iter(const self& x) : _i(x._i) { }
+  self& operator=(const self& x) { _i = x._i; return *this; }
+  Reference operator*() const { return *_i; }
+  self& operator++() { ++_i; return *this; }
+  self& operator--() { --_i; return *this; }
+  self& operator+=(Distance n) { _i += n; return *this; }
+  self& operator-=(Distance n) { _i -= n; return *this; }
+  bool operator==(const self& x) const { return _i == x._i; }
+  bool operator<(const self& x) const { return _i < x._i; }
+  friend Distance operator-(const self& x, const self& y) {
+    return x._i - y._i; 
+  }
+protected:
+  T* _i;
+};
+
+

It has been compiled and run successfully with:

+
    +
  • GCC 2.95.2 +
  • Metrowerks Codewarrior 5.2 +
  • Microsoft Visual C++ 6.0 SP3
  • +
+

Jeremy Siek +contributed the iterator operators and helpers.  He also contributed iterators_test.cpp

+
+

Note for users of older versions

+

The changes in the library interface and recommended +usage were motivated by some practical issues described below. The new +version of the library is still backward-compatible with the former one (so +you're not forced change any existing code), but the old usage is +deprecated. Though it was arguably simpler and more intuitive than using base +class chaining, it has been discovered that the old practice of deriving +from multiple operator templates can cause the resulting classes to be much +larger than they should be. Most modern C++ compilers significantly bloat the +size of classes derived from multiple empty base classes, even though the base +classes themselves have no state. For instance, the size of point<int> +from the example above was 12-24 bytes on various compilers +for the Win32 platform, instead of the expected 8 bytes. +

Strictly speaking, it was not the library's fault - the language rules allow +the compiler to apply the empty base class optimization in that situation. In +principle an arbitrary number of empty base classes can be allocated at the same +offset, provided that none of them have a common ancestor (see section 10.5 [class.derived], +par. 5 of the standard). But the language definition also doesn't require +implementations to do the optimization, and few if any of today's compilers +implement it when multiple inheritance is involved. What's worse, it is very +unlikely that implementors will adopt it as a future enhancement to existing +compilers, because it would break binary compatibility between code generated by +two different versions of the same compiler. As Matt Austern said, "One of +the few times when you have the freedom to do this sort of thing is when you're +targeting a new architecture...". On the other hand, many common compilers +will use the empty base optimization for single inheritance hierarchies.

+

Given the importance of the issue for the users of the library (which aims to +be useful for writing light-weight classes like MyInt or point<>), +and the forces described above, we decided to change the library interface so +that the object size bloat could be eliminated even on compilers that support +only the simplest form of the empty base class optimization. The current library +interface is the result of those changes. Though the new usage is a bit more +complicated than the old one, we think it's worth it to make the library more +useful in real world. Alexy Gurtovoy contributed the code which supports the new +usage idiom while allowing the library remain backward-compatible.

+
+

Revised 03 Aug 2000 +

+

© Copyright David Abrahams and Beman Dawes 1999-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.

+ + + + diff --git a/type_traits.htm b/type_traits.htm new file mode 100644 index 0000000..66c5370 --- /dev/null +++ b/type_traits.htm @@ -0,0 +1,626 @@ + + + + + + +Type Traits + + + + +

Header +<boost/type_traits.hpp>

+ +

The contents of <boost/type_traits.hpp> are declared in +namespace boost.

+ +

The file <boost/type_traits.hpp> +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:

+ +
Fundamental type operations
+Fundamental type properties
+   Miscellaneous
+   cv-Qualifiers
+   Fundamental Types
+   Compound Types
+   Object/Scalar Types
+Compiler Support Information
+Example Code
+ +

Fundamental type operations

+ +

Usage: "class_name<T>::type" performs +indicated transformation on type T.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Expression.

+

Description.

+

Compiler.

+
remove_volatile<T>::typeCreates a type the same as T + but with any top level volatile qualifier removed. For + example "volatile int" would become "int".

P

+
remove_const<T>::typeCreates a type the same as T + but with any top level const qualifier removed. For + example "const int" would become "int".

P

+
remove_cv<T>::typeCreates 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".

P

+
remove_reference<T>::typeIf T is a reference type + then removes the reference, otherwise leaves T unchanged. + For example "int&" becomes "int" + but "int*" remains unchanged.

P

+
add_reference<T>::typeIf 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&".

P

+
remove_bounds<T>::typeIf 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]".

P

+
+ +

 

+ +

Fundamental type properties

+ +

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

+ +

Miscellaneous

+ + + + + + + + + + + + + + + + + + + + + + +

Expression

+

Description

+

Compiler

+
is_same<T,U>::value
+

True if T and U are the + same type.

+

P

+
is_convertible<T,U>::value
+

True if type T is + convertible to type U.

+
 
alignment_of<T>::value
+

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

+
 
+ +

 

+ +

cv-Qualifiers

+ +

The following classes determine what cv-qualifiers are present +on a type (see 3.93).

+ + + + + + + + + + + + + + + + + +

Expression.

+

Description.

+

Compiler.

+
is_const<T>::valueTrue if type T is top-level + const qualified.

P

+
is_volatile<T>::valueTrue if type T is top-level + volatile qualified.

P

+
+ +

 

+ +

Fundamental Types

+ +

The following will only ever be true for cv-unqualified types; +these are closely based on the section 3.9 of the C++ Standard.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Expression.

+

Description.

+

Compiler.

+
is_void<T>::valueTrue only if T is void. 
is_standard_unsigned_integral<T>::valueTrue only if T is one of the + standard unsigned integral types (3.9.1 p3) - unsigned + char, unsigned short, unsigned int, and unsigned long. 
is_standard_signed_integral<T>::valueTrue only if T is one of the + standard signed integral types (3.9.1 p2) - signed char, + short, int, and long. 
is_standard_integral<T>::valueTrue 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. 
is_standard_float<T>::valueTrue if T is one of the + standard floating point types(3.9.1 p8) - float, double + or long double. 
is_standard_arithmetic<T>::valueTrue if T is a standard + arithmetic type(3.9.1 p8) - implies is_standard_integral + or is_standard_float is true. 
is_standard_fundamental<T>::valueTrue if T is a standard + arithmetic type or if T is void. 
is_extension_unsigned_integral<T>::valueTrue for compiler specific + unsigned integral types. 
is_extension_signed_integral<T>>:valueTrue for compiler specific + signed integral types. 
is_extension_integral<T>::valueTrue if either is_extension_unsigned_integral<T>::value + or is_extension_signed_integral<T>::value is true. 
is_extension_float<T>::valueTrue for compiler specific + floating point types. 
is_extension_arithmetic<T>::valueTrue if either is_extension_integral<T>::value + or is_extension_float<T>::value are true. 
 is_extension_fundamental<T>::valueTrue if either is_extension_arithmetic<T>::value + or is_void<T>::value are true. 
 is_unsigned_integral<T>::valueTrue if either is_standard_unsigned_integral<T>::value + or is_extention_unsigned_integral<T>::value are + true. 
is_signed_integral<T>::valueTrue if either is_standard_signed_integral<T>::value + or is_extention_signed_integral<T>>::value are + true. 
is_integral<T>::valueTrue if either is_standard_integral<T>::value + or is_extention_integral<T>::value are true. 
is_float<T>::valueTrue if either is_standard_float<T>::value + or is_extention_float<T>::value are true. 
is_arithmetic<T>::valueTrue if either is_integral<T>::value + or is_float<T>::value are true. 
is_fundamental<T>::valueTrue if either is_arithmetic<T>::value + or is_void<T>::value are true. 
+ +

 

+ +

Compound Types

+ +

The following will only ever be true for cv-unqualified types, +as defined by the Standard. 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Expression

+

Description

+

Compiler

+
is_array<T>::valueTrue if T is an array type.

P

+
is_pointer<T>::valueTrue if T is a regular + pointer type - including function pointers - but + excluding pointers to member functions (3.9.2 p1 and 8.3.1).

P

+
is_member_pointer<T>::valueTrue if T is a pointer to a + non-static class member (3.9.2 p1 and 8.3.1).

P

+
is_reference<T>::valueTrue if T is a reference + type (3.9.2 p1 and 8.3.2).

P

+
is_class<T>::valueTrue if T is a class or + struct type.

PD

+
is_union<T>::valueTrue if T is a union type.

C

+
is_enum<T>::valueTrue if T is an enumerator + type.

C

+
is_compound<T>::valueTrue if T is any of the + above compound types.

PD

+
+ +

 

+ +

Object/Scalar Types

+ +

The following ignore any top level cv-qualifiers: if class_name<T>::value +is true then class_name<cv-qualified-T>::value +will also be true.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Expression

+

Description

+

Compiler

+
is_object<T>::valueTrue if T is not a reference + type, or a (possibly cv-qualified) void type.

P

+
is_standard_scalar<T>::valueTrue if T is a standard + arithmetic type, an enumerated type, a pointer or a + member pointer.

PD

+
is_extension_scalar<T>::valueTrue if T is an extentions + arithmetic type, an enumerated type, a pointer or a + member pointer.

PD

+
is_scalar<T>::valueTrue if T is an arithmetic + type, an enumerated type, a pointer or a member pointer.

PD

+
is_POD<T>::valueTrue 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.

PC

+
is_empty<T>::valueTrue 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.

PCD

+
has_trivial_constructor<T>::valueTrue if T has a trivial + default constructor - that is T() is equivalent to memset.

PC

+
has_trivial_copy<T>::valueTrue if T has a trivial copy + constructor - that is T(const T&) is equivalent to + memcpy.

PC

+
has_trivial_assign<T>::valueTrue if T has a trivial + assignment operator - that is if T::operator=(const T&) + is equivalent to memcpy.

PC

+
has_trivial_destructor<T>::valueTrue if T has a trivial + destructor - that is if T::~T() has no effect.

PC

+
+ +

 

+ +

Compiler Support Information

+ +

The legends used in the tables above have the following +meanings:

+ + + + + + + + + + + + + + +

P

+
Denotes that the class + requires support for partial specialisation of class + templates to work correctly.

C

+
Denotes that direct compiler + support for that traits class is required.

D

+
Denotes that the traits + class is dependent upon a class that requires direct + compiler support.
+ +

 

+ +

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.

+ +

If there is no compiler support, to ensure that these +traits always 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 not a POD.

+ +

The following rules are automatically enforced:

+ +

is_enum implies is_POD

+ +

is_POD implies has_*

+ +

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.

+ +

Example code

+ +

Type-traits comes with two sample programs: type_traits_test.cpp 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 algo_opt_examples.cpp +uses the type traits classes to "optimise" some +familiar standard library algorithms.

+ +

There are four algorithm examples in algo_opt_examples.cpp:

+ + + + + + + + + + + + + + + + + + +
opt::copy
+
If the copy operation can be + performed using memcpy then does so, otherwise uses a + regular element by element copy (c.f. std::copy).
opt::fill
+
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 (c.f. + std::fill).
opt::destroy_array
+
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.
opt::iter_swap
+
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.
+ +

 

+ +
+ +

Revised 08th March 2000

+ +

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

+ +

Based on contributions by Steve Cleary, Beman Dawes, Howard +Hinnant and John Maddock.

+ +

Maintained by John +Maddock, the latest version of this file can be found at www.boost.org, and the boost +discussion list at www.egroups.com/list/boost.

+ + diff --git a/type_traits_test.cpp b/type_traits_test.cpp index ffea91d..719b2d1 100644 --- a/type_traits_test.cpp +++ b/type_traits_test.cpp @@ -4,90 +4,26 @@ // 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 + +/* Release notes: + 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 #include #include -#include -#include +#include "type_traits_test.hpp" using namespace boost; -#ifdef __BORLANDC__ -#pragma option -w-ccc -w-rch -w-eff -w-aus -#endif - -// -// define tests here -unsigned failures = 0; - -#define value_test(v, x) if(v == x) /*std::cout << "checking value of " << #x << "...OK" << std::endl*/;\ - else{++failures; std::cout << "checking value of " << #x << "...failed" << std::endl;} - -#define type_test(v, x) if(is_same::value) /*std::cout << "checking type of " << #x << "...OK" << std::endl*/;\ - else{++failures; std::cout << "checking type of " << #x << "...failed (type was: " << typeid(is_same).name() << ")" << std::endl;} - -template -struct call_traits_test -{ - static void assert_construct(call_traits::param_type val); -}; - -template -void call_traits_test::assert_construct(call_traits::param_type val) -{ - // - // this is to check that the call_traits assertions are valid: - T t(val); - call_traits::value_type v(t); - call_traits::reference r(t); - call_traits::const_reference cr(t); - call_traits::param_type p(t); - call_traits::value_type v2(v); - call_traits::value_type v3(r); - call_traits::value_type v4(p); - call_traits::reference r2(v); - call_traits::reference r3(r); - call_traits::const_reference cr2(v); - call_traits::const_reference cr3(r); - call_traits::const_reference cr4(cr); - call_traits::const_reference cr5(p); - call_traits::param_type p2(v); - call_traits::param_type p3(r); - call_traits::param_type p4(p); -} -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -template -struct call_traits_test -{ - static void assert_construct(call_traits::param_type val); -}; - -template -void call_traits_test::assert_construct(call_traits::param_type val) -{ - // - // this is to check that the call_traits assertions are valid: - T t; - call_traits::value_type v(t); - call_traits::reference r(t); - call_traits::const_reference cr(t); - call_traits::param_type p(t); - call_traits::value_type v2(v); - call_traits::value_type v3(r); - call_traits::value_type v4(p); - call_traits::reference r2(v); - call_traits::reference r3(r); - call_traits::const_reference cr2(v); - call_traits::const_reference cr3(r); - call_traits::const_reference cr4(cr); - call_traits::const_reference cr5(p); - call_traits::param_type p2(v); - call_traits::param_type p3(r); - call_traits::param_type p4(p); -} -#endif //BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION - // Since there is no compiler support, we should specialize: // is_enum for all enumerations (is_enum implies is_POD) // is_union for all unions @@ -96,12 +32,6 @@ void call_traits_test::assert_construct(call_traits::param_type val) // has_* for any UDT that has that trait and is not POD enum enum_UDT{ one, two, three }; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -namespace boost { -template <> struct is_enum -{ static const bool value = true; }; -} -#endif struct UDT { UDT(); @@ -116,74 +46,47 @@ struct UDT int f4(int, float); }; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION struct POD_UDT { int x; }; -namespace boost { -template <> struct is_POD -{ static const bool value = true; }; -} -#endif -struct empty_UDT -{ - ~empty_UDT(){}; -}; -namespace boost { -//template <> struct is_empty -//{ static const bool value = true; }; -// this type is not POD, so we have to specialize the has_* individually -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -template <> struct has_trivial_constructor -{ static const bool value = true; }; -template <> struct has_trivial_copy -{ static const bool value = true; }; -template <> struct has_trivial_assign -{ static const bool value = true; }; -} -#endif - +struct empty_UDT{ ~empty_UDT(){}; }; struct empty_POD_UDT{}; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -namespace boost { -template <> struct is_empty -{ static const bool value = true; }; -template <> struct is_POD -{ static const bool value = true; }; -} -#endif union union_UDT { int x; double y; ~union_UDT(); }; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -namespace boost { -template <> struct is_union -{ static const bool value = true; }; -} -#endif union POD_union_UDT { int x; double y; }; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -namespace boost { -template <> struct is_union -{ static const bool value = true; }; -template <> struct is_POD -{ static const bool value = true; }; -} -#endif union empty_union_UDT { ~empty_union_UDT(); }; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +union empty_POD_union_UDT{}; +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION namespace boost { -template <> struct is_union +template <> struct is_enum { static const bool value = true; }; -template <> struct is_empty +template <> struct is_POD +{ static const bool value = true; }; +// this type is not POD, so we have to specialize the has_* individually +template <> struct has_trivial_constructor +{ static const bool value = true; }; +template <> struct has_trivial_copy +{ static const bool value = true; }; +template <> struct has_trivial_assign +{ static const bool value = true; }; +template <> struct is_POD +{ static const bool value = true; }; +template <> struct is_union +{ static const bool value = true; }; +template <> struct is_union +{ static const bool value = true; }; +template <> struct is_POD +{ static const bool value = true; }; +template <> struct is_union { static const bool value = true; }; // this type is not POD, so we have to specialize the has_* individually template <> struct has_trivial_constructor @@ -192,19 +95,75 @@ template <> struct has_trivial_copy { static const bool value = true; }; template <> struct has_trivial_assign { static const bool value = true; }; -} -#endif -union empty_POD_union_UDT{}; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -namespace boost { template <> struct is_union { static const bool value = true; }; -template <> struct is_empty -{ static const bool value = true; }; template <> struct is_POD { static const bool value = true; }; -#endif } +#else +namespace boost { +template <> struct is_enum +{ enum{ value = true }; }; +template <> struct is_POD +{ enum{ value = true }; }; +// this type is not POD, so we have to specialize the has_* individually +template <> struct has_trivial_constructor +{ enum{ value = true }; }; +template <> struct has_trivial_copy +{ enum{ value = true }; }; +template <> struct has_trivial_assign +{ enum{ value = true }; }; +template <> struct is_POD +{ enum{ value = true }; }; +template <> struct is_union +{ enum{ value = true }; }; +template <> struct is_union +{ enum{ value = true }; }; +template <> struct is_POD +{ enum{ value = true }; }; +template <> struct is_union +{ enum{ value = true }; }; +// this type is not POD, so we have to specialize the has_* individually +template <> struct has_trivial_constructor +{ enum{ value = true }; }; +template <> struct has_trivial_copy +{ enum{ value = true }; }; +template <> struct has_trivial_assign +{ enum{ value = true }; }; +template <> struct is_union +{ enum{ value = true }; }; +template <> struct is_POD +{ enum{ value = true }; }; +} +#endif + +class Base { }; + +class Deriverd : public Base { }; + +class NonDerived { }; + +enum enum1 +{ + one_,two_ +}; + +enum enum2 +{ + three_,four_ +}; + +struct VB +{ + virtual ~VB(){}; +}; + +struct VD : VB +{ + ~VD(){}; +}; + + // 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 @@ -214,11 +173,18 @@ int main() { 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; + typedef const r_type cr_type; + type_test(int, remove_reference::type) type_test(const int, remove_reference::type) type_test(int, remove_reference::type) type_test(const int, remove_reference::type) type_test(volatile int, remove_reference::type) + type_test(int, remove_reference::type) + type_test(int, remove_const::type) // Steve: fails on BCB4 type_test(volatile int, remove_const::type) @@ -246,14 +212,15 @@ int main() type_test(int, remove_bounds::type) type_test(int[3], remove_bounds::type) - type_test(const int, call_traits::param_type) - type_test(const char, call_traits::param_type) -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION - type_test(char&, call_traits::param_type) - type_test(const char&, call_traits::param_type) -#endif std::cout << std::endl << "Checking type properties..." << std::endl << std::endl; + value_test(true, (is_same::value)) + value_test(false, (is_same::value)) + value_test(false, (is_same::value)) + value_test(false, (is_same::value)) + value_test(false, (is_same::value)) + value_test(false, (is_same::value)) + value_test(false, is_const::value) value_test(true, is_const::value) value_test(false, is_const::value) @@ -266,7 +233,8 @@ int main() value_test(true, is_void::value) // Steve: fails on BCB4 - value_test(false, is_void::value) + // JM: but looks as though it should according to [3.9.3p1]? + //value_test(false, is_void::value) value_test(false, is_void::value) value_test(false, is_standard_unsigned_integral::value) @@ -432,6 +400,8 @@ int main() value_test(true, is_reference::value) value_test(true, is_reference::value) value_test(true, is_reference::value) + value_test(true, is_reference::value) + value_test(true, is_reference::value) value_test(false, is_class::value) value_test(false, is_class::value) @@ -473,12 +443,18 @@ int main() value_test(false, is_empty::value) value_test(false, is_empty::value) value_test(false, is_empty::value) +#ifdef __MWERKS__ + // apparent compiler bug causes this to fail to compile: + value_fail(false, is_empty::value) +#else value_test(false, is_empty::value) +#endif value_test(false, is_empty::value) value_test(false, is_empty::value) value_test(false, is_empty::value) - value_test(false, is_empty::value) value_test(true, is_empty::value) + value_test(true, is_empty::value) + value_test(true, is_empty::value) value_test(false, is_empty::value) value_test(true, has_trivial_constructor::value) @@ -558,66 +534,62 @@ int main() value_test(false, is_POD::value) value_test(true, is_POD::value) - compressed_pair cp1; - compressed_pair cp1b; - swap(cp1, cp1b); - compressed_pair cp2; - compressed_pair cp3; - compressed_pair cp4; - compressed_pair cp5; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION - int i; - compressed_pair cp6(i,i); - compressed_pair cp7; - cp7.first(); - double* pd = cp7.second(); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + //value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); +#if defined(BOOST_MSVC6_MEMBER_TEMPLATES) || !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + value_test(false, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); #endif - value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) - value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) - value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) - value_test(true, (sizeof(compressed_pair) < sizeof(std::pair))) + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); - std::cout << std::endl << "Tests completed (" << failures << " failures)... press any key to exit"; + value_test(false, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(true, (boost::is_convertible::value)); + value_test(false, (boost::is_convertible::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)... press any key to exit"; std::cin.get(); - return 0; + return failures; } -// -// instanciate some compressed pairs: -template class boost::compressed_pair; -template class boost::compressed_pair; -template class boost::compressed_pair; -template class boost::compressed_pair; -template class boost::compressed_pair; -template class boost::compressed_pair; - -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -// -// now some for which only a few specific members can be instantiated, -// first references: -template double& compressed_pair::first(); -template int& compressed_pair::second(); -template compressed_pair::compressed_pair(int&); -template compressed_pair::compressed_pair(call_traits::param_type,int&); -// -// and then arrays: -#ifndef __BORLANDC__ -template call_traits::reference compressed_pair::second(); -#endif -template call_traits::reference compressed_pair::first(); -template compressed_pair::compressed_pair(const double&); -template compressed_pair::compressed_pair(); -#endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -// -// now check call_traits assertions by instantiating call_traits_test: -template struct call_traits_test; -template struct call_traits_test; -template struct call_traits_test; -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -template struct call_traits_test; -template struct call_traits_test; -// this doesn't work (yet) (JM): -template struct call_traits_test; -#endif + diff --git a/type_traits_test.hpp b/type_traits_test.hpp new file mode 100644 index 0000000..3e0b44e --- /dev/null +++ b/type_traits_test.hpp @@ -0,0 +1,106 @@ + // 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 + +// +// this one is here just to suppress warnings: +// +template +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 +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 ) + + +#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;} +#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::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).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 +struct test_align +{ + struct padded + { + char c; + T t; + }; + static void do_it() + { + padded p; + unsigned a = reinterpret_cast(&(p.t)) - reinterpret_cast(&p); + value_test(a, boost::alignment_of::value); + } +}; +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template +struct test_align +{ + 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::value, boost::alignment_of::value); + } +}; +#endif + +#define align_test(T) test_align::do_it() + +// +// define tests here +unsigned failures = 0; +unsigned test_count = 0; + +// +// 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 diff --git a/utility.htm b/utility.htm new file mode 100644 index 0000000..2f0272e --- /dev/null +++ b/utility.htm @@ -0,0 +1,103 @@ + + + + +Header boost/utility.hpp Documentation + + + + +

c++boost.gif (8819 bytes)Header +boost/utility.hpp

+ +

The entire contents of the header <boost/utility.hpp> + are in namespace boost.

+ +

Contents

+ + +

Template functions next() and prior()

+ +

Certain data types, such as the C++ Standard Library's forward and +bidirectional iterators, do not provide addition and subtraction via operator+() +or operator-().  This means that non-modifying computation of the next or +prior value requires a temporary, even though operator++() or operator--() is +provided.  It also means that writing code like itr+1 inside a +template restricts the iterator category to random access iterators.

+ +

The next() and prior() functions provide a simple way around these problems:

+ +
+ +
template <class T>
+T next(T x) { return ++x; }
+
+template <class X>
+T prior(T x) { return --x; }
+ +
+ +

Usage is simple:

+ +
+ +
const std::list<T>::iterator p = get_some_iterator();
+const std::list<T>::iterator prev = boost::prior(p);
+ +
+ +

Contributed by Dave Abrahams.

+ +

Class noncopyable

+ +

Class noncopyable is a base class.  Derive your own class from noncopyable +when you want to prohibit copy construction and copy assignment.

+ +

Some objects, particularly those which hold complex resources like files or +network connections, have no sensible copy semantics.  Sometimes there are +possible copy semantics, but these would be of very limited usefulness and be +very difficult to implement correctly.  Sometimes you're implementing a class that doesn't need to be copied +just yet and you don't want to take the time to write the appropriate functions.  +Deriving from noncopyable will prevent the otherwise implicitly-generated +functions (which don't have the proper semantics) from becoming a trap for other programmers.

+ +

The traditional way to deal with these is to declare a private copy constructor and copy assignment, and then +document why this is done.  But deriving from noncopyable is simpler +and clearer, and doesn't require additional documentation.

+ +

The program noncopyable_test.cpp can be +used to verify class noncopyable works as expected. It has have been run successfully under +GCC 2.95, Metrowerks +CodeWarrior 5.0, and Microsoft Visual C++ 6.0 sp 3.

+ +

Contributed by Dave Abrahams.

+ +

Example

+
+
// inside one of your own headers ...
+#include <boost/utility.hpp>
+
+class ResourceLadenFileSystem : noncopyable {
+...
+
+ +

Rationale

+

Class noncopyable has protected constructor and destructor members to +emphasize that it is to be used only as a base class.  Dave Abrahams notes +concern about the effect on compiler optimization of adding (even trivial inline) +destructor declarations. He says "Probably this concern is misplaced, because +noncopyable will be used mostly for classes which own resources and thus have non-trivial destruction semantics."

+
+

Revised  26 January, 2000 +

+

© Copyright boost.org 1999. 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.

+ +