Initial commit after public review (note change in library name per review)

[SVN r8514]
This commit is contained in:
Beman Dawes
2001-01-06 16:25:08 +00:00
parent beb912a139
commit 7f2c4ed981
6 changed files with 1030 additions and 0 deletions

145
cast.htm Normal file
View File

@@ -0,0 +1,145 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Header boost/cast.hpp Documentation</title>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<h1><img src="../../c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center" width="277" height="86">Header
<a href="../../boost/cast.hpp">boost/cast.hpp</a></h1>
<h2><a name="Cast Functions">Cast Functions</a></h2>
<p>The <code>header <a href="../../boost/cast.hpp">boost/cast.hpp</a></code>
provides <a href="#Polymorphic_cast"><b>polymorphic_cast</b></a>, <a href="#Polymorphic_cast"><b>polymorphic_downcast</b></a>,
and <a href="#numeric_cast"><b>numeric_cast</b></a> function templates designed
to complement the C++ built-in casts.</p>
<p>The program&nbsp;<a href="cast_test.cpp">cast_test.cpp</a> can be used to
verify these function templates work as expected.</p>
<h3><a name="Polymorphic_cast">Polymorphic casts</a></h3>
<p>Pointers to polymorphic objects (objects of classes which define at least one
virtual function) are sometimes downcast or crosscast.&nbsp; Downcasting means
casting from a base class to a derived class.&nbsp; Crosscasting means casting
across an inheritance hierarchy diagram, such as from one base to the other in a
<b>Y</b> diagram hierarchy.</p>
<p>Such casts can be done with old-style casts, but this approach is never to be
recommended.&nbsp; Old-style casts are sorely lacking in type safety, suffer
poor readability, and are difficult to locate with search tools.</p>
<p>The C++ built-in <b>static_cast</b> 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 <b>polymorphic_downcast</b>
template retains the efficiency of <b>static_cast</b> for non-debug
compilations, but for debug compilations adds safety via an assert() that a <b>dynamic_cast</b>
succeeds.&nbsp;<b>&nbsp;</b></p>
<p>The C++ built-in <b>dynamic_cast</b> 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.&nbsp; The <b>polymorphic_cast</b> template performs a <b>dynamic_cast</b>,
and throws an exception if the <b>dynamic_cast</b> returns 0.</p>
<p>A <b>polymorphic_downcast</b> 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, <b>polymorphic_cast</b> is
preferred.&nbsp; It must also be used for crosscasts.&nbsp; It does an assert(
dynamic_cast&lt;Derived&gt;(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.<b> Warning:</b>: Because <b>polymorphic_downcast</b>
uses assert(), it violates the one definition rule (ODR) if NDEBUG is inconsistently
defined across translation units.&nbsp; [See ISO Std 3.2]</p>
<p>The C++ built-in <b>dynamic_cast</b> must be used to cast references rather
than pointers.&nbsp; 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.</p>
<h3>polymorphic_cast and polymorphic_downcast synopsis</h3>
<blockquote>
<pre>namespace boost {
template &lt;class Derived, class Base&gt;
inline Derived polymorphic_cast(Base* x);
// Throws: std::bad_cast if ( dynamic_cast&lt;Derived&gt;(x) == 0 )
// Returns: dynamic_cast&lt;Derived&gt;(x)
template &lt;class Derived, class Base&gt;
inline Derived polymorphic_downcast(Base* x);
// Effects: assert( dynamic_cast&lt;Derived&gt;(x) == x );
// Returns: static_cast&lt;Derived&gt;(x)
}</pre>
</blockquote>
<h3>polymorphic_downcast example</h3>
<blockquote>
<pre>#include &lt;boost/cast.hpp&gt;
...
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&lt;Banana*&gt;(fruit);
...</pre>
</blockquote>
<h3><a name="numeric_cast">numeric_cast</a></h3>
<p>A <b>static_cast</b> or implicit conversion will not
detect failure to preserve range for numeric casts. The <b>numeric_cast</b> function
templates are similar to <b>static_cast</b> 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.</p>
<p>The requirements on the argument and result types are:</p>
<blockquote>
<ul>
<li>Both argument and result types are CopyConstructible [ISO Std 20.1.3].</li>
<li>Both argument and result types are Numeric, defined by <code>std::numeric_limits&lt;&gt;::is_specialized</code>
being true.</li>
<li>The argument can be converted to the result type using <b>static_cast</b>.</li>
</ul>
</blockquote>
<h3>numeric_cast synopsis</h3>
<blockquote>
<pre>namespace boost {
class bad_numeric_cast : public std::bad_cast {...};
template&lt;typename Target, typename Source&gt;
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&lt;Target&gt;(arg)
}</pre>
</blockquote>
<h3>numeric_cast example</h3>
<blockquote>
<pre>#include &lt;boost/cast.hpp&gt;
using namespace boost::cast;
void ariane(double vx)
{
...
unsigned short dx = numeric_cast&lt;unsigned short&gt;(vx);
...
}</pre>
</blockquote>
<h3>numeric_cast rationale</h3>
<p>The form of the throws condition is specified so that != is not a required
operation.</p>
<h3>History</h3>
<p><b>polymorphic_cast</b> was suggested by Bjarne Stroustrup in &quot;The C++
Programming Language&quot;.<br>
<b>polymorphic_downcast</b> was contributed by <a href="../../people/dave_abrahams.htm">Dave
Abrahams</a>.<b><br>
numeric_cast</b> was contributed by <a href="../../people/kevlin_henney.htm">Kevlin
Henney</a>.</p>
<hr>
<p>Revised&nbsp; <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan
-->06 January, 2001<!--webbot bot="Timestamp" endspan i-checksum="38320"
--></p>
<p><EFBFBD> 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 &quot;as is&quot; without express or
implied warranty, and with no claim as to its suitability for any purpose.</p>
</body>
</html>

153
cast_test.cpp Normal file
View File

@@ -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 <iostream>
#include <climits>
#include <limits>
#include <boost/cast.hpp>
# 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<Derived*>( base ); // downcast
assert( derived->kind() == 'D' );
derived = 0;
derived = polymorphic_cast<Derived*>( base ); // downcast, throw on error
assert( derived->kind() == 'D' );
base2 = polymorphic_cast<Base2*>( 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<Derived*>( base ); } // #1 assert failure
bool caught_exception = false;
try { derived = polymorphic_cast<Derived*>( 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<long>::max();
long large_negative_value = std::numeric_limits<long>::min();
signed char c = 0;
c = large_value; // see if compiler generates warning
c = numeric_cast<signed char>( small_value );
assert( c == 1 );
c = 0;
c = numeric_cast<signed char>( small_value );
assert( c == 1 );
c = 0;
c = numeric_cast<signed char>( small_negative_value );
assert( c == -1 );
// These tests courtesy of Joe R NWP Swatosh<joe.r.swatosh@usace.army.mil>
assert( 0.0f == numeric_cast<float>( 0.0 ) );
assert( 0.0 == numeric_cast<double>( 0.0 ) );
// tests which should result in errors being detected
caught_exception = false;
try { c = numeric_cast<signed char>( 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<signed char>( 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<unsigned long>( 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<unsigned long>( 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<int>( std::numeric_limits<double>::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

39
index.htm Normal file
View File

@@ -0,0 +1,39 @@
<html>
<head>
<meta http-equiv="Content-Language" content="en-us">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Boost Cast Library</title>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<h1><img border="0" src="../../c++boost.gif" align="center" width="277" height="86">Boost
Conversion Library</h1>
<p>The Conversion Library improves program safety and clarity by performing
otherwise messy conversions.&nbsp; It includes cast-style function templates designed to complement the C++
Standard's built-in casts.</p>
<p>To reduce coupling, particularly to standard library IOStreams, the Boost
Conversion Library is
supplied by several headers:</p>
<ul>
<li>The <a href="cast.htm">boost/cast</a> header provides <b>polymorphic_cast&lt;&gt;</b>
and <b>polymorphic_downcast&lt;&gt;</b> to perform safe casting between
polymorphic types, and <b> numeric_cast</b><i>&lt;&gt;</i> to perform safe casting
between numeric types.<br>
</li>
<li>The <a href="lexical_cast.htm">boost/lexical_cast</a> header provides <b>lexical_cast&lt;&gt;</b>
general literal text conversions, such as an <code>int</code> represented as
a <code>string</code>, or vice-versa.</li>
</ul>
<hr>
<p>Revised <!--webbot bot="Timestamp" S-Type="EDITED"
S-Format="%d %B, %Y" startspan -->06 January, 2001<!--webbot bot="Timestamp" endspan i-checksum="38320" -->
</p>
</body>
</html>

242
lexical_cast.htm Normal file
View File

@@ -0,0 +1,242 @@
<!doctype html public "-//W3C//DTD HTML Transitional 4.0//EN">
<html>
<head>
<title>lexical_cast</title>
<meta name="author" content="Kevlin Henney, mailto:kevlin@curbralan.com">
<meta name="generator" content="Microsoft FrontPage 4.0">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<h1><img src="../../c++boost.gif" alt="c++boost.gif (8819 bytes)" align="center" width="277" height="86">Header
<a href="../../boost/lexical_cast.hpp">boost/lexical_cast.hpp</a></h1>
<ul>
<li><a href="#motivation">Motivation</li>
<li></a><a href="#examples">Examples</li>
<li></a><a href="#synopsis">Synopsis</li>
<li></a><a href="#lexical_cast"><code>lexical_cast</code></li>
<li></a><a href="#bad_lexical_cast"><code>bad_lexical_cast</code></li>
<li></a><a href="#portability">Portability</li>
<li></a><a href="#future">Future directions</li>
</ul>
<hr>
<h2><a name="motivation">Motivation</a></h2>
Sometimes a value must be converted to a literal text form, such as an
<code>int</code> represented as a <code>string</code>, or vice-versa, when
a <code>string</code> is interpreted as an <code>int</code>. 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.
<p>
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.
<p>
For instance, there are a number of limitations with the family of standard C
functions typified by <code>atoi</code>:
<ul>
<li>
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
<code>sprintf</code> function, or the loss of portability associated
with non-standard functions such as <code>itoa</code>.
</li>
<li>
The range of types supported is only a subset of the built-in numeric
types, namely <code>int</code>, <code>long</code>,
and <code>double</code>.
</li>
<li>
The range of types cannot be extended in a uniform manner. For
instance, conversion from string representation to
<code>complex</code> or <code>rational</code>.
</li>
</ul>
The standard C functions typified by <code>strtol</code> 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
<code>scanf</code> family of functions offer even greater control, but also
lack safety and ease of use.
<p>
The standard C++ library offers <code>stringstream</code> 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 <code>stringstream</code> can be
either clumsy (with the introduction of extra local variables and the loss of
infix-expression convenience) or obscure (where <code>stringstream</code>
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.
<p>
The <code>lexical_cast</code> 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 <code>lexical_cast</code>, the conventional
<code>stringstream</code> approach is recommended. Where the conversions are
numeric to numeric, <code><a href="cast.htm#numeric_cast">numeric_cast</a></code> may offer more reasonable
behavior than <code>lexical_cast</code>.
<p>
<hr>
<h2><a name="examples">Examples</a></h2>
The following example treats command line arguments as a sequence of numeric data:
<blockquote>
<pre>
int main(int argc, char * argv[])
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
std::vector&lt;short&gt; args;
while(*++argv)
{
try
{
args.push_back(lexical_cast&lt;short&gt;(*argv));
}
catch(bad_lexical_cast &amp;)
{
args.push_back(0);
}
}
...
}
</pre>
</blockquote>
The following example uses numeric data in a string expression:
<blockquote>
<pre>
void log_message(const std::string &amp;);
void log_errno(int yoko)
{
log_message(&quot;Error &quot; + boost::lexical_cast&lt;std::string&gt;(yoko) + &quot;: &quot; + strerror(yoko));
}
</pre>
</blockquote>
<hr>
<h2><a name="synopsis">Synopsis</a></h2>
Library features defined in <a href="lexical_cast.hpp"><code>&quot;boost/lexical_cast.hpp&quot;</code></a>:
<blockquote>
<pre>
namespace boost
{
class <a href="#bad_lexical_cast">bad_lexical_cast</a>;
template&lt;typename Target, typename Source&gt;
Target <a href="#lexical_cast">lexical_cast</a>(Source arg);
}
</pre>
</blockquote>
Test harness defined in <a href="lexical_cast_test.cpp"><code>&quot;lexical_cast_test.cpp&quot;</code></a>.
<p>
<hr>
<h2><a name="lexical_cast"><code>lexical_cast</code></a></h2>
<blockquote>
<pre>
template&lt;typename Target, typename Source&gt;
Target lexical_cast(Source arg);
</pre>
</blockquote>
Returns the result of streaming <code>arg</code> into a <code>std::stringstream</code> and then
out as a <code>Target</code> object. The conversion is parameterized by the current
<a href="#lexical_context"><code>lexical_context</code></a>, if set. If the conversion is
unsuccessful, a <a href="#bad_lexical_cast"><code>bad_lexical_cast</code></a> exception is thrown
if the current <a href="#lexical_context"><code>lexical_context</code></a> is set for throwing or
if there is no current <a href="#lexical_context"><code>lexical_context</code></a> set, otherwise a
<code>Target()</code> is returned.
<p>
The requirements on the argument and result types are:
<ul>
<li>
<code>Source</code> is <i>OutputStreamable</i>, meaning that an
<code>operator&lt;&lt;</code> is defined that takes a
<code>std::ostream</code> object on the left hand side and an instance
of the argument type on the right.
</li>
<li>
Both <code>Source</code> and <code>Target</code> are <i>CopyConstructible</i> [20.1.3].
</li>
<li>
<code>Target</code> is <i>InputStreamable</i>, meaning that an
<code>operator&gt;&gt;</code> is defined that takes a
<code>std::istream</code> object on the left hand side and an instance
of the result type on the right.
</li>
<li>
<code>Target</code> is <i>DefaultConstructible</i>, meaning that it is
possible to <i>default-initialize</i> an object of that type [8.5, 20.1.3].
</li>
<li>
<code>Target</code> is <i>Assignable</i> [23.1].
</li>
</ul>
<p>
<hr>
<h2><a name="bad_lexical_cast"><code>bad_lexical_cast</code></a></h2>
<blockquote>
<pre>
class bad_lexical_cast : public std::bad_cast
{
public:
virtual const char * what() const throw();
};
</pre>
</blockquote>
Exception used to indicate runtime <a href="#lexical_cast"><code>lexical_cast</code></a> failure.
<p>
<hr>
<h2><a name="portability">Portability</a></h2>
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 <code>0</code> and <code>1</code>, as valid for <code>bool</code>; the other tests pass
without problem. The deprecated standard header <code>&lt;strstream&gt;</code> is used in
preference to the standard <code>&lt;sstream&gt;</code> header for out-of-the-box g++ support.
<p>
<hr>
<h2><a name="future">Future directions</a></h2>
<ul>
<li>
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.
</li>
<li>
Wide character and incompatible <code>std::basic_string</code> issues need to be catered for.
</li>
<li>
An <code>interpret_cast</code> that performs a <i>do-something-reasonable</i> conversion between
types. It would, for instance, select between <code>numeric_cast</code> and <code>lexical_cast</code>
based on <code>std::numeric_limits<>::is_specialized</code>.
</li>
</ul>
<hr>
<div align="right"><small><i>&copy; Copyright Kevlin Henney, 2000</i></small></div>
</body>
</html>

149
lexical_cast_test.cpp Normal file
View File

@@ -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 <boost/lexical_cast.hpp>
#include "test.hpp"
#include <complex>
#include <iostream>
#include <string>
using namespace boost;
using namespace std;
typedef test::test<const char *, void (*)()> test_case;
typedef const test_case * test_case_iterator;
extern const test_case_iterator begin, end;
int main()
{
test::tester<test_case_iterator> test_suite(begin, end);
return test_suite() ? EXIT_SUCCESS : EXIT_FAILURE;
}
void test_to_string()
{
test::check_equal(
lexical_cast<string>(2001), "2001",
"2001 -> \"2001\"");
test::check_equal(
lexical_cast<string>(2001.0), "2001",
"2001.0 ->\"2001\"");
test::check_equal(
lexical_cast<string>(complex<double>(2000,1)), "(2000,1)",
"complex<double>(2000,1) -> \"(2000,1)\"");
}
void test_to_int()
{
test::check_equal(
lexical_cast<int>("2001"), 2001,
"\"2001\" -> 2001");
test::check_equal(
lexical_cast<int>(" 2001"), 2001,
"\" 2001\" -> 2001");
test::check_equal(
lexical_cast<int>("2001 "), 2001,
"\"2001 \" -> 2001");
TEST_CHECK_THROW(
lexical_cast<int>("Two thousand and one"),
bad_lexical_cast,
"\"Two thousand and one\"");
TEST_CHECK_THROW(
lexical_cast<int>("2001: A Space Odyssey"),
bad_lexical_cast,
"\"2001: A Space Odyssey\"");
TEST_CHECK_THROW(
lexical_cast<int>(200.1),
bad_lexical_cast,
"200.1");
TEST_CHECK_THROW(
lexical_cast<int>("200e1"),
bad_lexical_cast,
"\"200e1\"");
}
void test_to_char()
{
test::check_equal(
lexical_cast<char>("2"), '2',
"\"2\" -> '2'");
test::check_equal(
lexical_cast<char>(" 2"), '2',
"\" 2\" -> '2'");
test::check_equal(
lexical_cast<char>("2 "), '2',
"\"2 \" -> '2'");
test::check_equal(
lexical_cast<char>(2), '2',
"2 -> '2'");
TEST_CHECK_THROW(
lexical_cast<char>("2001"),
bad_lexical_cast,
"\"2001\"");
TEST_CHECK_THROW(
lexical_cast<char>(2001),
bad_lexical_cast,
"2001");
}
void test_to_double()
{
test::check_equal(
lexical_cast<double>("1e6"), 1e6,
"\"1e6\" -> 1e6");
test::check_equal(
lexical_cast<double>("1e-2"), 1e-2,
"\"1e-2\" -> 1e-2");
}
void test_to_bool()
{
test::check_equal(
lexical_cast<bool>(1), true,
"1 -> true");
test::check_equal(
lexical_cast<bool>('0'), false,
"'0' -> false");
TEST_CHECK_THROW(
lexical_cast<bool>(2001),
bad_lexical_cast,
"2001");
TEST_CHECK_THROW(
lexical_cast<bool>(2),
bad_lexical_cast,
"2");
TEST_CHECK_THROW(
lexical_cast<bool>("true thousand and one"),
bad_lexical_cast,
"\"true thousand and one\"");
}
const test_case test_cases[] =
{
{ "lexical_cast<std::string>", test_to_string },
{ "lexical_cast<int>", test_to_int },
{ "lexical_cast<char>", test_to_char },
{ "lexical_cast<double>", test_to_double },
{ "lexical_cast<bool>", 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.

302
test.hpp Normal file
View File

@@ -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 <exception>
#include <iostream>
#include <strstream> // for out-of-the-box g++
#include <string>
namespace test // test tuple comprises name and nullary function (object)
{
template<typename string_type, typename function_type>
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<typename lhs_type, typename rhs_type>
void check_equal(
const lhs_type & lhs, const rhs_type & rhs,
const std::string & description)
{
check(lhs == rhs, "expected equal values: " + description);
}
template<typename lhs_type, typename rhs_type>
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<typename test_iterator>
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<typename test_iterator>
bool tester<test_iterator>::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.