From 7f2c4ed9813a5fbfac85aaff095e9c0d689d795d Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Sat, 6 Jan 2001 16:25:08 +0000 Subject: [PATCH] Initial commit after public review (note change in library name per review) [SVN r8514] --- cast.htm | 145 ++++++++++++++++++++ cast_test.cpp | 153 +++++++++++++++++++++ index.htm | 39 ++++++ lexical_cast.htm | 242 +++++++++++++++++++++++++++++++++ lexical_cast_test.cpp | 149 +++++++++++++++++++++ test.hpp | 302 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1030 insertions(+) create mode 100644 cast.htm create mode 100644 cast_test.cpp create mode 100644 index.htm create mode 100644 lexical_cast.htm create mode 100644 lexical_cast_test.cpp create mode 100644 test.hpp diff --git a/cast.htm b/cast.htm new file mode 100644 index 0000000..d64473b --- /dev/null +++ b/cast.htm @@ -0,0 +1,145 @@ + + + + + + +Header boost/cast.hpp Documentation + + + + +

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

+

Cast Functions

+

The header boost/cast.hpp +provides polymorphic_cast, polymorphic_downcast, +and numeric_cast function templates designed +to complement the C++ built-in casts.

+

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

+

Polymorphic casts

+

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

+

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

+

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

+

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

+

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

+

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

+

polymorphic_cast and polymorphic_downcast synopsis

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

polymorphic_downcast example

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

numeric_cast

+

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

+

The requirements on the argument and result types are:

+
+ +
+

numeric_cast synopsis

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

numeric_cast example

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

numeric_cast rationale

+

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

+

History

+

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

+
+

Revised  06 January, 2001

+

© Copyright boost.org 1999. Permission to copy, use, modify, sell and +distribute this document is granted provided this copyright notice appears in +all copies. This document is provided "as is" without express or +implied warranty, and with no claim as to its suitability for any purpose.

+ + + + diff --git a/cast_test.cpp b/cast_test.cpp new file mode 100644 index 0000000..1602f16 --- /dev/null +++ b/cast_test.cpp @@ -0,0 +1,153 @@ +// boost utility cast test program -----------------------------------------// + +// (C) Copyright boost.org 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. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 28 Jun 00 implicit_cast removed (Beman Dawes) +// 30 Aug 99 value_cast replaced by numeric_cast +// 3 Aug 99 Initial Version + +#include +#include +#include +#include + +# if SCHAR_MAX == LONG_MAX +# error "This test program doesn't work if SCHAR_MAX == LONG_MAX" +# endif + +using namespace boost; +using std::cout; + +namespace +{ + struct Base + { + virtual char kind() { return 'B'; } + }; + + struct Base2 + { + virtual char kind2() { return '2'; } + }; + + struct Derived : public Base, Base2 + { + virtual char kind() { return 'D'; } + }; +} + + +int main( int argc, char * argv[] ) +{ + cout << "Usage: test_casts [n], where n omitted or is:\n" + " 1 = execute #1 assert failure (#ifndef NDEBUG)\n" + " 2 = execute #2 assert failure (#ifndef NDEBUG)\n" + "Example: test_casts 2\n\n"; + +# ifdef NDEBUG + cout << "NDEBUG is defined\n"; +# else + cout << "NDEBUG is not defined\n"; +# endif + + cout << "\nBeginning tests...\n"; + +// test polymorphic_cast ---------------------------------------------------// + + // tests which should succeed + Base * base = new Derived; + Base2 * base2 = 0; + Derived * derived = 0; + derived = polymorphic_downcast( base ); // downcast + assert( derived->kind() == 'D' ); + + derived = 0; + derived = polymorphic_cast( base ); // downcast, throw on error + assert( derived->kind() == 'D' ); + + base2 = polymorphic_cast( base ); // crosscast + assert( base2->kind2() == '2' ); + + // tests which should result in errors being detected + int err_count = 0; + base = new Base; + + if ( argc > 1 && *argv[1] == '1' ) + { derived = polymorphic_downcast( base ); } // #1 assert failure + + bool caught_exception = false; + try { derived = polymorphic_cast( base ); } + catch (std::bad_cast) + { cout<<"caught bad_cast\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + // the following is just so generated code can be inspected + if ( derived->kind() == 'B' ) ++err_count; + +// test implicit_cast and numeric_cast -------------------------------------// + + // tests which should succeed + long small_value = 1; + long small_negative_value = -1; + long large_value = std::numeric_limits::max(); + long large_negative_value = std::numeric_limits::min(); + signed char c = 0; + + c = large_value; // see if compiler generates warning + + c = numeric_cast( small_value ); + assert( c == 1 ); + c = 0; + c = numeric_cast( small_value ); + assert( c == 1 ); + c = 0; + c = numeric_cast( small_negative_value ); + assert( c == -1 ); + + // These tests courtesy of Joe R NWP Swatosh + assert( 0.0f == numeric_cast( 0.0 ) ); + assert( 0.0 == numeric_cast( 0.0 ) ); + + // tests which should result in errors being detected + + caught_exception = false; + try { c = numeric_cast( large_value ); } + catch (bad_numeric_cast) + { cout<<"caught bad_numeric_cast #1\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + + caught_exception = false; + try { c = numeric_cast( large_negative_value ); } + catch (bad_numeric_cast) + { cout<<"caught bad_numeric_cast #2\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + + unsigned long ul; + caught_exception = false; + try { ul = numeric_cast( large_negative_value ); } + catch (bad_numeric_cast) + { cout<<"caught bad_numeric_cast #3\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + + caught_exception = false; + try { ul = numeric_cast( small_negative_value ); } + catch (bad_numeric_cast) + { cout<<"caught bad_numeric_cast #4\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + + caught_exception = false; + try { numeric_cast( std::numeric_limits::max() ); } + catch (bad_numeric_cast) + { cout<<"caught bad_numeric_cast #5\n"; caught_exception = true; } + if ( !caught_exception ) ++err_count; + + cout << err_count << " errors detected\nTest " + << (err_count==0 ? "passed\n" : "failed\n"); + return err_count; +} // main diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..3833736 --- /dev/null +++ b/index.htm @@ -0,0 +1,39 @@ + + + + + + + +Boost Cast Library + + + + +

Boost +Conversion Library

+ +

The Conversion Library improves program safety and clarity by performing +otherwise messy conversions.  It includes cast-style function templates designed to complement the C++ +Standard's built-in casts.

+

To reduce coupling, particularly to standard library IOStreams, the Boost +Conversion Library is +supplied by several headers:

+
    +
  • The boost/cast header provides polymorphic_cast<> + and polymorphic_downcast<> to perform safe casting between + polymorphic types, and numeric_cast<> to perform safe casting + between numeric types.
    +
  • +
  • The boost/lexical_cast header provides lexical_cast<> + general literal text conversions, such as an int represented as + a string, or vice-versa.
  • +
+
+

Revised 06 January, 2001 +

+ + + + diff --git a/lexical_cast.htm b/lexical_cast.htm new file mode 100644 index 0000000..2a4af6d --- /dev/null +++ b/lexical_cast.htm @@ -0,0 +1,242 @@ + + + + lexical_cast + + + + + + +

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

+ + + +
+

Motivation

+ +Sometimes a value must be converted to a literal text form, such as an +int represented as a string, or vice-versa, when +a string is interpreted as an int. Such examples +are common when converting between data types internal to a program and +representation external to a program, such as windows and configuration files. +

+The standard C and C++ libraries offer a number of facilities for performing +such conversions. However, they vary with their ease of use, extensibility, +and safety. +

+For instance, there are a number of limitations with the family of standard C +functions typified by atoi: +

    +
  • + Conversion is supported in one direction only: from text to + internal data type. Converting the other way using the C library + requires either the inconvenience and compromised safety of the + sprintf function, or the loss of portability associated + with non-standard functions such as itoa. +
  • +
  • + The range of types supported is only a subset of the built-in numeric + types, namely int, long, + and double. +
  • +
  • + The range of types cannot be extended in a uniform manner. For + instance, conversion from string representation to + complex or rational. +
  • +
+The standard C functions typified by strtol have the same basic +limitations, but offer finer control over the conversion process. However, for +the common case such control is often either not required or not used. The +scanf family of functions offer even greater control, but also +lack safety and ease of use. +

+The standard C++ library offers stringstream for the kind of +in-core formatting being discussed. It offers a great deal of control over the +formatting and conversion of I/O to and from arbitrary types through text. +However, for simple conversions direct use of stringstream can be +either clumsy (with the introduction of extra local variables and the loss of +infix-expression convenience) or obscure (where stringstream +objects are created as temporary objects in an expression). Facets provide a +comprehensive concept and facility for controlling textual representation, but +their relatively high entry level requires an extreme degree of involvement +for simple conversions. +

+The lexical_cast template function offers a convenient and consistent +form for supporting common conversions to and from arbitrary types when they are +represented as text. The simplification it offers is in expression-level +convenience for such conversions. For more involved conversions, such as where +precision or formatting need tighter control than is offered by the default +behavior of lexical_cast, the conventional +stringstream approach is recommended. Where the conversions are +numeric to numeric, numeric_cast may offer more reasonable +behavior than lexical_cast. +

+ +


+

Examples

+ +The following example treats command line arguments as a sequence of numeric data: +
+
+int main(int argc, char * argv[])
+{
+    using boost::lexical_cast;
+    using boost::bad_lexical_cast;
+
+    std::vector<short> args;
+
+    while(*++argv)
+    {
+        try
+        {
+            args.push_back(lexical_cast<short>(*argv));
+        }
+        catch(bad_lexical_cast &)
+        {
+            args.push_back(0);
+        }
+    }
+    ...
+}
+
+
+ +The following example uses numeric data in a string expression: +
+
+void log_message(const std::string &);
+
+void log_errno(int yoko)
+{
+    log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko));
+}
+
+
+ +
+

Synopsis

+ +Library features defined in "boost/lexical_cast.hpp": + +
+
+namespace boost
+{
+    class bad_lexical_cast;
+    template<typename Target, typename Source>
+      Target lexical_cast(Source arg);
+}
+
+
+ +Test harness defined in "lexical_cast_test.cpp". +

+ +


+

lexical_cast

+ +
+
+template<typename Target, typename Source>
+  Target lexical_cast(Source arg);
+
+
+ +Returns the result of streaming arg into a std::stringstream and then +out as a Target object. The conversion is parameterized by the current +lexical_context, if set. If the conversion is +unsuccessful, a bad_lexical_cast exception is thrown +if the current lexical_context is set for throwing or +if there is no current lexical_context set, otherwise a +Target() is returned. +

+The requirements on the argument and result types are: +

    +
  • + Source is OutputStreamable, meaning that an + operator<< is defined that takes a + std::ostream object on the left hand side and an instance + of the argument type on the right. +
  • +
  • + Both Source and Target are CopyConstructible [20.1.3]. +
  • +
  • + Target is InputStreamable, meaning that an + operator>> is defined that takes a + std::istream object on the left hand side and an instance + of the result type on the right. +
  • +
  • + Target is DefaultConstructible, meaning that it is + possible to default-initialize an object of that type [8.5, 20.1.3]. +
  • +
  • + Target is Assignable [23.1]. +
  • +
+

+ +


+

bad_lexical_cast

+ +
+
+class bad_lexical_cast : public std::bad_cast
+{
+public:
+    virtual const char * what() const throw();
+};
+
+
+ +Exception used to indicate runtime lexical_cast failure. +

+ +


+

Portability

+ +To date the code and test harness have been compiled successfully using +Microsoft Visual C++ 6.0, Borland C++ 5.5, and GNU g++ 2.91. Tests have run successfully for +Microsoft Visual C++ 6.0 and Borland C++ 5.5. For g++ streams interpret any integer, rather than +just 0 and 1, as valid for bool; the other tests pass +without problem. The deprecated standard header <strstream> is used in +preference to the standard <sstream> header for out-of-the-box g++ support. +

+ +


+

Future directions

+ +
    +
  • + A mechanism for providing quality-of-service control is needed, e.g. formatting and exception + behavior. In the name of simplicity (and release), the current version strips out an earlier + experimental version. +
  • +
  • + Wide character and incompatible std::basic_string issues need to be catered for. +
  • +
  • + An interpret_cast that performs a do-something-reasonable conversion between + types. It would, for instance, select between numeric_cast and lexical_cast + based on std::numeric_limits<>::is_specialized. +
  • +
+ +
+ +
© Copyright Kevlin Henney, 2000
+ + + diff --git a/lexical_cast_test.cpp b/lexical_cast_test.cpp new file mode 100644 index 0000000..c19cf08 --- /dev/null +++ b/lexical_cast_test.cpp @@ -0,0 +1,149 @@ +// boost lexical_cast_test.cpp program -------------------------------------// + +// See http://www.boost.org for most recent version including documentation. + +// what: lexical_cast custom keyword cast tests +// who: contributed by Kevlin Henney +// when: October 2000 +// where: tested with MSVC 6.0 and BCC 5.5 + +#include +#include "test.hpp" +#include +#include +#include + +using namespace boost; +using namespace std; + +typedef test::test test_case; +typedef const test_case * test_case_iterator; + +extern const test_case_iterator begin, end; + +int main() +{ + test::tester test_suite(begin, end); + return test_suite() ? EXIT_SUCCESS : EXIT_FAILURE; +} + +void test_to_string() +{ + test::check_equal( + lexical_cast(2001), "2001", + "2001 -> \"2001\""); + test::check_equal( + lexical_cast(2001.0), "2001", + "2001.0 ->\"2001\""); + test::check_equal( + lexical_cast(complex(2000,1)), "(2000,1)", + "complex(2000,1) -> \"(2000,1)\""); +} + +void test_to_int() +{ + test::check_equal( + lexical_cast("2001"), 2001, + "\"2001\" -> 2001"); + test::check_equal( + lexical_cast(" 2001"), 2001, + "\" 2001\" -> 2001"); + test::check_equal( + lexical_cast("2001 "), 2001, + "\"2001 \" -> 2001"); + TEST_CHECK_THROW( + lexical_cast("Two thousand and one"), + bad_lexical_cast, + "\"Two thousand and one\""); + TEST_CHECK_THROW( + lexical_cast("2001: A Space Odyssey"), + bad_lexical_cast, + "\"2001: A Space Odyssey\""); + TEST_CHECK_THROW( + lexical_cast(200.1), + bad_lexical_cast, + "200.1"); + TEST_CHECK_THROW( + lexical_cast("200e1"), + bad_lexical_cast, + "\"200e1\""); +} + +void test_to_char() +{ + test::check_equal( + lexical_cast("2"), '2', + "\"2\" -> '2'"); + test::check_equal( + lexical_cast(" 2"), '2', + "\" 2\" -> '2'"); + test::check_equal( + lexical_cast("2 "), '2', + "\"2 \" -> '2'"); + test::check_equal( + lexical_cast(2), '2', + "2 -> '2'"); + TEST_CHECK_THROW( + lexical_cast("2001"), + bad_lexical_cast, + "\"2001\""); + TEST_CHECK_THROW( + lexical_cast(2001), + bad_lexical_cast, + "2001"); +} + +void test_to_double() +{ + test::check_equal( + lexical_cast("1e6"), 1e6, + "\"1e6\" -> 1e6"); + test::check_equal( + lexical_cast("1e-2"), 1e-2, + "\"1e-2\" -> 1e-2"); +} + +void test_to_bool() +{ + test::check_equal( + lexical_cast(1), true, + "1 -> true"); + test::check_equal( + lexical_cast('0'), false, + "'0' -> false"); + TEST_CHECK_THROW( + lexical_cast(2001), + bad_lexical_cast, + "2001"); + TEST_CHECK_THROW( + lexical_cast(2), + bad_lexical_cast, + "2"); + TEST_CHECK_THROW( + lexical_cast("true thousand and one"), + bad_lexical_cast, + "\"true thousand and one\""); +} + +const test_case test_cases[] = +{ + { "lexical_cast", test_to_string }, + { "lexical_cast", test_to_int }, + { "lexical_cast", test_to_char }, + { "lexical_cast", test_to_double }, + { "lexical_cast", test_to_bool } +}; + +const test_case_iterator begin = test_cases; +const test_case_iterator end = + test_cases + (sizeof test_cases / sizeof *test_cases); + +// Copyright Kevlin Henney, 2000. All rights reserved. +// +// Permission to use, copy, modify, and distribute this software for any +// purpose is hereby granted without fee, provided that this copyright and +// permissions notice appear in all copies and derivatives, and that no +// charge may be made for the software and its documentation except to cover +// cost of distribution. +// +// This software is provided "as is" without express or implied warranty. diff --git a/test.hpp b/test.hpp new file mode 100644 index 0000000..ba183ef --- /dev/null +++ b/test.hpp @@ -0,0 +1,302 @@ +// what: simple unit test framework +// who: developed by Kevlin Henney +// when: November 2000 +// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.91 + +#ifndef TEST_INCLUDED +#define TEST_INCLUDED + +#include +#include +#include // for out-of-the-box g++ +#include + +namespace test // test tuple comprises name and nullary function (object) +{ + template + struct test + { + string_type name; + function_type action; + + static test make(string_type name, function_type action) + { + test result; // MSVC aggreggate initializer bugs + result.name = name; + result.action = action; + return result; + } + }; +} + +namespace test // failure exception used to indicate checked test failures +{ + class failure : public std::exception + { + public: // struction (default cases are OK) + + failure(const std::string & why) + : reason(why) + { + } + + public: // usage + + virtual const char * what() const throw() + { + return reason.c_str(); + } + + private: // representation + + std::string reason; + + }; +} + +namespace test // not_implemented exception used to mark unimplemented tests +{ + class not_implemented : public std::exception + { + public: // usage (default ctor and dtor are OK) + + virtual const char * what() const throw() + { + return "not implemented"; + } + + }; +} + +namespace test // test utilities +{ + inline void check(bool condition, const std::string & description) + { + if(!condition) + { + throw failure(description); + } + } + + inline void check_true(bool value, const std::string & description) + { + check(value, "expected true: " + description); + } + + inline void check_false(bool value, const std::string & description) + { + check(!value, "expected false: " + description); + } + + template + void check_equal( + const lhs_type & lhs, const rhs_type & rhs, + const std::string & description) + { + check(lhs == rhs, "expected equal values: " + description); + } + + template + void check_unequal( + const lhs_type & lhs, const rhs_type & rhs, + const std::string & description) + { + check(lhs != rhs, "expected unequal values: " + description); + } + + inline void check_null(const void * ptr, const std::string & description) + { + check(!ptr, "expected null pointer: " + description); + } + + inline void check_non_null(const void * ptr, const std::string & description) + { + check(ptr, "expected non-null pointer: " + description); + } +} + +#define TEST_CHECK_THROW(expression, exception, description) \ + try \ + { \ + expression; \ + throw ::test::failure(description); \ + } \ + catch(exception &) \ + { \ + } + +namespace test // memory tracking (enabled if test new and delete linked in) +{ + class allocations + { + public: // singleton access + + static allocations & instance() + { + static allocations singleton; + return singleton; + } + + public: // logging + + void clear() + { + alloc_count = dealloc_count = 0; + } + + void allocation() + { + ++alloc_count; + } + + void deallocation() + { + ++dealloc_count; + } + + public: // reporting + + unsigned long allocated() const + { + return alloc_count; + } + + unsigned long deallocated() const + { + return dealloc_count; + } + + bool balanced() const + { + return alloc_count == dealloc_count; + } + + private: // structors (default dtor is fine) + + allocations() + : alloc_count(0), dealloc_count(0) + { + } + + private: // prevention + + allocations(const allocations &); + allocations & operator=(const allocations &); + + private: // state + + unsigned long alloc_count, dealloc_count; + + }; +} + +namespace test // tester is the driver class for a sequence of tests +{ + template + class tester + { + public: // structors (default destructor is OK) + + tester(test_iterator first_test, test_iterator after_last_test) + : begin(first_test), end(after_last_test) + { + } + + public: // usage + + bool operator()(); // returns true if all tests passed + + private: // representation + + test_iterator begin, end; + + private: // prevention + + tester(const tester &); + tester &operator=(const tester &); + + }; + + template + bool tester::operator()() + { + using namespace std; + + unsigned long passed = 0, failed = 0, unimplemented = 0; + + for(test_iterator current = begin; current != end; ++current) + { + cerr << "[" << current->name << "] " << flush; + string result = "passed"; // optimistic + + try + { + allocations::instance().clear(); + current->action(); + + if(!allocations::instance().balanced()) + { + unsigned long allocated = allocations::instance().allocated(); + unsigned long deallocated = allocations::instance().deallocated(); + ostrstream report; + report << "new/delete (" + << allocated << " allocated, " + << deallocated << " deallocated)" + << ends; + const char * text = report.str(); + report.freeze(false); + throw failure(text); + } + + ++passed; + } + catch(const failure & caught) + { + (result = "failed: ") += caught.what(); + ++failed; + } + catch(const not_implemented &) + { + result = "not implemented"; + ++unimplemented; + } + catch(const exception & caught) + { + (result = "exception: ") += caught.what(); + ++failed; + } + catch(...) + { + result = "failed with unknown exception"; + ++failed; + } + + cerr << result << endl; + } + + cerr << passed + failed << " tests: " + << passed << " passed, " + << failed << " failed"; + + if(unimplemented) + { + cerr << " (" << unimplemented << " not implemented)"; + } + + cerr << endl; + + return failed == 0; + } +} + +#endif + +// Copyright Kevlin Henney, 2000. All rights reserved. +// +// Permission to use, copy, modify, and distribute this software for any +// purpose is hereby granted without fee, provided that this copyright and +// permissions notice appear in all copies and derivatives, and that no +// charge may be made for the software and its documentation except to cover +// cost of distribution. +// +// This software is provided "as is" without express or implied warranty.