forked from boostorg/conversion
This commit was manufactured by cvs2svn to create branch
'unlabeled-1.1.2'. [SVN r8787]
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
<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 <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. 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
|
||||
<b>Y</b> diagram hierarchy.</p>
|
||||
<p>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.</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. <b> </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. 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. 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.<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. [See ISO Std 3.2]</p>
|
||||
<p>The C++ built-in <b>dynamic_cast</b> 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.</p>
|
||||
<h3>polymorphic_cast and polymorphic_downcast synopsis</h3>
|
||||
<blockquote>
|
||||
<pre>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)
|
||||
|
||||
}</pre>
|
||||
</blockquote>
|
||||
<h3>polymorphic_downcast example</h3>
|
||||
<blockquote>
|
||||
<pre>#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);
|
||||
...</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<>::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<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)
|
||||
|
||||
}</pre>
|
||||
</blockquote>
|
||||
<h3>numeric_cast example</h3>
|
||||
<blockquote>
|
||||
<pre>#include <boost/cast.hpp>
|
||||
using namespace boost::cast;
|
||||
|
||||
void ariane(double vx)
|
||||
{
|
||||
...
|
||||
unsigned short dx = numeric_cast<unsigned short>(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 "The C++
|
||||
Programming Language".<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 <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan
|
||||
-->06 January, 2001<!--webbot bot="Timestamp" endspan i-checksum="38320"
|
||||
--></p>
|
||||
<p>© 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.</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
// 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
|
||||
// 20 Jan 01 removed use of <limits> for portability to raw GCC (David Abrahams)
|
||||
// 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 <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 = LONG_MAX;
|
||||
long large_negative_value = 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>( DBL_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
|
||||
@@ -1,342 +0,0 @@
|
||||
// boost cast.hpp header file ----------------------------------------------//
|
||||
|
||||
// (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
|
||||
// 21 Jan 01 Undid a bug I introduced yesterday. numeric_cast<> never
|
||||
// worked with stock GCC; trying to get it to do that broke
|
||||
// vc-stlport.
|
||||
// 20 Jan 01 Moved BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS to config.hpp.
|
||||
// Removed unused BOOST_EXPLICIT_TARGET macro. Moved
|
||||
// boost::detail::type to boost/type.hpp. Made it compile with
|
||||
// stock gcc again (Dave Abrahams)
|
||||
// 29 Nov 00 Remove nested namespace cast, cleanup spacing before Formal
|
||||
// Review (Beman Dawes)
|
||||
// 19 Oct 00 Fix numeric_cast for floating-point types (Dave Abrahams)
|
||||
// 15 Jul 00 Suppress numeric_cast warnings for GCC, Borland and MSVC
|
||||
// (Dave Abrahams)
|
||||
// 30 Jun 00 More MSVC6 wordarounds. See comments below. (Dave Abrahams)
|
||||
// 28 Jun 00 Removed implicit_cast<>. See comment below. (Beman Dawes)
|
||||
// 27 Jun 00 More MSVC6 workarounds
|
||||
// 15 Jun 00 Add workarounds for MSVC6
|
||||
// 2 Feb 00 Remove bad_numeric_cast ";" syntax error (Doncho Angelov)
|
||||
// 26 Jan 00 Add missing throw() to bad_numeric_cast::what(0 (Adam Levar)
|
||||
// 29 Dec 99 Change using declarations so usages in other namespaces work
|
||||
// correctly (Dave Abrahams)
|
||||
// 23 Sep 99 Change polymorphic_downcast assert to also detect M.I. errors
|
||||
// as suggested Darin Adler and improved by Valentin Bonnard.
|
||||
// 2 Sep 99 Remove controversial asserts, simplify, rename.
|
||||
// 30 Aug 99 Move to cast.hpp, replace value_cast with numeric_cast,
|
||||
// place in nested namespace.
|
||||
// 3 Aug 99 Initial version
|
||||
|
||||
#ifndef BOOST_CAST_HPP
|
||||
#define BOOST_CAST_HPP
|
||||
|
||||
# include <boost/config.hpp>
|
||||
# include <cassert>
|
||||
# include <typeinfo>
|
||||
# include <boost/type.hpp>
|
||||
# ifndef BOOST_NO_LIMITS
|
||||
# include <limits>
|
||||
# endif
|
||||
|
||||
// It has been demonstrated numerous times that MSVC 6.0 fails silently at link
|
||||
// time if you use a template function which has template parameters that don't
|
||||
// appear in the function's argument list.
|
||||
//
|
||||
// TODO: Add this to config.hpp?
|
||||
# if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 // 1200 = VC6
|
||||
# define BOOST_EXPLICIT_DEFAULT_TARGET , ::boost::type<Target>* = 0
|
||||
# else
|
||||
# define BOOST_EXPLICIT_DEFAULT_TARGET
|
||||
# endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
// See the documentation for descriptions of how to choose between
|
||||
// static_cast<>, dynamic_cast<>, polymorphic_cast<> and polymorphic_downcast<>
|
||||
|
||||
// polymorphic_cast --------------------------------------------------------//
|
||||
|
||||
// Runtime checked polymorphic downcasts and crosscasts.
|
||||
// Suggested in The C++ Programming Language, 3rd Ed, Bjarne Stroustrup,
|
||||
// section 15.8 exercise 1, page 425.
|
||||
|
||||
template <class Target, class Source>
|
||||
inline Target polymorphic_cast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
|
||||
{
|
||||
Target tmp = dynamic_cast<Target>(x);
|
||||
if ( tmp == 0 ) throw std::bad_cast();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// polymorphic_downcast ----------------------------------------------------//
|
||||
|
||||
// assert() checked polymorphic downcast. Crosscasts prohibited.
|
||||
|
||||
// WARNING: Because this cast uses assert(), it violates the One Definition
|
||||
// Rule if NDEBUG is inconsistently defined across translation units.
|
||||
|
||||
// Contributed by Dave Abrahams
|
||||
|
||||
template <class Target, class Source>
|
||||
inline Target polymorphic_downcast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
|
||||
{
|
||||
assert( dynamic_cast<Target>(x) == x ); // detect logic error
|
||||
return static_cast<Target>(x);
|
||||
}
|
||||
|
||||
// implicit_cast -----------------------------------------------------------//
|
||||
//
|
||||
// Removed due to uncertain purpose. Use either numeric_cast (see below)
|
||||
// or static_cast according to the need.
|
||||
|
||||
// numeric_cast and related exception --------------------------------------//
|
||||
|
||||
// Contributed by Kevlin Henney
|
||||
|
||||
// bad_numeric_cast --------------------------------------------------------//
|
||||
|
||||
// exception used to indicate runtime numeric_cast failure
|
||||
class bad_numeric_cast : public std::bad_cast
|
||||
{
|
||||
public:
|
||||
// constructors, destructors and assignment operator defaulted
|
||||
|
||||
// function inlined for brevity and consistency with rest of library
|
||||
virtual const char *what() const throw()
|
||||
{
|
||||
return "bad numeric cast: loss of range in numeric_cast";
|
||||
}
|
||||
};
|
||||
|
||||
// numeric_cast ------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <bool is_signed> struct numeric_min_select;
|
||||
|
||||
template<>
|
||||
struct numeric_min_select<true>
|
||||
{
|
||||
template <class T>
|
||||
struct limits : std::numeric_limits<T>
|
||||
{
|
||||
static inline T min()
|
||||
# ifndef __GNUC__ // bug workaround courtesy Jens Maurer
|
||||
{
|
||||
return std::numeric_limits<T>::min() >= 0
|
||||
// unary minus causes integral promotion, thus the static_cast<>
|
||||
? static_cast<T>(-std::numeric_limits<T>::max())
|
||||
: std::numeric_limits<T>::min();
|
||||
}
|
||||
# else
|
||||
;
|
||||
# endif
|
||||
};
|
||||
};
|
||||
|
||||
# ifdef __GNUC__ // bug workaround courtesy Jens Maurer
|
||||
template<> template<class T>
|
||||
inline T numeric_min_select<true>::limits<T>::min()
|
||||
{
|
||||
return std::numeric_limits<T>::min() >= 0
|
||||
// unary minus causes integral promotion, thus the static_cast<>
|
||||
? static_cast<T>(-std::numeric_limits<T>::max())
|
||||
: std::numeric_limits<T>::min();
|
||||
}
|
||||
# endif
|
||||
|
||||
template<>
|
||||
struct numeric_min_select<false>
|
||||
{
|
||||
template <class T>
|
||||
struct limits : std::numeric_limits<T> {};
|
||||
};
|
||||
|
||||
// Move to namespace boost in utility.hpp?
|
||||
template <class T>
|
||||
struct fixed_numeric_limits
|
||||
: public numeric_min_select<
|
||||
std::numeric_limits<T>::is_signed
|
||||
>::template limits<T>
|
||||
{
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// less_than_type_min -
|
||||
// x_is_signed should be numeric_limits<X>::is_signed
|
||||
// y_is_signed should be numeric_limits<Y>::is_signed
|
||||
// y_min should be numeric_limits<Y>::min()
|
||||
//
|
||||
// check(x, y_min) returns true iff x < y_min without invoking comparisons
|
||||
// between signed and unsigned values.
|
||||
//
|
||||
// "poor man's partial specialization" is in use here.
|
||||
template <bool x_is_signed, bool y_is_signed>
|
||||
struct less_than_type_min
|
||||
{
|
||||
template <class X, class Y>
|
||||
static bool check(X x, Y y_min)
|
||||
{ return x < y_min; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct less_than_type_min<false, true>
|
||||
{
|
||||
template <class X, class Y>
|
||||
static bool check(X, Y)
|
||||
{ return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct less_than_type_min<true, false>
|
||||
{
|
||||
template <class X, class Y>
|
||||
static bool check(X x, Y)
|
||||
{ return x < 0; }
|
||||
};
|
||||
|
||||
// greater_than_type_max -
|
||||
// same_sign should be:
|
||||
// numeric_limits<X>::is_signed == numeric_limits<Y>::is_signed
|
||||
// y_max should be numeric_limits<Y>::max()
|
||||
//
|
||||
// check(x, y_max) returns true iff x > y_max without invoking comparisons
|
||||
// between signed and unsigned values.
|
||||
//
|
||||
// "poor man's partial specialization" is in use here.
|
||||
template <bool same_sign, bool x_is_signed>
|
||||
struct greater_than_type_max;
|
||||
|
||||
template<>
|
||||
struct greater_than_type_max<true, true>
|
||||
{
|
||||
template <class X, class Y>
|
||||
static inline bool check(X x, Y y_max)
|
||||
{ return x > y_max; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct greater_than_type_max<false, true>
|
||||
{
|
||||
// What does the standard say about this? I think it's right, and it
|
||||
// will work with every compiler I know of.
|
||||
template <class X, class Y>
|
||||
static inline bool check(X x, Y)
|
||||
{ return x >= 0 && static_cast<X>(static_cast<Y>(x)) != x; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct greater_than_type_max<true, false>
|
||||
{
|
||||
template <class X, class Y>
|
||||
static inline bool check(X x, Y y_max)
|
||||
{ return x > y_max; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct greater_than_type_max<false, false>
|
||||
{
|
||||
// What does the standard say about this? I think it's right, and it
|
||||
// will work with every compiler I know of.
|
||||
template <class X, class Y>
|
||||
static inline bool check(X x, Y)
|
||||
{ return static_cast<X>(static_cast<Y>(x)) != x; }
|
||||
};
|
||||
|
||||
#else // use #pragma hacks if available
|
||||
|
||||
namespace detail
|
||||
{
|
||||
# if BOOST_MSVC
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4018)
|
||||
# pragma warning(disable : 4146)
|
||||
#elif defined(__BORLANDC__)
|
||||
# pragma option push -w-8041
|
||||
# endif
|
||||
|
||||
# ifndef BOOST_NO_LIMITS
|
||||
// Move to namespace boost in utility.hpp?
|
||||
template <class T>
|
||||
struct fixed_numeric_limits : public std::numeric_limits<T>
|
||||
{
|
||||
static inline T min()
|
||||
{
|
||||
return std::numeric_limits<T>::is_signed && std::numeric_limits<T>::min() >= 0
|
||||
? T(-std::numeric_limits<T>::max()) : std::numeric_limits<T>::min();
|
||||
}
|
||||
};
|
||||
# endif // BOOST_NO_LIMITS
|
||||
|
||||
# if BOOST_MSVC
|
||||
# pragma warning(pop)
|
||||
#elif defined(__BORLANDC__)
|
||||
# pragma option pop
|
||||
# endif
|
||||
} // namespace detail
|
||||
|
||||
#endif
|
||||
|
||||
template<typename Target, typename Source>
|
||||
inline Target numeric_cast(Source arg BOOST_EXPLICIT_DEFAULT_TARGET)
|
||||
{
|
||||
#ifndef BOOST_NO_LIMITS
|
||||
// typedefs abbreviating respective trait classes
|
||||
typedef std::numeric_limits<Source> arg_traits;
|
||||
typedef detail::fixed_numeric_limits<Target> result_traits;
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
|
||||
// typedefs that act as compile time assertions
|
||||
// (to be replaced by boost compile time assertions
|
||||
// as and when they become available and are stable)
|
||||
typedef bool argument_must_be_numeric[arg_traits::is_specialized];
|
||||
typedef bool result_must_be_numeric[result_traits::is_specialized];
|
||||
|
||||
const bool arg_is_signed = arg_traits::is_signed;
|
||||
const bool result_is_signed = result_traits::is_signed;
|
||||
const bool same_sign = arg_is_signed == result_is_signed;
|
||||
|
||||
if (less_than_type_min<arg_is_signed, result_is_signed>::check(arg, result_traits::min())
|
||||
|| greater_than_type_max<same_sign, arg_is_signed>::check(arg, result_traits::max())
|
||||
)
|
||||
|
||||
#else // We need to use #pragma hacks if available
|
||||
|
||||
# if BOOST_MSVC
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4018)
|
||||
#elif defined(__BORLANDC__)
|
||||
#pragma option push -w-8012
|
||||
# endif
|
||||
if ((arg < 0 && !result_traits::is_signed) // loss of negative range
|
||||
|| (arg_traits::is_signed && arg < result_traits::min()) // underflow
|
||||
|| arg > result_traits::max()) // overflow
|
||||
# if BOOST_MSVC
|
||||
# pragma warning(pop)
|
||||
#elif defined(__BORLANDC__)
|
||||
#pragma option pop
|
||||
# endif
|
||||
#endif
|
||||
{
|
||||
throw bad_numeric_cast();
|
||||
}
|
||||
return static_cast<Target>(arg);
|
||||
} // numeric_cast
|
||||
|
||||
# undef BOOST_EXPLICIT_DEFAULT_TARGET
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_CAST_HPP
|
||||
@@ -1,67 +0,0 @@
|
||||
// boost lexical_cast.hpp header -------------------------------------------//
|
||||
|
||||
// See http://www.boost.org for most recent version including documentation.
|
||||
|
||||
#ifndef BOOST_LEXICAL_CAST_INCLUDED
|
||||
#define BOOST_LEXICAL_CAST_INCLUDED
|
||||
|
||||
// what: lexical_cast custom keyword cast
|
||||
// who: contributed by Kevlin Henney, with alternative naming, behaviors
|
||||
// and fixes contributed by Dave Abrahams, Daryle Walker and other
|
||||
// Boosters on the list
|
||||
// when: November 2000
|
||||
// where: tested with MSVC 6.0, BCC 5.5, and g++ 2.91
|
||||
|
||||
#include <boost/config.hpp>
|
||||
# ifndef BOOST_NO_STRINGSTREAM
|
||||
# include <sstream>
|
||||
# else
|
||||
# include <strstream>
|
||||
# endif
|
||||
#include <typeinfo>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
// exception used to indicate runtime lexical_cast failure
|
||||
class bad_lexical_cast : public std::bad_cast
|
||||
{
|
||||
public:
|
||||
// constructors, destructors, and assignment operator defaulted
|
||||
|
||||
// function inlined for brevity and consistency with rest of library
|
||||
virtual const char * what() const throw()
|
||||
{
|
||||
return "bad lexical cast: "
|
||||
"source type value could not be interpreted as target";
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Target, typename Source>
|
||||
Target lexical_cast(Source arg)
|
||||
{
|
||||
# ifndef BOOST_NO_STRINGSTREAM
|
||||
std::stringstream interpreter;
|
||||
# else
|
||||
std::strstream interpreter; // for out-of-the-box g++ 2.95.2
|
||||
# endif
|
||||
Target result;
|
||||
|
||||
if(!(interpreter << arg) || !(interpreter >> result) ||
|
||||
!(interpreter >> std::ws).eof())
|
||||
throw bad_lexical_cast();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
<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 Conversion 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. 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<></b>
|
||||
and <b>polymorphic_downcast<></b> to perform safe casting between
|
||||
polymorphic types, and <b> numeric_cast</b><i><></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<></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>
|
||||
@@ -1,149 +0,0 @@
|
||||
// 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.
|
||||
@@ -1,312 +0,0 @@
|
||||
// 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
|
||||
//
|
||||
// ChangeLog:
|
||||
// 20 Jan 2001 - Fixed a warning for MSVC (Dave Abrahams)
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
// std::~string has no exception-specification (could throw anything),
|
||||
// but we need to be compatible with std::~exception's empty one
|
||||
// see std::15.4p13 and std::15.4p3
|
||||
~failure() throw()
|
||||
{
|
||||
}
|
||||
|
||||
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 != 0, "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.
|
||||
Reference in New Issue
Block a user