From b7c8e0c17f8351e152b69ac4f16be703a42a5d4f Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 4 Jun 2001 11:57:37 +0000 Subject: [PATCH] changes from Daryle Walker [SVN r10264] --- include/boost/operators.hpp | 381 ++++++--- iterators_test.cpp | 382 ++++++--- operators.htm | 1513 +++++++++++++++++++++++------------ operators_test.cpp | 522 ++++++++---- 4 files changed, 1921 insertions(+), 877 deletions(-) diff --git a/include/boost/operators.hpp b/include/boost/operators.hpp index e6a2cb0..14d4c31 100644 --- a/include/boost/operators.hpp +++ b/include/boost/operators.hpp @@ -1,20 +1,20 @@ // Boost operators.hpp header file ----------------------------------------// -// (C) Copyright David Abrahams 1999. 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. - -// (C) Copyright Jeremy Siek 1999. 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. +// (C) Copyright David Abrahams, Jeremy Siek, and Daryle Walker 1999-2001. +// 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. // See http://www.boost.org for most recent version including documentation. // Revision History +// 29 May 01 Added operator classes for << and >>. Added input and output +// iterator helper classes. Added classes to connect equality and +// relational operators. Added classes for groups of related +// operators. Reimplemented example operator and iterator helper +// classes in terms of the new groups. (Daryle Walker, with help +// from Alexy Gurtovoy) // 11 Feb 01 Fixed bugs in the iterator helpers which prevented explicitly // supplied arguments from actually being used (Dave Abrahams) // 04 Jul 00 Fixed NO_OPERATORS_IN_NAMESPACE bugs, major cleanup and @@ -282,12 +282,190 @@ struct indexable : B } }; +// More operator classes (contributed by Daryle Walker) --------------------// + +template +struct left_shiftable2 : B +{ + friend T operator<<(T x, const U& y) { return x <<= y; } +}; + +template +struct left_shiftable1 : B +{ + friend T operator<<(T x, const T& y) { return x <<= y; } +}; + +template +struct right_shiftable2 : B +{ + friend T operator>>(T x, const U& y) { return x >>= y; } +}; + +template +struct right_shiftable1 : B +{ + friend T operator>>(T x, const T& y) { return x >>= y; } +}; + +template +struct equivalent2 : B +{ + friend bool operator==(const T& x, const U& y) + { + return !(x < y) && !(x > y); + } +}; + +template +struct equivalent1 : B +{ + friend bool operator==(const T&x, const T&y) + { + return !(x < y) && !(y < x); + } +}; + +template +struct partially_ordered2 : B +{ + friend bool operator<=(const T& x, const U& y) + { return (x < y) || (x == y); } + friend bool operator>=(const T& x, const U& y) + { return (x > y) || (x == y); } + friend bool operator>(const U& x, const T& y) + { return y < x; } + friend bool operator<(const U& x, const T& y) + { return y > x; } + friend bool operator<=(const U& x, const T& y) + { return (y > x) || (y == x); } + friend bool operator>=(const U& x, const T& y) + { return (y < x) || (y == x); } +}; + +template +struct partially_ordered1 : B +{ + friend bool operator>(const T& x, const T& y) + { return y < x; } + friend bool operator<=(const T& x, const T& y) + { return (x < y) || (x == y); } + friend bool operator>=(const T& x, const T& y) + { return (y < x) || (x == y); } +}; + +// Combined operator classes (contributed by Daryle Walker) ----------------// + +template +struct totally_ordered2 + : less_than_comparable2 > {}; + +template +struct totally_ordered1 + : less_than_comparable1 > {}; + +template +struct additive2 + : addable2 > {}; + +template +struct additive1 + : addable1 > {}; + +template +struct multiplicative2 + : multipliable2 > {}; + +template +struct multiplicative1 + : multipliable1 > {}; + +template +struct integer_multiplicative2 + : multiplicative2 > {}; + +template +struct integer_multiplicative1 + : multiplicative1 > {}; + +template +struct arithmetic2 + : additive2 > {}; + +template +struct arithmetic1 + : additive1 > {}; + +template +struct integer_arithmetic2 + : additive2 > {}; + +template +struct integer_arithmetic1 + : additive1 > {}; + +template +struct bitwise2 + : xorable2 > > {}; + +template +struct bitwise1 + : xorable1 > > {}; + +template +struct unit_steppable + : incrementable > {}; + +template +struct shiftable2 + : left_shiftable2 > {}; + +template +struct shiftable1 + : left_shiftable1 > {}; + #ifndef BOOST_NO_OPERATORS_IN_NAMESPACE } // namespace boost #endif // BOOST_NO_OPERATORS_IN_NAMESPACE -// BOOST_IMPORT_TEMPLATE1/BOOST_IMPORT_TEMPLATE2 - +// BOOST_IMPORT_TEMPLATE1 .. BOOST_IMPORT_TEMPLATE3 - // // When BOOST_NO_OPERATORS_IN_NAMESPACE is defined we need a way to import an // operator template into the boost namespace. BOOST_IMPORT_TEMPLATE1 is used @@ -295,12 +473,31 @@ struct indexable : B // two-argument forms. Note that these macros expect to be invoked from within // boost. -#if defined(BOOST_NO_OPERATORS_IN_NAMESPACE) +#ifndef BOOST_NO_OPERATORS_IN_NAMESPACE -# if defined(BOOST_NO_USING_TEMPLATE) + // The template is already in boost so we have nothing to do. +# define BOOST_IMPORT_TEMPLATE3(template_name) +# define BOOST_IMPORT_TEMPLATE2(template_name) +# define BOOST_IMPORT_TEMPLATE1(template_name) + +#else // BOOST_NO_OPERATORS_IN_NAMESPACE + +# ifndef BOOST_NO_USING_TEMPLATE + + // Bring the names in with a using-declaration + // to avoid stressing the compiler. +# define BOOST_IMPORT_TEMPLATE3(template_name) using ::template_name; +# define BOOST_IMPORT_TEMPLATE2(template_name) using ::template_name; +# define BOOST_IMPORT_TEMPLATE1(template_name) using ::template_name; + +# else + + // Otherwise, because a Borland C++ 5.5 bug prevents a using declaration + // from working, we are forced to use inheritance for that compiler. +# define BOOST_IMPORT_TEMPLATE3(template_name) \ + template \ + struct template_name : ::template_name {}; - // Because a Borland C++ 5.5 bug prevents a using declaration from working, - // we are forced to use inheritance for that compiler. # define BOOST_IMPORT_TEMPLATE2(template_name) \ template \ struct template_name : ::template_name {}; @@ -309,21 +506,8 @@ struct indexable : B template \ struct template_name : ::template_name {}; -# else - - // Otherwise, bring the names in with a using-declaration to avoid - // stressing the compiler -# define BOOST_IMPORT_TEMPLATE2(template_name) using ::template_name; -# define BOOST_IMPORT_TEMPLATE1(template_name) using ::template_name; - # endif // BOOST_NO_USING_TEMPLATE -#else // !BOOST_NO_OPERATORS_IN_NAMESPACE - - // The template is already in boost so we have nothing to do. -# define BOOST_IMPORT_TEMPLATE2(template_name) -# define BOOST_IMPORT_TEMPLATE1(template_name) - #endif // BOOST_NO_OPERATORS_IN_NAMESPACE // @@ -332,7 +516,7 @@ struct indexable : B // the xxxx, xxxx1, and xxxx2 templates, importing them into boost:: as // neccessary. // -#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // is_chained_base<> - a traits class used to distinguish whether an operator // template argument is being used for base class chaining, or is specifying a @@ -355,6 +539,15 @@ template struct is_chained_base { } // namespace boost +// Import a 3-type-argument operator template into boost (if neccessary) and +// provide a specialization of 'is_chained_base<>' for it. +# define BOOST_OPERATOR_TEMPLATE3(template_name3) \ + BOOST_IMPORT_TEMPLATE3(template_name3) \ + template \ + struct is_chained_base< ::boost::template_name3 > { \ + typedef ::boost::detail::true_t value; \ + }; + // Import a 2-type-argument operator template into boost (if neccessary) and // provide a specialization of 'is_chained_base<>' for it. # define BOOST_OPERATOR_TEMPLATE2(template_name2) \ @@ -414,6 +607,8 @@ BOOST_OPERATOR_TEMPLATE1(template_name##1) #else // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +# define BOOST_OPERATOR_TEMPLATE3(template_name3) \ + BOOST_IMPORT_TEMPLATE3(template_name3) # define BOOST_OPERATOR_TEMPLATE2(template_name2) \ BOOST_IMPORT_TEMPLATE2(template_name2) # define BOOST_OPERATOR_TEMPLATE1(template_name1) \ @@ -442,47 +637,41 @@ BOOST_OPERATOR_TEMPLATE(orable) BOOST_OPERATOR_TEMPLATE1(incrementable) BOOST_OPERATOR_TEMPLATE1(decrementable) + BOOST_OPERATOR_TEMPLATE2(dereferenceable) +BOOST_OPERATOR_TEMPLATE3(indexable) -// indexable doesn't follow the patterns above (it has 4 template arguments), so -// we just write out the compiler hacks explicitly. -#ifdef BOOST_NO_OPERATORS_IN_NAMESPACE -# ifdef BOOST_NO_USING_TEMPLATE - template - struct indexable : ::indexable {}; -# else - using ::indexable; -# endif -#endif +BOOST_OPERATOR_TEMPLATE(left_shiftable) +BOOST_OPERATOR_TEMPLATE(right_shiftable) +BOOST_OPERATOR_TEMPLATE(equivalent) +BOOST_OPERATOR_TEMPLATE(partially_ordered) -#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -template -struct is_chained_base< ::boost::indexable > { - typedef ::boost::detail::true_t operator_template_type; -}; -#endif +BOOST_OPERATOR_TEMPLATE(totally_ordered) +BOOST_OPERATOR_TEMPLATE(additive) +BOOST_OPERATOR_TEMPLATE(multiplicative) +BOOST_OPERATOR_TEMPLATE(integer_multiplicative) +BOOST_OPERATOR_TEMPLATE(arithmetic) +BOOST_OPERATOR_TEMPLATE(integer_arithmetic) +BOOST_OPERATOR_TEMPLATE(bitwise) +BOOST_OPERATOR_TEMPLATE1(unit_steppable) +BOOST_OPERATOR_TEMPLATE(shiftable) #undef BOOST_OPERATOR_TEMPLATE +#undef BOOST_OPERATOR_TEMPLATE3 #undef BOOST_OPERATOR_TEMPLATE2 #undef BOOST_OPERATOR_TEMPLATE1 #undef BOOST_IMPORT_TEMPLATE1 #undef BOOST_IMPORT_TEMPLATE2 +#undef BOOST_IMPORT_TEMPLATE3 // The following 'operators' classes can only be used portably if the derived class // declares ALL of the required member operators. template struct operators2 - : less_than_comparable2 > > > > > > > > > {}; + : totally_ordered2 > > {}; #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION template @@ -492,31 +681,47 @@ template struct operators #else template struct operators #endif - : less_than_comparable > > > > > > > > > > > {}; + : totally_ordered > > > {}; // Iterator helper classes (contributed by Jeremy Siek) -------------------// +// (Input and output iterator helpers contributed by Daryle Walker) -------// +// (Changed to use combined operator classes by Daryle Walker) ------------// +template +struct input_iterator_helper + : equality_comparable1 > > > {}; + +template +struct output_iterator_helper + : incrementable > {}; + template struct forward_iterator_helper - : equality_comparable > > > {}; template struct bidirectional_iterator_helper - : equality_comparable > > > > {}; + : equality_comparable1 > > > {}; template struct random_access_iterator_helper - : equality_comparable > > > > > > > > + : totally_ordered1 > > > > > { -#ifndef __BORLANDC__ friend D requires_difference_operator(const T& x, const T& y) { return x - y; } -#endif }; // random_access_iterator_helper } // namespace boost diff --git a/iterators_test.cpp b/iterators_test.cpp index e264aff..852bf09 100644 --- a/iterators_test.cpp +++ b/iterators_test.cpp @@ -9,16 +9,25 @@ // See http://www.boost.org for most recent version including documentation. // Revision History +// 29 May 01 Factored implementation, added comparison tests, use Test Tools +// library (Daryle Walker) // 12 Dec 99 Initial version with iterator operators (Jeremy Siek) -#include -#include -using namespace std; +#define BOOST_INCLUDE_MAIN +#include // for main -#include -using namespace boost; +#include // for BOOST_STATIC_CONSTANT +#include // for boost::exit_success +#include // for boost::random_access_iterator_helper + +#include // for std::ptrdiff_t, std::size_t +#include // for std::strcmp +#include // for std::cout (std::endl, ends, and flush indirectly) +#include // for std::string +#include // for std::ostrstream +// Iterator test class template struct test_iter : public boost::random_access_iterator_helper< @@ -29,7 +38,7 @@ struct test_iter typedef std::ptrdiff_t Distance; public: - test_iter(T* i) : _i(i) { } + explicit test_iter(T* i =0) : _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; } @@ -43,127 +52,280 @@ public: return x._i - y._i; } protected: - T* _i; + P _i; }; - -int -main() +// Iterator operator testing classes +class test_opr_base { - string array[] = { "apple", "orange", "pear", "peach", "grape", "plum" }; - { - test_iter i = array, - ie = array + sizeof(array)/sizeof(string); +protected: + // Test data and types + BOOST_STATIC_CONSTANT( std::size_t, fruit_length = 6u ); + BOOST_STATIC_CONSTANT( std::size_t, scratch_length = 40u ); - // Tests for all of the operators added by random_access_iterator_helper + typedef std::string fruit_array_type[ fruit_length ]; + typedef char scratch_array_type[ scratch_length ]; - // test i++ - while (i != ie) - cout << *i++ << " "; - cout << endl; - i = array; + static fruit_array_type fruit; + static scratch_array_type scratch; - // test i-- - while (ie != i) { - ie--; - cout << *ie << " "; - } - cout << endl; - ie = array + sizeof(array)/sizeof(string); +}; // test_opr_base - // test i->m - while (i != ie) { - cout << i->size() << " "; - ++i; - } - cout << endl; - i = array; +template +class test_opr + : public test_opr_base +{ + typedef test_opr self_type; - // test i + n - while (i < ie) { - cout << *i << " "; - i = i + 2; - } - cout << endl; - i = array; +public: + // Types + typedef T value_type; + typedef R reference; + typedef P pointer; - // test n + i - while (i < ie) { - cout << *i << " "; - i = ptrdiff_t(2) + i; - } - cout << endl; - i = array; + typedef test_iter iter_type; - // test i - n - while (ie > i) { - ie = ie - 2; - cout << *ie << " "; - } - cout << endl; - ie = array + sizeof(array)/sizeof(string); + // Test controller + static void master_test( char const name[] ); - // test i[n] - for (std::size_t j = 0; j < sizeof(array)/sizeof(string); ++j) - cout << i[j] << " "; - cout << endl; - } - { - test_iter i = array, - ie = array + sizeof(array)/sizeof(string); +private: + // Test data + static iter_type const fruit_begin, fruit_end; - // Tests for all of the operators added by random_access_iterator_helper + // Test parts + static void post_increment_test(); + static void post_decrement_test(); + static void indirect_referral_test(); + static void offset_addition_test(); + static void reverse_offset_addition_test(); + static void offset_subtraction_test(); + static void comparison_test(); + static void indexing_test(); - // test i++ - while (i != ie) - cout << *i++ << " "; - cout << endl; - i = array; +}; // test_opr - // test i-- - while (ie != i) { - ie--; - cout << *ie << " "; - } - cout << endl; - ie = array + sizeof(array)/sizeof(string); - // test i->m - while (i != ie) { - cout << i->size() << " "; - ++i; - } - cout << endl; - i = array; +// Class-static data definitions +typename test_opr_base::fruit_array_type + test_opr_base::fruit = { "apple", "orange", "pear", "peach", "grape", "plum" }; - // test i + n - while (i < ie) { - cout << *i << " "; - i = i + 2; - } - cout << endl; - i = array; +typename test_opr_base::scratch_array_type + test_opr_base::scratch = ""; - // test n + i - while (i < ie) { - cout << *i << " "; - i = ptrdiff_t(2) + i; - } - cout << endl; - i = array; +template +typename test_opr::iter_type const + test_opr::fruit_begin( fruit ); - // test i - n - while (ie > i) { - ie = ie - 2; - cout << *ie << " "; - } - cout << endl; - ie = array + sizeof(array)/sizeof(string); +template +typename test_opr::iter_type const + test_opr::fruit_end( fruit + fruit_length ); - // test i[n] - for (std::size_t j = 0; j < sizeof(array)/sizeof(string); ++j) - cout << i[j] << " "; - cout << endl; - } - return 0; + +// Main testing function +int +test_main( int , char * [] ) +{ + using std::string; + + typedef test_opr test1_type; + typedef test_opr test2_type; + + test1_type::master_test( "non-const string" ); + test2_type::master_test( "const string" ); + + return boost::exit_success; +} + +// Tests for all of the operators added by random_access_iterator_helper +template +void +test_opr::master_test +( + char const name[] +) +{ + std::cout << "Doing test run for " << name << '.' << std::endl; + + post_increment_test(); + post_decrement_test(); + indirect_referral_test(); + offset_addition_test(); + reverse_offset_addition_test(); + offset_subtraction_test(); + comparison_test(); + indexing_test(); +} + +// Test post-increment +template +void +test_opr::post_increment_test +( +) +{ + std::cout << "\tDoing post-increment test." << std::endl; + + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_begin ; i != fruit_end ; ) + { + oss << *i++ << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "apple orange pear peach grape plum ") + == 0 ); +} + +// Test post-decrement +template +void +test_opr::post_decrement_test +( +) +{ + std::cout << "\tDoing post-decrement test." << std::endl; + + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_end ; i != fruit_begin ; ) + { + i--; + oss << *i << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "plum grape peach pear orange apple ") + == 0 ); +} + +// Test indirect structure referral +template +void +test_opr::indirect_referral_test +( +) +{ + std::cout << "\tDoing indirect reference test." << std::endl; + + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_begin ; i != fruit_end ; ++i ) + { + oss << i->size() << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "5 6 4 5 5 4 ") == 0 ); +} + +// Test offset addition +template +void +test_opr::offset_addition_test +( +) +{ + std::cout << "\tDoing offset addition test." << std::endl; + + std::ptrdiff_t const two = 2; + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_begin ; i != fruit_end ; i = i + two ) + { + oss << *i << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "apple pear grape ") == 0 ); +} + +// Test offset addition, in reverse order +template +void +test_opr::reverse_offset_addition_test +( +) +{ + std::cout << "\tDoing reverse offset addition test." << std::endl; + + std::ptrdiff_t const two = 2; + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_begin ; i != fruit_end ; i = two + i ) + { + oss << *i << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "apple pear grape ") == 0 ); +} + +// Test offset subtraction +template +void +test_opr::offset_subtraction_test +( +) +{ + std::cout << "\tDoing offset subtraction test." << std::endl; + + std::ptrdiff_t const two = 2; + std::ostrstream oss( scratch, scratch_length ); + for ( iter_type i = fruit_end ; fruit_begin < i ; ) + { + i = i - two; + if ( (fruit_begin < i) || (fruit_begin == i) ) + { + oss << *i << ' '; + } + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "grape pear apple ") == 0 ); +} + +// Test comparisons +template +void +test_opr::comparison_test +( +) +{ + using std::cout; + using std::ptrdiff_t; + + cout << "\tDoing comparison tests.\n\t\tPass:"; + + for ( iter_type i = fruit_begin ; i != fruit_end ; ++i ) + { + ptrdiff_t const i_offset = i - fruit_begin; + + cout << ' ' << *i << std::flush; + for ( iter_type j = fruit_begin ; j != fruit_end ; ++j ) + { + ptrdiff_t const j_offset = j - fruit_begin; + + BOOST_TEST( (i != j) == (i_offset != j_offset) ); + BOOST_TEST( (i > j) == (i_offset > j_offset) ); + BOOST_TEST( (i <= j) == (i_offset <= j_offset) ); + BOOST_TEST( (i >= j) == (i_offset >= j_offset) ); + } + } + cout << std::endl; +} + +// Test indexing +template +void +test_opr::indexing_test +( +) +{ + std::cout << "\tDoing indexing test." << std::endl; + + std::ostrstream oss( scratch, scratch_length ); + for ( std::size_t k = 0u ; k < fruit_length ; ++k ) + { + oss << fruit_begin[ k ] << ' '; + } + + oss << std::ends; + BOOST_TEST( std::strcmp(oss.str(), "apple orange pear peach grape plum ") + == 0 ); } diff --git a/operators.htm b/operators.htm index 3e9e7bc..8f78f51 100644 --- a/operators.htm +++ b/operators.htm @@ -1,28 +1,81 @@ + - - - -Header boost/operators.hpp Documentation + +Header <boost/operators.hpp> Documentation - + + +

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

+ +

The header <boost/operators.hpp> +supplies several sets of class templates (in namespace +boost). These templates define operators at namespace +scope in terms of a minimal number of fundamental operators +provided by the class.

+ +

Contents

-

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

+ +

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.

+

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

+
-
class MyInt : boost::operators<MyInt>
+
class MyInt
+    : boost::operators<MyInt>
 {
     bool operator<(const MyInt& x) const; 
     bool operator==(const MyInt& x) const;
@@ -38,279 +91,172 @@ fundamental operators.

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: -

+
+

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

+ +

Summary of Template Semantics

+ +
    +
  1. Each operator template completes the concept(s) it describes by + defining overloaded operators for its target class.
  2. + +
  3. The name of an operator class template indicates the concept that its target class will + model.
  4. + +
  5. Usually, the target class uses an instantation of the operator class + template as a base class. Some operator templates support an + alternate method.
  6. + +
  7. The concept can be compound, i.e. it may represent a + common combination of other, simpler concepts.
  8. + +
  9. Most operator templates require their target class to support + operations related to the operators supplied by the template. In + accordance with widely accepted coding style + recommendations, the target class is often required to + supply the assignment counterpart operator of the concept's + "main operator." For example, the addable + template requires operator+=(T const&) and + in turn supplies operator+(T const&, T + const&).
  10. +
+ +

Use of concepts

+ +

The discussed concepts are not necessarily the standard library's +concepts (CopyConstructible, etc.), although some of them could +be; they are what we call concepts with a small 'c'. In +particular, they are different from the former ones in that they do +not describe precise semantics of the operators they require to +be defined, except the requirements that (a) the semantics of the +operators grouped in one concept should be consistent (e.g. +effects of evaluating of a += b and a = a + b +expressions should be the same), and (b) that the return types of the +operators should follow semantics of return types of corresponding +operators for built-in types (e.g. operator< +should return a type convertible to bool, and +T::operator-= should return type convertible to +T). Such "loose" requirements make operators +library applicable to broader set of target classes from different +domains, i.e. eventually more useful.

+ +

Usage

+ +

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<T, +U>, equality_comparable<T, +U>, equivalent<T, U>, +and partially_ordered<T, U>) +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.

+ +

Base Class Chaining and Object Size

+ +

Every operator class template, except the arithmetic +examples and the iterator helpers, has an +additional, but optional, template type parameter B. This +parameter will be a publicly-derived base class of the instantiated template. +This means it must be a class type. It can be used to avoid the bloating of +object sizes that is commonly associated with multiple-inheritance from +several empty base classes (see the note for users of +older versions for more details). To provide support for a group of +operators, use the B parameter to chain operator templates +into a single-base class hierarchy, demostrated in the usage +example. The technique is also used by the composite operator templates +to group operator definitions. If a chain becomes too long for the compiler to +support, try replacing some of the operator templates with a single grouped +operator template that chains the old templates together; the length limit only +applies to the number of templates directly in the chain, not those hidden in +group templates.

+ +

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.

+ +

Separate, Explicit Instantiation

+ +

On some compilers (e.g. Borland, GCC) even single-inheritance seems +to cause an increase in object size in some cases. If you are not defining +a class 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>
+    template struct less_than_comparable<myclass>;
+    template struct equality_comparable<myclass>;
+    template struct incrementable<myclass>;
+    template struct decrementable<myclass>;
+    template struct addable<myclass,long>;
+    template struct subtractable<myclass,long>;
+
+ +

Note that some operator templates cannot use this workaround and must +be a base class of their primary operand type. Those templates define +operators which must be member functions, and the workaround needs the +operators to be independent friend functions. The relevant templates are:

+ + + +

Requirement Portability

+ +

Many compilers (e.g. MSVC 6.3, GCC 2.95.2) will not enforce +the requirements in the operator template tables unless the operations +which depend on them are actually used. This is not standard-conforming +behavior. In particular, although 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 +of those templates, this shortcut is not portable. Even if this currently +works with your compiler, it may not work later.

+ +

Example

+ +

This example shows how some of the arithmetic +operator templates can be used with a geometric point class (template).

+ +
+template <class T>
 class point    // note: private inheritance is OK here!
     : boost::addable< point<T>          // point + point
     , boost::subtractable< point<T>     // point - point
@@ -355,165 +301,692 @@ T length(const point<T> p)
 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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const point<float> pi_over_4_normalized = pi_over_4 / length(pi_over_4); + + +

Arithmetic Operators

+ +

The arithmetic operator templates ease the task of creating a custom +numeric type. Given a core set of operators, the templates add related +operators to the numeric class. These operations are like the ones the +standard arithmetic types have, and may include comparisons, adding, +incrementing, logical and bitwise manipulations, etc. Further, since +most numeric types need more than one of these operators, some +templates are provided to combine several of the basic operator +templates in one declaration.

+ +

The requirements for the types used to instantiate the simple operator +templates are specified in terms of expressions which must be valid and +the expression's return type. The composite operator templates only list +what other templates they use. The supplied operations and requirements +of the composite operator templates can be inferred from the operations and +requirements of the listed components.

+ +

Simple Arithmetic Operators

+ +

These templates are "simple" since they provide operators +based on a single operation the base type has to provide. They have an +additional optional template parameter B, which is not shown, +for the base class chaining technique. + +

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.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Simple Arithmetic Operator Template Classes
+ + + + + + + + + +
Key
T: primary operand typeU: alternate operand type
t, t1: values of type + Tu: value of type U
TemplateSupplied OperationsRequirements
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. See the Ordering Note.
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. t > u.
+ Returns convertible to bool. See the Ordering Note.
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>T operator++(T& x, int)T temp(x); ++x; return temp;
+ Return convertible to T.
decrementable<T>T operator--(T& x, int)T temp(x); --x; return temp;
+ Return convertible to T.
left_shiftable<T>
+ left_shiftable1<T>
T operator<<(T, const T&)t <<= t1.
+ Return convertible to T.
left_shiftable<T, U>
+ left_shiftable2<T, U>
T operator<<(T, const U&)t <<= u.
+ Return convertible to T.
right_shiftable<T>
+ right_shiftable1<T>
T operator>>(T, const T&)t >>= t1.
+ Return convertible to T.
right_shiftable<T, U>
+ right_shiftable2<T, U>
T operator>>(T, const U&)t >>= u.
+ Return convertible to T.
equivalent<T>
+ equivalent1<T>
bool operator==(const T&, const T&)t < t1.
+ Return convertible to bool. See the Ordering Note.
equivalent<T, U>
+ equivalent2<T, U>
bool operator==(const T&, const U&)t < u. t > u.
+ Returns convertible to bool. See the Ordering Note.
partially_ordered<T>
+ partially_ordered1<T>
bool operator>(const T&, const T&)
+ bool operator<=(const T&, const T&)
+ bool operator>=(const T&, const T&)
t < t1. t == t1.
+ Returns convertible to bool. See the Ordering Note.
partially_ordered<T, U>
+ partially_ordered2<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. t > u. t == u.
+ Returns convertible to bool. See the Ordering Note.
-

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

Ordering Note
+The less_than_comparable<T> +and partially_ordered<T> +templates provide the same set of operations. However, the workings of +less_than_comparable<T> +assume that all values of type T can be placed in a total order. If +that is not true (e.g. Not-a-Number values in IEEE floating point +arithmetic), then +partially_ordered<T> +should be used. The +partially_ordered<T> +template can be used for a totally-ordered type, but it is not as efficient as +less_than_comparable<T>. +This rule also applies for +less_than_comparable<T, +U> and partially_ordered<T, +U> with respect to the ordering of all T and +U values, and for both versions of +equivalent<>. The solution for +equivalent<> is to write a +custom operator== for the target class.

+ +

Grouped Arithmetic Operators

+ +

The following templates provide common groups of related operations. +For example, since a type which is addable is usually also subractable, the +additive template provides the combined +operators of both. The grouped operator templates have an additional +optional template parameter B, which is not shown, for the +base class chaining technique.

+ +
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.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Grouped Arithmetic Operator Template Classes
+ + + + + +
Key
T: primary operand typeU: alternate operand type
TemplateComponent Operator Templates
totally_ordered<T>
+ totally_ordered1<T>
totally_ordered<T, U>
+ totally_ordered2<T, U>
additive<T>
+ additive1<T>
additive<T, U>
+ additive2<T, U>
multiplicative<T>
+ multiplicative1<T>
multiplicative<T, U>
+ multiplicative2<T, U>
integer_multiplicative<T>
+ integer_multiplicative1<T>
integer_multiplicative<T, U>
+ integer_multiplicative2<T, U>
arithmetic<T>
+ arithmetic1<T>
arithmetic<T, U>
+ arithmetic2<T, U>
integer_arithmetic<T>
+ integer_arithmetic1<T>
integer_arithmetic<T, U>
+ integer_arithmetic2<T, U>
bitwise<T>
+ bitwise1<T>
bitwise<T, U>
+ bitwise2<T, U>
unit_steppable<T>
shiftable<T>
+ shiftable1<T>
shiftable<T, U>
+ shiftable2<T, U>
-

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

- - - - - - - - - - - - - - - - - - - - - - - + +

Example Templates

+ +

The arithmetic operator class templates operators<> and operators2<> are examples of +non-extensible operator grouping classes. These legacy class templates, +from previous versions of the header, cannot be used for +base class chaining.

+ +
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
+ + + + + + + + + + + + + + + +
Final Arithmetic Operator Template Classes
+ + + + + +
Key
T: primary operand typeU: alternate operand type
TemplateComponent Operator Templates
operators<T>
operators<T, U>
+ operators2<T, U>
-

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.

+ +

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. Check the compiler status report for the test results +with selected platforms.

+ +

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[])!

+ +

The requirements for the types used to instantiate the dereference +operators are specified in terms of expressions which must be valid and +their return type. The composite operator templates list their component +templates, which the instantiating type must support, and possibly other +requirements.

+ +

Dereference Operators

+ +

All the dereference operator templates in this table accept an +optional template parameter (not shown) to be used for base class chaining.

+ + + + + + + + + + + + + + + + + + + + + +
Dereference Operator Template Classes
+ + + + + + + + + + + + + +
Key
T: operand typeP: pointer type
D: difference_typeR: reference type
i: object of type T (an iterator)n: object of type D (an index)
TemplateSupplied OperationsRequirements
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. These classes cannot be used for base class chaining. 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.).

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Iterator Helper Template Classes
+ + + + + + + + + + + + + +
Key
T: operand typeP: pointer type
D: difference_typeR: reference type
V: value_typex1, x2: objects of type T
TemplateOperations & Requirements
input_iterator_helper<T, V, D, P, R>Supports the operations and has the requirements of +
output_iterator_helper<T, V, D, P, R>Supports the operations and has the requirements of +
forward_iterator_helper<T, V, D, P, R>Supports the operations and has the requirements of +
bidirectional_iterator_helper<T, V, D, P, R>Supports the operations and has the requirements of +
random_access_iterator_helper<T, V, D, P, R>Supports the operations and has the requirements of + + To satisfy RandomAccessIterator, + x1 - x2 with return convertible to D + is also required.
+ +

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>
+
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>
@@ -523,75 +996,109 @@ struct test_iter
   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;
+  explicit test_iter(T* i =0);
+  test_iter(const self& x);
+  self& operator=(const self& x);
+  Reference operator*() const;
+  self& operator++();
+  self& operator--();
+  self& operator+=(Distance n);
+  self& operator-=(Distance n);
+  bool operator==(const self& x) const;
+  bool operator<(const self& x) const;
+  friend Distance operator-(const self& x, const self& y);
 };
-

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

+ +

Check the compiler status report for +the test results with selected platforms.

+
-

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.

+ +

Contributors

+ +
+
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. + Also contributed iterators_test.cpp. + +
Aleksey Gurtovoy +
Contributed the code to support base class + chaining while remaining backward-compatible with old + versions of the library. + +
Beman Dawes +
Contributed operators_test.cpp. + +
Daryle Walker +
Contributed classes for the shift operators, equivalence, + partial ordering, and arithmetic conversions. Added the + grouped operator classes. Added helper classes for + input and output iterators. +
+ +

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] paragraph 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 10 Feb 2001

-

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

+ +

Revised: 20 May 2001

+ +

Copyright © David Abrahams and Beman Dawes 1999-2001. +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/operators_test.cpp b/operators_test.cpp index a194521..ce03e87 100644 --- a/operators_test.cpp +++ b/operators_test.cpp @@ -8,18 +8,26 @@ // See http://www.boost.org for most recent version including documentation. // Revision History +// 20 May 01 Output progress messages. Added tests for new operator +// templates. Updated random number generator. Changed tests to +// use Boost Test Tools library. (Daryle Walker) // 04 Jun 00 Added regression test for a bug I found (David Abrahams) // 17 Jun 00 Fix for broken compilers (Aleksey Gurtovoy) // ?? ??? 00 Major update to randomly test all one- and two- argument forms by -// wrapping integral types and comparing the results of operations to -// the results for the raw types (David Abrahams) +// wrapping integral types and comparing the results of operations +// to the results for the raw types (David Abrahams) // 12 Dec 99 Minor update, output confirmation message. // 15 Nov 99 Initial version -#include -#include -#include -#include +#define BOOST_INCLUDE_MAIN + +#include // for BOOST_MSVC +#include // for boost::exit_success +#include // for the tested items +#include // for boost::minstd_rand +#include // for main + +#include // for std::cout (std::endl indirectly) namespace @@ -28,14 +36,18 @@ namespace int true_value(int x) { return x; } long true_value(long x) { return x; } signed char true_value(signed char x) { return x; } + short true_value(short x) { return x; } unsigned int true_value(unsigned int x) { return x; } unsigned long true_value(unsigned long x) { return x; } unsigned char true_value(unsigned char x) { return x; } + unsigned short true_value(unsigned short x) { return x; } - // The use of operators<> here tended to obscure interactions with certain - // compiler bugs + // The use of operators<> here tended to obscure + // interactions with certain compiler bugs template - class Wrapped1 : boost::operators > + class Wrapped1 + : boost::operators > + , boost::shiftable > { public: explicit Wrapped1( T v = T() ) : _value(v) {} @@ -60,6 +72,10 @@ namespace { _value &= x._value; return *this; } Wrapped1& operator^=(const Wrapped1& x) { _value ^= x._value; return *this; } + Wrapped1& operator<<=(const Wrapped1& x) + { _value <<= x._value; return *this; } + Wrapped1& operator>>=(const Wrapped1& x) + { _value >>= x._value; return *this; } Wrapped1& operator++() { ++_value; return *this; } Wrapped1& operator--() { --_value; return *this; } @@ -70,9 +86,11 @@ namespace T true_value(Wrapped1 x) { return x.value(); } template - class Wrapped2 : - boost::operators >, - boost::operators2, U> + class Wrapped2 + : boost::operators > + , boost::operators2, U> + , boost::shiftable1 + , boost::shiftable2, U > > { public: explicit Wrapped2( T v = T() ) : _value(v) {} @@ -97,6 +115,10 @@ namespace { _value &= x._value; return *this; } Wrapped2& operator^=(const Wrapped2& x) { _value ^= x._value; return *this; } + Wrapped2& operator<<=(const Wrapped2& x) + { _value <<= x._value; return *this; } + Wrapped2& operator>>=(const Wrapped2& x) + { _value >>= x._value; return *this; } Wrapped2& operator++() { ++_value; return *this; } Wrapped2& operator--() { --_value; return *this; } @@ -111,6 +133,8 @@ namespace Wrapped2& operator|=(U u) { _value |= u; return *this; } Wrapped2& operator&=(U u) { _value &= u; return *this; } Wrapped2& operator^=(U u) { _value ^= u; return *this; } + Wrapped2& operator<<=(U u) { _value <<= u; return *this; } + Wrapped2& operator>>=(U u) { _value >>= u; return *this; } private: T _value; @@ -118,203 +142,268 @@ namespace template T true_value(Wrapped2 x) { return x.value(); } + template + class Wrapped3 + : boost::equivalent > + , boost::partially_ordered > + , boost::equality_comparable > + { + public: + explicit Wrapped3( T v = T() ) : _value(v) {} + T value() const { return _value; } + + bool operator<(const Wrapped3& x) const { return _value < x._value; } + + private: + T _value; + }; + template + T true_value(Wrapped3 x) { return x.value(); } + + template + class Wrapped4 + : boost::equality_comparable1 + , boost::equivalent1 + , boost::partially_ordered1 > > > + , boost::partially_ordered2, U + , boost::equivalent2, U + , boost::equality_comparable2, U> > > + { + public: + explicit Wrapped4( T v = T() ) : _value(v) {} + T value() const { return _value; } + + bool operator<(const Wrapped4& x) const { return _value < x._value; } + + bool operator<(U u) const { return _value < u; } + bool operator>(U u) const { return _value > u; } + + private: + T _value; + }; + template + T true_value(Wrapped4 x) { return x.value(); } + // MyInt uses only the single template-argument form of all_operators<> typedef Wrapped1 MyInt; typedef Wrapped2 MyLong; + typedef Wrapped3 MyChar; + + typedef Wrapped4 MyShort; + template void sanity_check(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert(true_value(y1) == true_value(y2)); - assert(true_value(x1) == true_value(x2)); + BOOST_TEST( true_value(y1) == true_value(y2) ); + BOOST_TEST( true_value(x1) == true_value(x2) ); } template void test_less_than_comparable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 < y1) == (x2 < y2)); - assert((x1 <= y1) == (x2 <= y2)); - assert((x1 >= y1) == (x2 >= y2)); - assert((x1 > y1) == (x2 > y2)); + BOOST_TEST( (x1 < y1) == (x2 < y2) ); + BOOST_TEST( (x1 <= y1) == (x2 <= y2) ); + BOOST_TEST( (x1 >= y1) == (x2 >= y2) ); + BOOST_TEST( (x1 > y1) == (x2 > y2) ); } template void test_less_than_comparable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_less_than_comparable_aux(x1, y1, x2, y2); - test_less_than_comparable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_less_than_comparable_aux( x1, y1, x2, y2 ); + test_less_than_comparable_aux( y1, x1, y2, x2 ); } template void test_equality_comparable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 == y1) == (x2 == y2)); - assert((x1 != y1) == (x2 != y2)); + BOOST_TEST( (x1 == y1) == (x2 == y2) ); + BOOST_TEST( (x1 != y1) == (x2 != y2) ); } template void test_equality_comparable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_equality_comparable_aux(x1, y1, x2, y2); - test_equality_comparable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_equality_comparable_aux( x1, y1, x2, y2 ); + test_equality_comparable_aux( y1, x1, y2, x2 ); } template void test_multipliable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 * y1).value() == (x2 * y2)); + BOOST_TEST( (x1 * y1).value() == (x2 * y2) ); } template void test_multipliable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_multipliable_aux(x1, y1, x2, y2); - test_multipliable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_multipliable_aux( x1, y1, x2, y2 ); + test_multipliable_aux( y1, x1, y2, x2 ); } template void test_addable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 + y1).value() == (x2 + y2)); + BOOST_TEST( (x1 + y1).value() == (x2 + y2) ); } template void test_addable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_addable_aux(x1, y1, x2, y2); - test_addable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_addable_aux( x1, y1, x2, y2 ); + test_addable_aux( y1, x1, y2, x2 ); } template void test_subtractable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - assert((x1 - y1).value() == x2 - y2); + sanity_check( x1, y1, x2, y2 ); + BOOST_TEST( (x1 - y1).value() == (x2 - y2) ); } template void test_dividable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - if (y2 != 0) - assert((x1 / y1).value() == x2 / y2); + sanity_check( x1, y1, x2, y2 ); + if ( y2 != 0 ) + BOOST_TEST( (x1 / y1).value() == (x2 / y2) ); } template void test_modable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - if (y2 != 0) - assert((x1 / y1).value() == x2 / y2); + sanity_check( x1, y1, x2, y2 ); + if ( y2 != 0 ) + BOOST_TEST( (x1 % y1).value() == (x2 % y2) ); } template void test_xorable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 ^ y1).value() == (x2 ^ y2)); + BOOST_TEST( (x1 ^ y1).value() == (x2 ^ y2) ); } template void test_xorable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_xorable_aux(x1, y1, x2, y2); - test_xorable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_xorable_aux( x1, y1, x2, y2 ); + test_xorable_aux( y1, x1, y2, x2 ); } template void test_andable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 & y1).value() == (x2 & y2)); + BOOST_TEST( (x1 & y1).value() == (x2 & y2) ); } template void test_andable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_andable_aux(x1, y1, x2, y2); - test_andable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_andable_aux( x1, y1, x2, y2 ); + test_andable_aux( y1, x1, y2, x2 ); } template void test_orable_aux(X1 x1, Y1 y1, X2 x2, Y2 y2) { - assert((x1 | y1).value() == (x2 | y2)); + BOOST_TEST( (x1 | y1).value() == (x2 | y2) ); } template void test_orable(X1 x1, Y1 y1, X2 x2, Y2 y2) { - sanity_check(x1, y1, x2, y2); - test_orable_aux(x1, y1, x2, y2); - test_orable_aux(y1, x1, y2, x2); + sanity_check( x1, y1, x2, y2 ); + test_orable_aux( x1, y1, x2, y2 ); + test_orable_aux( y1, x1, y2, x2 ); + } + + template + void test_left_shiftable(X1 x1, Y1 y1, X2 x2, Y2 y2) + { + sanity_check( x1, y1, x2, y2 ); + BOOST_TEST( (x1 << y1).value() == (x2 << y2) ); + } + + template + void test_right_shiftable(X1 x1, Y1 y1, X2 x2, Y2 y2) + { + sanity_check( x1, y1, x2, y2 ); + BOOST_TEST( (x1 >> y1).value() == (x2 >> y2) ); } template void test_incrementable(X1 x1, X2 x2) { - sanity_check(x1, x1, x2, x2); - assert(x1++.value() == x2++); - assert(x1.value() == x2); + sanity_check( x1, x1, x2, x2 ); + BOOST_TEST( (x1++).value() == x2++ ); + BOOST_TEST( x1.value() == x2 ); } template void test_decrementable(X1 x1, X2 x2) { - sanity_check(x1, x1, x2, x2); - assert(x1--.value() == x2--); - assert(x1.value() == x2); + sanity_check( x1, x1, x2, x2 ); + BOOST_TEST( (x1--).value() == x2-- ); + BOOST_TEST( x1.value() == x2 ); } template void test_all(X1 x1, Y1 y1, X2 x2, Y2 y2) { - test_less_than_comparable(x1, y1, x2, y2); - test_equality_comparable(x1, y1, x2, y2); - test_multipliable(x1, y1, x2, y2); - test_addable(x1, y1, x2, y2); - test_subtractable(x1, y1, x2, y2); - test_dividable(x1, y1, x2, y2); - test_modable(x1, y1, x2, y2); - test_xorable(x1, y1, x2, y2); - test_andable(x1, y1, x2, y2); - test_orable(x1, y1, x2, y2); - test_incrementable(x1, x2); - test_decrementable(x1, x2); + test_less_than_comparable( x1, y1, x2, y2 ); + test_equality_comparable( x1, y1, x2, y2 ); + test_multipliable( x1, y1, x2, y2 ); + test_addable( x1, y1, x2, y2 ); + test_subtractable( x1, y1, x2, y2 ); + test_dividable( x1, y1, x2, y2 ); + test_modable( x1, y1, x2, y2 ); + test_xorable( x1, y1, x2, y2 ); + test_andable( x1, y1, x2, y2 ); + test_orable( x1, y1, x2, y2 ); + test_left_shiftable( x1, y1, x2, y2 ); + test_right_shiftable( x1, y1, x2, y2 ); + test_incrementable( x1, x2 ); + test_decrementable( x1, x2 ); } template struct tester { - void operator()(boost::min_rand& randomizer) const + void operator()(boost::minstd_rand& randomizer) const { - Big b1 = Big(randomizer()); - Big b2 = Big(randomizer()); - Small s = Small(randomizer()); + Big b1 = Big( randomizer() ); + Big b2 = Big( randomizer() ); + Small s = Small( randomizer() ); - test_all(Wrapped1(b1), Wrapped1(b2), b1, b2); - test_all(Wrapped2(b1), s, b1, s); + test_all( Wrapped1(b1), Wrapped1(b2), b1, b2 ); + test_all( Wrapped2(b1), s, b1, s ); } }; // added as a regression test. We had a bug which this uncovered. struct Point - : boost::addable > + : boost::addable > { Point( int h, int v ) : h(h), v(v) {} Point() :h(0), v(0) {} - const Point& operator+=( const Point& rhs ) { h += rhs.h; v += rhs.v; return *this; } - const Point& operator-=( const Point& rhs ) { h -= rhs.h; v -= rhs.v; return *this; } + const Point& operator+=( const Point& rhs ) + { h += rhs.h; v += rhs.v; return *this; } + const Point& operator-=( const Point& rhs ) + { h -= rhs.h; v -= rhs.v; return *this; } int h; int v; }; + } // unnamed namespace @@ -340,20 +429,25 @@ template Wrapped2; template Wrapped2; #endif -#ifdef NDEBUG -#error This program is pointless when NDEBUG disables assert()! -#endif +#define PRIVATE_EXPR_TEST(e, t) BOOST_TEST( ((e), (t)) ) -int main() + +int +test_main( int , char * [] ) { + using std::cout; + using std::endl; + // Regression test. Point x; x = x + Point(3, 4); x = x - Point(3, 4); + cout << "Created point, and operated on it." << endl; + for (int n = 0; n < 10000; ++n) { - boost::min_rand r; + boost::minstd_rand r; tester()(r); tester()(r); tester()(r); @@ -367,115 +461,197 @@ int main() tester()(r); } + cout << "Did random tester loop." << endl; + MyInt i1(1); MyInt i2(2); MyInt i; - assert( i1.value() == 1 ); - assert( i2.value() == 2 ); - assert( i.value() == 0 ); + BOOST_TEST( i1.value() == 1 ); + BOOST_TEST( i2.value() == 2 ); + BOOST_TEST( i.value() == 0 ); - i = i2; - assert( i.value() == 2 ); - assert( i2 == i ); - assert( i1 != i2 ); - assert( i1 < i2 ); - assert( i1 <= i2 ); - assert( i <= i2 ); - assert( i2 > i1 ); - assert( i2 >= i1 ); - assert( i2 >= i ); + cout << "Created MyInt objects.\n"; - i = i1 + i2; assert( i.value() == 3 ); - i = i + i2; assert( i.value() == 5 ); - i = i - i1; assert( i.value() == 4 ); - i = i * i2; assert( i.value() == 8 ); - i = i / i2; assert( i.value() == 4 ); - i = i % (i - i1); assert( i.value() == 1 ); - i = i2 + i2; assert( i.value() == 4 ); - i = i1 | i2 | i; assert( i.value() == 7 ); - i = i & i2; assert( i.value() == 2 ); - i = i + i1; assert( i.value() == 3 ); - i = i ^ i1; assert( i.value() == 2 ); - i = (i+i1)*(i2|i1); assert( i.value() == 9 ); + PRIVATE_EXPR_TEST( (i = i2), (i.value() == 2) ); + + BOOST_TEST( i2 == i ); + BOOST_TEST( i1 != i2 ); + BOOST_TEST( i1 < i2 ); + BOOST_TEST( i1 <= i2 ); + BOOST_TEST( i <= i2 ); + BOOST_TEST( i2 > i1 ); + BOOST_TEST( i2 >= i1 ); + BOOST_TEST( i2 >= i ); + + PRIVATE_EXPR_TEST( (i = i1 + i2), (i.value() == 3) ); + PRIVATE_EXPR_TEST( (i = i + i2), (i.value() == 5) ); + PRIVATE_EXPR_TEST( (i = i - i1), (i.value() == 4) ); + PRIVATE_EXPR_TEST( (i = i * i2), (i.value() == 8) ); + PRIVATE_EXPR_TEST( (i = i / i2), (i.value() == 4) ); + PRIVATE_EXPR_TEST( (i = i % ( i - i1 )), (i.value() == 1) ); + PRIVATE_EXPR_TEST( (i = i2 + i2), (i.value() == 4) ); + PRIVATE_EXPR_TEST( (i = i1 | i2 | i), (i.value() == 7) ); + PRIVATE_EXPR_TEST( (i = i & i2), (i.value() == 2) ); + PRIVATE_EXPR_TEST( (i = i + i1), (i.value() == 3) ); + PRIVATE_EXPR_TEST( (i = i ^ i1), (i.value() == 2) ); + PRIVATE_EXPR_TEST( (i = ( i + i1 ) * ( i2 | i1 )), (i.value() == 9) ); + + PRIVATE_EXPR_TEST( (i = i1 << i2), (i.value() == 4) ); + PRIVATE_EXPR_TEST( (i = i2 >> i1), (i.value() == 1) ); + cout << "Performed tests on MyInt objects.\n"; + MyLong j1(1); MyLong j2(2); MyLong j; - assert( j1.value() == 1 ); - assert( j2.value() == 2 ); - assert( j.value() == 0 ); + BOOST_TEST( j1.value() == 1 ); + BOOST_TEST( j2.value() == 2 ); + BOOST_TEST( j.value() == 0 ); - j = j2; - assert( j.value() == 2 ); - - assert( j2 == j ); - assert( 2 == j ); - assert( j2 == 2 ); - assert( j == j2 ); - assert( j1 != j2 ); - assert( j1 != 2 ); - assert( 1 != j2 ); - assert( j1 < j2 ); - assert( 1 < j2 ); - assert( j1 < 2 ); - assert( j1 <= j2 ); - assert( 1 <= j2 ); - assert( j1 <= j ); - assert( j <= j2 ); - assert( 2 <= j2 ); - assert( j <= 2 ); - assert( j2 > j1 ); - assert( 2 > j1 ); - assert( j2 > 1 ); - assert( j2 >= j1 ); - assert( 2 >= j1 ); - assert( j2 >= 1 ); - assert( j2 >= j ); - assert( 2 >= j ); - assert( j2 >= 2 ); + cout << "Created MyLong objects.\n"; - assert( (j1 + 2) == 3 ); - assert( (1 + j2) == 3 ); - j = j1 + j2; assert( j.value() == 3 ); + PRIVATE_EXPR_TEST( (j = j2), (j.value() == 2) ); - assert( (j + 2) == 5 ); - assert( (3 + j2) == 5 ); - j = j + j2; assert( j.value() == 5 ); + BOOST_TEST( j2 == j ); + BOOST_TEST( 2 == j ); + BOOST_TEST( j2 == 2 ); + BOOST_TEST( j == j2 ); + BOOST_TEST( j1 != j2 ); + BOOST_TEST( j1 != 2 ); + BOOST_TEST( 1 != j2 ); + BOOST_TEST( j1 < j2 ); + BOOST_TEST( 1 < j2 ); + BOOST_TEST( j1 < 2 ); + BOOST_TEST( j1 <= j2 ); + BOOST_TEST( 1 <= j2 ); + BOOST_TEST( j1 <= j ); + BOOST_TEST( j <= j2 ); + BOOST_TEST( 2 <= j2 ); + BOOST_TEST( j <= 2 ); + BOOST_TEST( j2 > j1 ); + BOOST_TEST( 2 > j1 ); + BOOST_TEST( j2 > 1 ); + BOOST_TEST( j2 >= j1 ); + BOOST_TEST( 2 >= j1 ); + BOOST_TEST( j2 >= 1 ); + BOOST_TEST( j2 >= j ); + BOOST_TEST( 2 >= j ); + BOOST_TEST( j2 >= 2 ); + + BOOST_TEST( (j1 + 2) == 3 ); + BOOST_TEST( (1 + j2) == 3 ); + PRIVATE_EXPR_TEST( (j = j1 + j2), (j.value() == 3) ); - assert( (j - 1) == 4 ); - j = j - j1; assert( j.value() == 4 ); + BOOST_TEST( (j + 2) == 5 ); + BOOST_TEST( (3 + j2) == 5 ); + PRIVATE_EXPR_TEST( (j = j + j2), (j.value() == 5) ); - assert( (j * 2) == 8 ); - assert( (4 * j2) == 8 ); - j = j * j2; assert( j.value() == 8 ); + BOOST_TEST( (j - 1) == 4 ); + PRIVATE_EXPR_TEST( (j = j - j1), (j.value() == 4) ); - assert( (j / 2) == 4 ); - j = j / j2; assert( j.value() == 4 ); + BOOST_TEST( (j * 2) == 8 ); + BOOST_TEST( (4 * j2) == 8 ); + PRIVATE_EXPR_TEST( (j = j * j2), (j.value() == 8) ); - assert( (j % 3) == 1 ); - j = j % (j - j1); assert( j.value() == 1 ); + BOOST_TEST( (j / 2) == 4 ); + PRIVATE_EXPR_TEST( (j = j / j2), (j.value() == 4) ); - j = j2 + j2; assert( j.value() == 4 ); + BOOST_TEST( (j % 3) == 1 ); + PRIVATE_EXPR_TEST( (j = j % ( j - j1 )), (j.value() == 1) ); - assert( (1 | j2 | j) == 7 ); - assert( (j1 | 2 | j) == 7 ); - assert( (j1 | j2 | 4) == 7 ); - j = j1 | j2 | j; assert( j.value() == 7 ); + PRIVATE_EXPR_TEST( (j = j2 + j2), (j.value() == 4) ); - assert( (7 & j2) == 2 ); - assert( (j & 2) == 2 ); - j = j & j2; assert( j.value() == 2 ); + BOOST_TEST( (1 | j2 | j) == 7 ); + BOOST_TEST( (j1 | 2 | j) == 7 ); + BOOST_TEST( (j1 | j2 | 4) == 7 ); + PRIVATE_EXPR_TEST( (j = j1 | j2 | j), (j.value() == 7) ); - j = j | j1; assert( j.value() == 3 ); + BOOST_TEST( (7 & j2) == 2 ); + BOOST_TEST( (j & 2) == 2 ); + PRIVATE_EXPR_TEST( (j = j & j2), (j.value() == 2) ); - assert( (3 ^ j1) == 2 ); - assert( (j ^ 1) == 2 ); - j = j ^ j1; assert( j.value() == 2 ); + PRIVATE_EXPR_TEST( (j = j | j1), (j.value() == 3) ); - j = (j+j1)*(j2|j1); assert( j.value() == 9 ); + BOOST_TEST( (3 ^ j1) == 2 ); + BOOST_TEST( (j ^ 1) == 2 ); + PRIVATE_EXPR_TEST( (j = j ^ j1), (j.value() == 2) ); - std::cout << "0 errors detected\n"; - return 0; + PRIVATE_EXPR_TEST( (j = ( j + j1 ) * ( j2 | j1 )), (j.value() == 9) ); + + BOOST_TEST( (j1 << 2) == 4 ); + BOOST_TEST( (j2 << 1) == 4 ); + PRIVATE_EXPR_TEST( (j = j1 << j2), (j.value() == 4) ); + + BOOST_TEST( (j >> 2) == 1 ); + BOOST_TEST( (j2 >> 1) == 1 ); + PRIVATE_EXPR_TEST( (j = j2 >> j1), (j.value() == 1) ); + + cout << "Performed tests on MyLong objects.\n"; + + MyChar k1(1); + MyChar k2(2); + MyChar k; + + BOOST_TEST( k1.value() == 1 ); + BOOST_TEST( k2.value() == 2 ); + BOOST_TEST( k.value() == 0 ); + + cout << "Created MyChar objects.\n"; + + PRIVATE_EXPR_TEST( (k = k2), (k.value() == 2) ); + + BOOST_TEST( k2 == k ); + BOOST_TEST( k1 != k2 ); + BOOST_TEST( k1 < k2 ); + BOOST_TEST( k1 <= k2 ); + BOOST_TEST( k <= k2 ); + BOOST_TEST( k2 > k1 ); + BOOST_TEST( k2 >= k1 ); + BOOST_TEST( k2 >= k ); + + cout << "Performed tests on MyChar objects.\n"; + + MyShort l1(1); + MyShort l2(2); + MyShort l; + + BOOST_TEST( l1.value() == 1 ); + BOOST_TEST( l2.value() == 2 ); + BOOST_TEST( l.value() == 0 ); + + cout << "Created MyShort objects.\n"; + + PRIVATE_EXPR_TEST( (l = l2), (l.value() == 2) ); + + BOOST_TEST( l2 == l ); + BOOST_TEST( 2 == l ); + BOOST_TEST( l2 == 2 ); + BOOST_TEST( l == l2 ); + BOOST_TEST( l1 != l2 ); + BOOST_TEST( l1 != 2 ); + BOOST_TEST( 1 != l2 ); + BOOST_TEST( l1 < l2 ); + BOOST_TEST( 1 < l2 ); + BOOST_TEST( l1 < 2 ); + BOOST_TEST( l1 <= l2 ); + BOOST_TEST( 1 <= l2 ); + BOOST_TEST( l1 <= l ); + BOOST_TEST( l <= l2 ); + BOOST_TEST( 2 <= l2 ); + BOOST_TEST( l <= 2 ); + BOOST_TEST( l2 > l1 ); + BOOST_TEST( 2 > l1 ); + BOOST_TEST( l2 > 1 ); + BOOST_TEST( l2 >= l1 ); + BOOST_TEST( 2 >= l1 ); + BOOST_TEST( l2 >= 1 ); + BOOST_TEST( l2 >= l ); + BOOST_TEST( 2 >= l ); + BOOST_TEST( l2 >= 2 ); + + cout << "Performed tests on MyShort objects.\n"; + + return boost::exit_success; }