commit 1c5e20a7bc44ff8574c67917af6c9b24b0bc2fb4 Author: nobody Date: Wed Feb 28 21:39:58 2001 +0000 This commit was manufactured by cvs2svn to create branch 'unlabeled-1.4.2'. [SVN r9366] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3e84d7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/algo_opt_examples.cpp b/algo_opt_examples.cpp new file mode 100644 index 0000000..c30587c --- /dev/null +++ b/algo_opt_examples.cpp @@ -0,0 +1,424 @@ + +/* + * + * Copyright (c) 1999 + * Dr John Maddock + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Dr John Maddock makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * This file provides some example of type_traits usage - + * by "optimising" various algorithms: + * + * opt::copy - optimised for trivial copy (cf std::copy) + * opt::fill - optimised for trivial copy/small types (cf std::fill) + * opt::destroy_array - an example of optimisation based upon omitted destructor calls + * opt::iter_swap - uses type_traits to determine whether the iterator is a proxy + * in which case it uses a "safe" approach, otherwise calls swap + * on the assumption that swap may be specialised for the pointed-to type. + * + */ + +/* Release notes: + 23rd July 2000: + Added explicit failure for broken compilers that don't support these examples. + Fixed broken gcc support (broken using directive). + Reordered tests slightly. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using std::cout; +using std::endl; +using std::cin; + +#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +#error "Sorry, without template partial specialisation support there isn't anything to test here..." +#endif + +namespace opt{ + +// +// algorithm destroy_array: +// The reverse of std::unitialized_copy, takes a block of +// unitialized memory and calls destructors on all objects therein. +// + +namespace detail{ + +template +struct array_destroyer +{ + template + static void destroy_array(T* i, T* j){ do_destroy_array(i, j); } +}; + +template <> +struct array_destroyer +{ + template + static void destroy_array(T*, T*){} +}; + +template +void do_destroy_array(T* first, T* last) +{ + while(first != last) + { + first->~T(); + ++first; + } +} + +}; // namespace detail + +template +inline void destroy_array(T* p1, T* p2) +{ + detail::array_destroyer::value>::destroy_array(p1, p2); +} + +// +// unoptimised versions of destroy_array: +// +template +void destroy_array1(T* first, T* last) +{ + while(first != last) + { + first->~T(); + ++first; + } +} +template +void destroy_array2(T* first, T* last) +{ + for(; first != last; ++first) first->~T(); +} + + +// +// opt::copy +// same semantics as std::copy +// calls memcpy where appropiate. +// + +namespace detail{ + +template +struct copier +{ + template + static I2 do_copy(I1 first, I1 last, I2 out); +}; + +template +template +I2 copier::do_copy(I1 first, I1 last, I2 out) +{ + while(first != last) + { + *out = *first; + ++out; + ++first; + } + return out; +} + +template <> +struct copier +{ + template + static I2* do_copy(I1* first, I1* last, I2* out) + { + memcpy(out, first, (last-first)*sizeof(I2)); + return out+(last-first); + } +}; + + +} + +template +inline I2 copy(I1 first, I1 last, I2 out) +{ + typedef typename boost::remove_cv::value_type>::type v1_t; + typedef typename boost::remove_cv::value_type>::type v2_t; + enum{ can_opt = boost::is_same::value + && boost::is_pointer::value + && boost::is_pointer::value + && boost::has_trivial_assign::value }; + return detail::copier::do_copy(first, last, out); +} + +// +// fill +// same as std::fill, uses memset where appropriate, along with call_traits +// to "optimise" parameter passing. +// +namespace detail{ + +template +struct filler +{ + template + static void do_fill(I first, I last, typename boost::call_traits::param_type val); + }; + +template +template +void filler::do_fill(I first, I last, typename boost::call_traits::param_type val) +{ + while(first != last) + { + *first = val; + ++first; + } +} + +template <> +struct filler +{ + template + static void do_fill(I first, I last, T val) + { + std::memset(first, val, last-first); + } +}; + +} + +template +inline void fill(I first, I last, const T& val) +{ + enum{ can_opt = boost::is_pointer::value + && boost::is_arithmetic::value + && (sizeof(T) == 1) }; + typedef detail::filler filler_t; + filler_t::template do_fill(first, last, val); +} + +// +// iter_swap: +// tests whether iterator is a proxying iterator or not, and +// uses optimal form accordingly: +// +namespace detail{ + +template +struct swapper +{ + template + static void do_swap(I one, I two) + { + typedef typename std::iterator_traits::value_type v_t; + v_t v = *one; + *one = *two; + *two = v; + } +}; + +#ifdef __GNUC__ +using std::swap; +#endif + +template <> +struct swapper +{ + template + static void do_swap(I one, I two) + { + using std::swap; + swap(*one, *two); + } +}; + +} + +template +inline void iter_swap(I1 one, I2 two) +{ + typedef typename std::iterator_traits::reference r1_t; + typedef typename std::iterator_traits::reference r2_t; + enum{ can_opt = boost::is_reference::value && boost::is_reference::value && boost::is_same::value }; + detail::swapper::do_swap(one, two); +} + + +}; // namespace opt + +// +// define some global data: +// +const int array_size = 1000; +int i_array[array_size] = {0,}; +const int ci_array[array_size] = {0,}; +char c_array[array_size] = {0,}; +const char cc_array[array_size] = { 0,}; + +const int iter_count = 1000000; + + +int main() +{ + // + // test destroy_array, + // compare destruction time of an array of ints + // with unoptimised form. + // + cout << "Measuring times in micro-seconds per 1000 elements processed" << endl << endl; + cout << "testing destroy_array...\n" + "[Some compilers may be able to optimise the \"unoptimised\"\n versions as well as type_traits does.]" << endl; + /*cache load*/ opt::destroy_array(i_array, i_array + array_size); + boost::timer t; + double result; + int i; + for(i = 0; i < iter_count; ++i) + { + opt::destroy_array(i_array, i_array + array_size); + } + result = t.elapsed(); + cout << "destroy_array: " << result << endl; + /*cache load*/ opt::destroy_array1(i_array, i_array + array_size); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::destroy_array1(i_array, i_array + array_size); + } + result = t.elapsed(); + cout << "destroy_array(unoptimised#1): " << result << endl; + /*cache load*/ opt::destroy_array2(i_array, i_array + array_size); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::destroy_array2(i_array, i_array + array_size); + } + result = t.elapsed(); + cout << "destroy_array(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); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::copy(ci_array, ci_array + array_size, i_array); + } + result = t.elapsed(); + cout << "opt::copy: " << result << endl; + /*cache load*/ std::copy(ci_array, ci_array + array_size, i_array); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + std::copy(ci_array, ci_array + array_size, i_array); + } + result = t.elapsed(); + cout << "std::copy: " << result << endl; + /*cache load*/ opt::detail::copier::template do_copy(ci_array, ci_array + array_size, i_array); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::detail::copier::template do_copy(ci_array, ci_array + array_size, i_array); + } + result = t.elapsed(); + cout << "standard \"unoptimised\" copy: " << result << endl << endl; + + /*cache load*/ opt::copy(cc_array, cc_array + array_size, c_array); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::copy(cc_array, cc_array + array_size, c_array); + } + result = t.elapsed(); + cout << "opt::copy: " << result << endl; + /*cache load*/ std::copy(cc_array, cc_array + array_size, c_array); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + std::copy(cc_array, cc_array + array_size, c_array); + } + result = t.elapsed(); + cout << "std::copy: " << result << endl; + /*cache load*/ opt::detail::copier::template do_copy(cc_array, cc_array + array_size, c_array); + t.restart(); + for(i = 0; i < iter_count; ++i) + { + opt::detail::copier::template do_copy(cc_array, cc_array + array_size, c_array); + } + result = t.elapsed(); + cout << "standard \"unoptimised\" copy: " << result << endl << endl; + + + // + // testing iter_swap + // really just a check that it does in fact compile... + std::vector v1; + v1.push_back(0); + v1.push_back(1); + std::vector v2; + v2.push_back(0); + v2.push_back(1); + opt::iter_swap(v1.begin(), v1.begin()+1); + opt::iter_swap(v2.begin(), v2.begin()+1); + + cout << "Press any key to exit..."; + cin.get(); +} + + + + + + diff --git a/filter_iterator_example.cpp b/filter_iterator_example.cpp new file mode 100644 index 0000000..6ca0605 --- /dev/null +++ b/filter_iterator_example.cpp @@ -0,0 +1,53 @@ +// Example of using the filter iterator adaptor from +// boost/iterator_adaptors.hpp. + +// (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. + + +#include +#include +#include +#include +#include + +struct is_positive_number { + bool operator()(int x) { return 0 < x; } +}; + +int main() +{ + int numbers[] = { 0, -1, 4, -3, 5, 8, -2 }; + const int N = sizeof(numbers)/sizeof(int); + + // Example using make_filter_iterator() + std::copy(boost::make_filter_iterator(numbers, numbers + N), + boost::make_filter_iterator(numbers + N, numbers + N), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + // Example using filter_iterator_generator + typedef boost::filter_iterator_generator::type + FilterIter; + is_positive_number predicate; + FilterIter::policies_type policies(predicate, numbers + N); + FilterIter filter_iter_first(numbers, policies); + FilterIter filter_iter_last(numbers + N, policies); + + std::copy(filter_iter_first, filter_iter_last, std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + // Another example using make_filter_iterator() + std::copy(boost::make_filter_iterator(numbers, numbers + N, + std::bind2nd(std::greater(), -2)), + boost::make_filter_iterator(numbers + N, numbers + N, + std::bind2nd(std::greater(), -2)), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + + return 0; +} diff --git a/numeric_traits_test.cpp b/numeric_traits_test.cpp new file mode 100644 index 0000000..0bc0ca5 --- /dev/null +++ b/numeric_traits_test.cpp @@ -0,0 +1,385 @@ +// (C) Copyright David Abrahams 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 +// 23 Jan 2001 Added test for wchar_t (David Abrahams) +// 23 Jan 2001 Now statically selecting a test for signed numbers to avoid +// warnings with fancy compilers. Added commentary and +// additional dumping of traits data for tested types (David +// Abrahams). +// 21 Jan 2001 Initial version (David Abrahams) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef BOOST_NO_LIMITS +# include +#endif + +// A macro for declaring class compile-time constants. +#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define DECLARE_CLASS_CONST(type, init) static const type init +#else +# define DECLARE_CLASS_CONST(type, init) enum { init } +#endif + +// ================================================================================= +// template class complement_traits -- +// +// statically computes the max and min for 1s and 2s-complement binary +// numbers. This helps on platforms without support. It also shows +// an example of a recursive template that works with MSVC! +// + +template struct complement; // forward + +// The template complement, below, does all the real work, using "poor man's +// partial specialization". We need complement_traits_aux<> so that MSVC doesn't +// complain about undefined min/max as we're trying to recursively define them. +template +struct complement_traits_aux +{ + DECLARE_CLASS_CONST(Number, max = complement::template traits::max); + DECLARE_CLASS_CONST(Number, min = complement::template traits::min); +}; + +template +struct complement +{ + template + struct traits + { + private: + // indirection through complement_traits_aux neccessary to keep MSVC happy + typedef complement_traits_aux prev; + public: + DECLARE_CLASS_CONST(Number, max = + Number(Number(prev::max) << CHAR_BIT) + + Number(UCHAR_MAX)); + + DECLARE_CLASS_CONST(Number, min = Number(Number(prev::min) << CHAR_BIT)); + }; +}; + +// Template class complement_base<> -- defines values for min and max for +// complement<1>, at the deepest level of recursion. Uses "poor man's partial +// specialization" again. +template struct complement_base; + +template <> struct complement_base +{ + template + struct values + { + DECLARE_CLASS_CONST(Number, min = 0); + DECLARE_CLASS_CONST(Number, max = UCHAR_MAX); + }; +}; + +template <> struct complement_base +{ + template + struct values + { + DECLARE_CLASS_CONST(Number, min = SCHAR_MIN); + DECLARE_CLASS_CONST(Number, max = SCHAR_MAX); + }; +}; + +// Base specialization of complement, puts an end to the recursion. +template <> +struct complement<1> +{ + template + struct traits + { + DECLARE_CLASS_CONST(bool, is_signed = boost::detail::is_signed::value); + DECLARE_CLASS_CONST(Number, min = + complement_base::template values::min); + DECLARE_CLASS_CONST(Number, max = + complement_base::template values::max); + }; +}; + +// Now here's the "pretty" template you're intended to actually use. +// complement_traits::min, complement_traits::max are the +// minimum and maximum values of Number if Number is a built-in integer type. +template +struct complement_traits +{ + DECLARE_CLASS_CONST(Number, max = (complement_traits_aux::max)); + DECLARE_CLASS_CONST(Number, min = (complement_traits_aux::min)); +}; + +// ================================================================================= + +// Support for streaming various numeric types in exactly the format I want. I +// needed this in addition to all the assertions so that I could see exactly +// what was going on. +// +// Numbers go through a 2-stage conversion process (by default, though, no real +// conversion). +// +template struct stream_as { + typedef T t1; + typedef T t2; +}; + +// char types first get converted to unsigned char, then to unsigned. +template <> struct stream_as { + typedef unsigned char t1; + typedef unsigned t2; +}; +template <> struct stream_as { + typedef unsigned char t1; typedef unsigned t2; +}; +template <> struct stream_as { + typedef unsigned char t1; typedef unsigned t2; +}; + +#if defined(BOOST_MSVC) // No intmax streaming built-in + +// On this platform, __int64 and __uint64 get streamed as strings +template <> struct stream_as { + typedef std::string t1; + typedef std::string t2; +}; + +template <> struct stream_as { + typedef std::string t1; + typedef std::string t2; +}; +#endif + +// Standard promotion process for streaming +template struct promote +{ + static typename stream_as::t1 from(T x) { + typedef typename stream_as::t1 t1; + return t1(x); + } +}; + +#if defined(BOOST_MSVC) // No intmax streaming built-in + +// On this platform, stream them as long/unsigned long if they fit. +// Otherwise, write a string. +template <> struct promote { + std::string static from(const boost::uintmax_t x) { + if (x > ULONG_MAX) + return std::string("large unsigned value"); + else + return boost::lexical_cast((unsigned long)x); + } +}; +template <> struct promote { + std::string static from(const boost::intmax_t x) { + if (x > boost::intmax_t(ULONG_MAX)) + return std::string("large positive signed value"); + else if (x >= 0) + return boost::lexical_cast((unsigned long)x); + + if (x < boost::intmax_t(LONG_MIN)) + return std::string("large negative signed value"); + else + return boost::lexical_cast((long)x); + } +}; +#endif + +// This is the function which converts types to the form I want to stream them in. +template +typename stream_as::t2 stream_number(T x) +{ + return promote::from(x); +} +// ================================================================================= + +// +// Tests for built-in signed and unsigned types +// + +// Tag types for selecting tests +struct unsigned_tag {}; +struct signed_tag {}; + +// Tests for unsigned numbers. The extra default Number parameter works around +// an MSVC bug. +template +void test_aux(unsigned_tag, Number* = 0) +{ + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(!boost::detail::is_signed::value); + BOOST_STATIC_ASSERT( + (sizeof(Number) < sizeof(boost::intmax_t)) + | (boost::is_same::value)); + + // Force casting to Number here to work around the fact that it's an enum on MSVC + BOOST_STATIC_ASSERT(Number(complement_traits::max) > Number(0)); + BOOST_STATIC_ASSERT(Number(complement_traits::min) == Number(0)); + + const Number max = complement_traits::max; + const Number min = complement_traits::min; + + const Number test_max = (sizeof(Number) < sizeof(boost::intmax_t)) + ? max + : max / 2 - 1; + + std::cout << std::hex << "(unsigned) min = " << stream_number(min) << ", max = " + << stream_number(max) << "..." << std::flush; + std::cout << "difference_type = " << typeid(difference_type).name() << "..." + << std::flush; + + difference_type d1 = boost::detail::numeric_distance(Number(0), test_max); + difference_type d2 = boost::detail::numeric_distance(test_max, Number(0)); + + std::cout << "0->" << stream_number(test_max) << "==" << std::dec << stream_number(d1) << "; " + << std::hex << stream_number(test_max) << "->0==" << std::dec << stream_number(d2) << "..." << std::flush; + + assert(d1 == difference_type(test_max)); + assert(d2 == -difference_type(test_max)); +} + +// Tests for signed numbers. The extra default Number parameter works around an +// MSVC bug. +struct out_of_range_tag {}; +struct in_range_tag {}; + +// This test morsel gets executed for numbers whose difference will always be +// representable in intmax_t +template +void signed_test(in_range_tag, Number* = 0) +{ + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + typedef typename boost::detail::numeric_traits::difference_type difference_type; + const Number max = complement_traits::max; + const Number min = complement_traits::min; + + difference_type d1 = boost::detail::numeric_distance(min, max); + difference_type d2 = boost::detail::numeric_distance(max, min); + + std::cout << stream_number(min) << "->" << stream_number(max) << "=="; + std::cout << std::dec << stream_number(d1) << "; "; + std::cout << std::hex << stream_number(max) << "->" << stream_number(min) + << "==" << std::dec << stream_number(d2) << "..." << std::flush; + assert(d1 == difference_type(max) - difference_type(min)); + assert(d2 == difference_type(min) - difference_type(max)); +} + +// This test morsel gets executed for numbers whose difference may exceed the +// capacity of intmax_t. +template +void signed_test(out_of_range_tag, Number* = 0) +{ + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + typedef typename boost::detail::numeric_traits::difference_type difference_type; + const Number max = complement_traits::max; + const Number min = complement_traits::min; + + difference_type min_distance = complement_traits::min; + difference_type max_distance = complement_traits::max; + + const Number n1 = Number(min + max_distance); + const Number n2 = Number(max + min_distance); + difference_type d1 = boost::detail::numeric_distance(min, n1); + difference_type d2 = boost::detail::numeric_distance(max, n2); + + std::cout << stream_number(min) << "->" << stream_number(n1) << "=="; + std::cout << std::dec << stream_number(d1) << "; "; + std::cout << std::hex << stream_number(max) << "->" << stream_number(n2) + << "==" << std::dec << stream_number(d2) << "..." << std::flush; + assert(d1 == max_distance); + assert(d2 == min_distance); +} + +template +void test_aux(signed_tag, Number* = 0) +{ + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + BOOST_STATIC_ASSERT( + (sizeof(Number) < sizeof(boost::intmax_t)) + | (boost::is_same::value)); + + // Force casting to Number here to work around the fact that it's an enum on MSVC + BOOST_STATIC_ASSERT(Number(complement_traits::max) > Number(0)); + BOOST_STATIC_ASSERT(Number(complement_traits::min) < Number(0)); + + const Number max = complement_traits::max; + const Number min = complement_traits::min; + + std::cout << std::hex << "min = " << stream_number(min) << ", max = " + << stream_number(max) << "..." << std::flush; + std::cout << "difference_type = " << typeid(difference_type).name() << "..." + << std::flush; + + typedef typename boost::detail::if_true< + (sizeof(Number) < sizeof(boost::intmax_t))> + ::template then< + in_range_tag, + out_of_range_tag + >::type + range_tag; + signed_test(range_tag()); +} + + +// Test for all numbers. The extra default Number parameter works around an MSVC +// bug. +template +void test(Number* = 0) +{ + std::cout << "testing " << typeid(Number).name() << ":\n" +#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS + << "is_signed: " << (std::numeric_limits::is_signed ? "true\n" : "false\n") + << "is_bounded: " << (std::numeric_limits::is_bounded ? "true\n" : "false\n") + << "digits: " << std::numeric_limits::digits << "\n" +#endif + << "..." << std::flush; + typedef typename boost::detail::numeric_traits::difference_type difference_type; + BOOST_STATIC_ASSERT(boost::detail::is_signed::value); + + typedef typename boost::detail::if_true< + boost::detail::is_signed::value + >::template then::type signedness; + + test_aux(signedness()); + std::cout << "passed" << std::endl; +} + +int main() +{ + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); +#if defined(ULLONG_MAX) || defined(ULONG_LONG_MAX) + test(); + test(); +#elif defined(BOOST_MSVC) + // The problem of not having compile-time static class constants other than + // enums prevents this from working, since values get truncated. + // test(); + // test(); +#endif + return 0; +} diff --git a/projection_iterator.htm b/projection_iterator.htm new file mode 100644 index 0000000..e410027 --- /dev/null +++ b/projection_iterator.htm @@ -0,0 +1,391 @@ + + + + + + +Projection Iterator Adaptor Documentation + + + + +c++boost.gif (8819 bytes) + +

Projection Iterator Adaptor

+ +Defined in header +boost/iterator_adaptors.hpp + +

+The projection iterator adaptor is similar to the transform iterator adaptor in that +its operator*() applies some function to the result of +dereferencing the base iterator and then returns the result. The +difference is that the function must return a reference to some +existing object (for example, a data member within the +value_type of the base iterator). The following +pseudo-code gives the basic idea. The data member p is +the function object. + +

+  reference projection_iterator::operator*() const {
+    return this->p(*this->base_iterator);
+  }
+
+ +

Synopsis

+ +
+namespace boost {
+  template <class AdaptableUnaryFunction, class BaseIterator>
+  struct projection_iterator_generator;
+  
+  template <class AdaptableUnaryFunction, 
+            class BaseIterator, class ConstBaseIterator>
+  struct projection_iterator_pair_generator;
+
+  template <class AdaptableUnaryFunction, class BaseIterator>
+  typename projection_iterator_generator<AdaptableUnaryFunction, BaseIterator>::type
+  make_projection_iterator(BaseIterator base,
+			   const AdaptableUnaryFunction& p = AdaptableUnaryFunction())
+
+  template <class AdaptableUnaryFunction, class ConstBaseIterator>
+  typename projection_iterator_generator<AdaptableUnaryFunction, ConstBaseIterator>::type
+  make_const_projection_iterator(ConstBaseIterator base,
+                                 const AdaptableUnaryFunction& p = AdaptableUnaryFunction())  
+}
+
+ +
+ +

The Projection Iterator Type +Generator

+ +The class projection_iterator_generator is a helper class +whose purpose is to construct an projection iterator type. The main +template parameter for this class is the AdaptableUnaryFunction +function object type and the BaseIterator type that is being +wrapped. + +
+template <class AdaptableUnaryFunction, class BaseIterator>
+class projection_iterator_generator
+{
+public:
+  typedef iterator_adaptor<...> type; // the resulting projection iterator type 
+};
+
+ +

Example

+ +In the following example we have a list of personnel records. Each +record has an employee's name and ID number. We want to be able to +traverse through the list accessing either the name or the ID numbers +of the employees using the projection iterator so we create the +function object classes select_name and +select_ID. We then use the +projection_iterator_generator class to create a projection +iterator and use it to print out the names of the employees. + +
+#include <boost/config.hpp>
+#include <list>
+#include <iostream>
+#include <iterator>
+#include <algorithm>
+#include <string>
+#include <boost/iterator_adaptors.hpp>
+
+struct personnel_record {
+  personnel_record(std::string n, int id) : m_name(n), m_ID(id) { }
+  std::string m_name;
+  int m_ID;
+};
+
+struct select_name {
+  typedef personnel_record argument_type;
+  typedef std::string result_type;
+  const std::string& operator()(const personnel_record& r) const {
+    return r.m_name;
+  }
+  std::string& operator()(personnel_record& r) const {
+    return r.m_name;
+  }
+};
+
+struct select_ID {
+  typedef personnel_record argument_type;
+  typedef int result_type;
+  const int& operator()(const personnel_record& r) const {
+    return r.m_ID;
+  }
+  int& operator()(personnel_record& r) const {
+    return r.m_ID;
+  }
+};
+
+int main(int, char*[])
+{
+  std::list<personnel_record> personnel_list;
+
+  personnel_list.push_back(personnel_record("Barney", 13423));
+  personnel_list.push_back(personnel_record("Fred", 12343));
+  personnel_list.push_back(personnel_record("Wilma", 62454));
+  personnel_list.push_back(personnel_record("Betty", 20490));
+
+  // Example of using projection_iterator_generator
+  // to print out the names in the personnel list.
+
+  boost::projection_iterator_generator<select_name,
+    std::list<personnel_record>::iterator>::type
+    personnel_first(personnel_list.begin()),
+    personnel_last(personnel_list.end());
+
+  std::copy(personnel_first, personnel_last,
+            std::ostream_iterator<std::string>(std::cout, "\n"));
+  std::cout << std::endl;
+
+  // to be continued...
+
+The output for this part is: +
+Barney
+Fred
+Wilma
+Betty
+
+ +

Template Parameters

+ + + + + + + + + + + + + + + + + +
ParameterDescription
AdaptableUnaryFunctionThe type of the function object. The argument_type of the +function must match the value type of the base iterator. The function +should return a reference to the function's result_type. +The result_type will be the resulting iterator's value_type. +
BaseIteratorThe iterator type being wrapped.
+ +

Model of

+ +If the base iterator is a model of Random +Access Iterator then so is the resulting projection iterator. If +the base iterator supports less functionality than this the resulting +projection iterator will also support less functionality. + +

Members

+ +The projection iterator type implements the member functions and +operators required of the Random +Access Iterator concept. +In addition it has the following constructor: + +
+projection_iterator_generator::type(const BaseIterator& it,
+                                    const AdaptableUnaryFunction& p = AdaptableUnaryFunction())
+
+ +

+


+

+ +

The Projection Iterator Pair +Generator

+ +Sometimes a mutable/const pair of iterator types is needed, such as +when implementing a container type. The +projection_iterator_pair_generator class makes it more +convenient to create this pair of iterator types. + +
+template <class AdaptableUnaryFunction, class BaseIterator, class ConstBaseIterator>
+class projection_iterator_pair_generator
+{
+public:
+  typedef iterator_adaptor<...> iterator;       // the mutable projection iterator type 
+  typedef iterator_adaptor<...> const_iterator; // the immutable projection iterator type 
+};
+
+ +

Example

+ +In this part of the example we use the +projection_iterator_pair_generator to create a mutable/const +pair of projection iterators that access the ID numbers of the +personnel. We use the mutable iterator to re-index the ID numbers from +zero. We then use the constant iterator to print the ID numbers out. + +
+  // continuing from the last example...
+
+  typedef boost::projection_iterator_pair_generator<select_ID,
+    std::list<personnel_record>::iterator,
+    std::list<personnel_record>::const_iterator> PairGen;
+
+  PairGen::iterator ID_first(personnel_list.begin()),
+    ID_last(personnel_list.end());
+
+  int new_id = 0;
+  while (ID_first != ID_last) {
+    *ID_first = new_id++;
+    ++ID_first;
+  }
+
+  PairGen::const_iterator const_ID_first(personnel_list.begin()),
+    const_ID_last(personnel_list.end());
+
+  std::copy(const_ID_first, const_ID_last,
+            std::ostream_iterator<int>(std::cout, " "));
+  std::cout << std::endl;
+  std::cout << std::endl;
+  
+  // to be continued...
+
+0 1 2 3 
+
+ +

Template Parameters

+ + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescription
AdaptableUnaryFunctionThe type of the function object. The argument_type of the +function must match the value type of the base iterator. The function +should return a true reference to the function's result_type. +The result_type will be the resulting iterator's value_type. +
BaseIteratorThe mutable iterator type being wrapped.
ConstBaseIteratorThe constant iterator type being wrapped.
+ +

Model of

+ +If the base iterator types model the Random +Access Iterator then so do the resulting projection iterator +types. If the base iterators support less functionality the +resulting projection iterator types will also support less +functionality. The resulting iterator type is mutable, and +the resulting const_iterator type is constant. + +

Members

+ +The resulting iterator and const_iterator types +implements the member functions and operators required of the Random +Access Iterator concept. In addition they support the following +constructors: + +
+projection_iterator_pair_generator::iterator(const BaseIterator& it,
+                                             const AdaptableUnaryFunction& p = AdaptableUnaryFunction())
+ +
+projection_iterator_pair_generator::const_iterator(const BaseIterator& it,
+                                                   const AdaptableUnaryFunction& p = AdaptableUnaryFunction())
+
+ +

+


+

+ +

The Projection Iterator Object Generators

+ +The make_projection_iterator() and +make_const_projection_iterator() functions provide a more +convenient way to create projection iterator objects. The functions +save the user the trouble of explicitly writing out the iterator +types. + +
+template <class AdaptableUnaryFunction, class BaseIterator>
+typename projection_iterator_generator<AdaptableUnaryFunction, BaseIterator>::type
+make_projection_iterator(BaseIterator base,
+			 const AdaptableUnaryFunction& p = AdaptableUnaryFunction())  
+
+template <class AdaptableUnaryFunction, class ConstBaseIterator>
+typename projection_iterator_generator<AdaptableUnaryFunction, ConstBaseIterator>::type
+make_const_projection_iterator(ConstBaseIterator base,
+			       const AdaptableUnaryFunction& p = AdaptableUnaryFunction())  
+
+ + +

Example

+ +In this part of the example, we again print out the names of the +personnel, but this time we use the +make_const_projection_iterator() function to save some typing. + +
+  // continuing from the last example...
+
+  std::copy
+    (boost::make_const_projection_iterator<select_name>(personnel_list.begin()),
+     boost::make_const_projection_iterator<select_name>(personnel_list.end()),
+     std::ostream_iterator(std::cout, "\n"));
+
+  return 0;
+}
+
+The output is: +
+Barney
+Fred
+Wilma
+Betty
+
+ +
+

Revised 16 Feb 2001

+

© Copyright Jeremy Siek 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.

+ + + + + + + +