From d3609ba51f52ccfa1549968f1b3334b65747401e Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Tue, 3 Jul 2001 14:07:01 +0000 Subject: [PATCH] Initial commit [SVN r10512] --- .gitattributes | 96 +++++++++++ any_test.cpp | 199 +++++++++++++++++++++++ index.html | 431 +++++++++++++++++++++++++++++++++++++++++++++++++ test.hpp | 302 ++++++++++++++++++++++++++++++++++ 4 files changed, 1028 insertions(+) create mode 100644 .gitattributes create mode 100644 any_test.cpp create mode 100644 index.html create mode 100644 test.hpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3e84d7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/any_test.cpp b/any_test.cpp new file mode 100644 index 0000000..f72147b --- /dev/null +++ b/any_test.cpp @@ -0,0 +1,199 @@ +// what: unit tests for variant type boost::any +// who: contributed by Kevlin Henney +// when: July 2001 +// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 + +#include +#include +#include + +#include "boost/any.hpp" +#include "test.hpp" + +namespace any_tests +{ + typedef test::test test_case; + typedef const test_case * test_case_iterator; + + extern const test_case_iterator begin, end; +} + +int main() +{ + using namespace any_tests; + test::tester test_suite(begin, end); + return test_suite() ? EXIT_SUCCESS : EXIT_FAILURE; +} + +namespace any_tests // test suite +{ + void test_default_ctor(); + void test_converting_ctor(); + void test_copy_ctor(); + void test_copy_assign(); + void test_converting_assign(); + void test_bad_cast(); + void test_swap(); + void test_null_copying(); + + const test_case test_cases[] = + { + { "default construction", test_default_ctor }, + { "single argument construction", test_converting_ctor }, + { "copy construction", test_copy_ctor }, + { "copy assignment operator", test_copy_assign }, + { "converting assignment operator", test_converting_assign }, + { "failed custom keyword cast", test_bad_cast }, + { "swap member function", test_swap }, + { "copying operations on a null", test_null_copying } + }; + + const test_case_iterator begin = test_cases; + const test_case_iterator end = + test_cases + (sizeof test_cases / sizeof *test_cases); +} + +namespace any_tests // test definitions +{ + using namespace test; + using namespace boost; + + void test_default_ctor() + { + const any value; + + check_true(value.empty(), "empty"); + check_null(any_cast(&value), "any_cast"); + check_equal(value.type(), typeid(void), "type"); + } + + void test_converting_ctor() + { + std::string text = "test message"; + any value = text; + + check_false(value.empty(), "empty"); + check_equal(value.type(), typeid(std::string), "type"); + check_null(any_cast(&value), "any_cast"); + check_non_null(any_cast(&value), "any_cast"); + check_equal( + any_cast(value), text, + "comparing cast copy against original text"); + check_unequal( + any_cast(&value), &text, + "comparing address in copy against original text"); + } + + void test_copy_ctor() + { + std::string text = "test message"; + any original = text, copy = original; + + check_false(copy.empty(), "empty"); + check_equal(original.type(), copy.type(), "type"); + check_equal( + any_cast(original), any_cast(copy), + "comparing cast copy against original"); + check_equal( + text, any_cast(copy), + "comparing cast copy against original text"); + check_unequal( + any_cast(&original), + any_cast(©), + "comparing address in copy against original"); + } + + void test_copy_assign() + { + std::string text = "test message"; + any original = text, copy; + any * assign_result = &(copy = original); + + check_false(copy.empty(), "empty"); + check_equal(original.type(), copy.type(), "type"); + check_equal( + any_cast(original), any_cast(copy), + "comparing cast copy against cast original"); + check_equal( + text, any_cast(copy), + "comparing cast copy against original text"); + check_unequal( + any_cast(&original), + any_cast(©), + "comparing address in copy against original"); + check_equal(assign_result, ©, "address of assignment result"); + } + + void test_converting_assign() + { + std::string text = "test message"; + any value; + any * assign_result = &(value = text); + + check_false(value.empty(), "type"); + check_equal(value.type(), typeid(std::string), "type"); + check_null(any_cast(&value), "any_cast"); + check_non_null(any_cast(&value), "any_cast"); + check_equal( + any_cast(value), text, + "comparing cast copy against original text"); + check_unequal( + any_cast(&value), + &text, + "comparing address in copy against original text"); + check_equal(assign_result, &value, "address of assignment result"); + } + + void test_bad_cast() + { + std::string text = "test message"; + any value = text; + + TEST_CHECK_THROW( + any_cast(value), + bad_any_cast, + "any_cast to incorrect type"); + } + + void test_swap() + { + std::string text = "test message"; + any original = text, swapped; + std::string * original_ptr = any_cast(&original); + any * swap_result = &original.swap(swapped); + + check_true(original.empty(), "empty on original"); + check_false(swapped.empty(), "empty on swapped"); + check_equal(swapped.type(), typeid(std::string), "type"); + check_equal( + text, any_cast(swapped), + "comparing swapped copy against original text"); + check_non_null(original_ptr, "address in pre-swapped original"); + check_equal( + original_ptr, + any_cast(&swapped), + "comparing address in swapped against original"); + check_equal(swap_result, &original, "address of swap result"); + } + + void test_null_copying() + { + const any null; + any copied = null, assigned; + assigned = null; + + check_true(null.empty(), "empty on null"); + check_true(copied.empty(), "empty on copied"); + check_true(assigned.empty(), "empty on copied"); + } +} + +// Copyright Kevlin Henney, 2000, 2001. 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/index.html b/index.html new file mode 100644 index 0000000..684aaeb --- /dev/null +++ b/index.html @@ -0,0 +1,431 @@ + + + + any + + + + + + +

Header <boost/any.hpp>

+ + + +
+

Motivation

+ +There are times when a generic (in the sense of general as opposed to +template-based programming) type is needed: variables that are truly variable, +accommodating values of many other more specific types rather than C++'s normal strict +and static types. We can distinguish three basic kinds of generic type: + +
    +
  1. + Converting types that can hold one of a number of possible value types, + e.g. int and string, and freely convert between them, + for instance interpreting 5 as "5" or vice-versa. + Such types are common in scripting and other interpreted languages. + + boost::lexical_cast supports such conversion functionality. +
  2. +
  3. + Discriminated types that contain values of different types but do not attempt conversion between + them, i.e. 5 is held strictly as an int and is not implicitly + convertible either to "5" or to 5.0. Their indifference to + interpretation but awareness of type effectively makes them safe, generic containers of single + values, with no scope for surprises from ambiguous conversions. +
  4. +
  5. + Indiscriminate types that can refer to anything but are oblivious to the actual underlying type, + entrusting all forms of access and interpretation to the programmer. This niche is dominated by + void *, which offers plenty of scope for surprising, undefined behavior. +
  6. +
+ +The any class (based on the class of the same name described in +"Valued Conversions" +by Kevlin Henney, C++ Report 12(7), July/August 2000) is a variant value type based on the second +category. It supports copying of any value type and safe checked extraction of that value strictly +against its type. A similar design, offering more appropriate operators, can be used for a generalized +function adaptor, any_function, a generalized iterator adaptor, any_iterator, +and other object types that need uniform runtime treatment but support only compile-time template +parameter conformance. +

+ +


+

Examples

+ +The following code demonstrates the syntax for using implicit conversions to and copying of +any objects: + +
+
+#include <list>
+#include <boost/any.hpp>
+
+typedef std::list<boost::any> many;
+
+void append_int(many & values, int value)
+{
+    boost::any to_append = value;
+    values.push_back(to_append);
+}
+
+void append_string(many & values, const std::string & value)
+{
+    values.push_back(value);
+}
+
+void append_char_ptr(many & values, const char * value)
+{
+    values.push_back(value);
+}
+
+void append_any(many & values, const boost::any & value)
+{
+    values.push_back(value);
+}
+
+void append_nothing(many & values)
+{
+    values.push_back(boost::any());
+}
+
+
+ +The following predicates follow on from the previous definitions and demonstrate +the use of queries on any objects: + +
+
+bool is_empty(const boost::any & operand)
+{
+    return operand.empty();
+}
+
+bool is_int(const boost::any & operand)
+{
+    return operand.type() == typeid(int);
+}
+
+bool is_char_ptr(const boost::any & operand)
+{
+    try
+    {
+        any_ptr<const char *>(operand);
+        return true;
+    }
+    catch(const boost::bad_any_cast &)
+    {
+        return false;
+    }
+}
+
+bool is_string(const boost::any & operand)
+{
+    return any_ptr<std::string>(&operand);
+}
+
+void count_all(many & values, std::ostream & out)
+{
+    out << "#empty == "
+        << std::count_if(values.begin(), values.end(), is_empty) << std::endl;
+    out << "#int == "
+        << std::count_if(values.begin(), values.end(), is_int) << std::endl;
+    out << "#const char * == "
+        << std::count_if(values.begin(), values.end(), is_char_ptr) << std::endl;
+    out << "#string == "
+        << std::count_if(values.begin(), values.end(), is_string) << std::endl;
+}
+
+
+ +The following type, patterned after the OMG's Property Service, defines name–value +pairs for arbitrary value types: + +
+
+struct property
+{
+    property();
+    property(const std::string &, const boost::any &);
+
+    std::string name;
+    boost::any value;
+};
+
+typedef std::list<property> properties;
+
+
+ +The following base class demonstrates one approach to runtime polymorphism based callbacks that also +require arbitrary argument types. The absence of virtual member templates requires that +different solutions have different trade-offs in terms of efficiency, safety, and generality. Using +a checked variant type offers one approach: + +
+
+class consumer
+{
+public:
+    virtual void notify(const any &) = 0;
+    ...
+};
+
+
+ +
+

Synopsis

+ +Dependencies and library features defined in "boost/any.hpp": + +
+
+#include <typeinfo>
+
+namespace boost
+{
+    class any;
+    class bad_any_cast;
+    template<typename ValueType>
+      ValueType any_cast(const any &);
+}
+
+
+ +Test harness defined in "any_test.cpp". +

+ +


+

ValueType requirements

+ +Values are strongly informational objects for which identity is not significant, +i.e. the focus is principally on their state content and any behavior organized around that. +Another distinguishing feature of values is their granularity: normally fine-grained +objects representing simple concepts in the system such as quantities. +

+As the emphasis of a value lies in its state not its identity, values can be copied and +typically assigned one to another, requiring the explicit or implicit definition of a +public copy constructor and public assignment operator. Values typically live within +other scopes, i.e. within objects or blocks, rather than on the heap. Values are therefore +normally passed around and manipulated directly as variables or through references, but not +as pointers that emphasize identity and indirection. +

+The specific requirements on value types to be used in an any are: +

    +
  • + A ValueType is CopyConstructible [20.1.3]. +
  • +
  • + A ValueType is optionally Assignable [23.1]. The strong exception-safety + guarantee is required for all forms of assignment. +
  • +
  • + The destructor for a ValueType upholds the no-throw exception-safety guarantee. +
  • +
+

+ +


+

any

+ +
+
+class any
+{
+public: // structors
+
+    any();
+    any(const any &);
+    template<typename ValueType>
+      any(const ValueType &);
+    ~any();
+
+public: // modifiers
+
+    any & swap(any &);
+    any & operator=(const any &);
+    template<typename ValueType>
+      any & operator=(const ValueType &);
+
+public: // queries
+
+    bool empty() const;
+    const std::type_info & type() const;
+
+private: // representation
+    ...
+};
+
+
+ +A class whose instances can hold instances of any type that satisfies +ValueType requirements: effectively an unbounded union type. +Note that any itself satisfies ValueType requirements with +assignment. +

+ +

    +
    +

    Structors

    + +
    +
    +any::any();
    +
    +
    + +Default constructor that sets new instance to empty. + +
    +
    +any::any(const any & other);
    +
    +
    + +Copy constructor that copies content of other into new instance, so that +any content is equivalent in both type and value to the content of other, +or empty if other is empty. May fail with a std::bad_alloc exception +or any exceptions arising from the copy constructor of the contained type. + +
    +
    +template<typename ValueType>
    +  any::any(const ValueType & value);
    +
    +
    + +Templated converting constructor makes a copy of value, so that the initial +content of the new instance is equivalent in both type and value to value. +May fail with a std::bad_alloc exception or any exceptions arising from the +copy constructor of +the contained type. + +
    +
    +any::~any();
    +
    +
    + +Non-throwing destructor that releases any and all resources used in management of instance. +

    + +


    +

    Modifiers

    + +
    +
    +any & swap(any & rhs);
    +
    +
    + +Non-throwing exchange of the contents of *this and rhs. + +
    +
    +any & operator=(const any & rhs);
    +
    +
    + +Copy assignment operator that copies content of rhs into current instance, discarding +previous content, so that the new content is equivalent in both type and value to the content of +rhs, or empty if rhs.empty(). May fail with a std::bad_alloc +exception or any exceptions arising from the copy constructor of the contained type. +Assignment satisfies the strong guarantee of exception safety. + +
    +
    +template<typename ValueType>
    +  any & operator=(const ValueType & rhs);
    +
    +
    + +Templated assignment operator makes a copy of rhs, discarding previous content, +so that the new content of is equivalent in both type and value to rhs. May fail with a +std::bad_alloc exception or any exceptions arising from the copy constructor of +the contained type. Assignment satisfies the strong guarantee of exception safety. +

    + +


    +

    Queries

    + +
    +
    +bool empty() const;
    +
    +
    + +Test for emptiness, returning true if instance is empty, otherwise false. + +
    +
    +const std::type_info & type() const;
    +
    +
    + +Returns the typeid of the contained value if instance is non-empty, otherwise +typeid(void) returned. Useful for querying against types known either at +compile time or only at runtime. +
+ +
+

bad_any_cast

+ +
+
+class bad_any_cast : public std::bad_cast
+{
+public:
+    virtual const char * what() const;
+};
+
+
+ +The exception thrown in the event of a failed any_cast of +an any value. +

+ +


+

any_cast

+ +
+
+template<typename ValueType>
+  ValueType any_cast(const any & operand);
+template<typename ValueType>
+  const ValueType * any_cast(const any * operand);
+template<typename ValueType>
+  ValueType * any_cast(any * operand);
+
+
+ +Custom keyword cast for extracting a value of a given type from an +any. If passed a pointer, it returns a similarly qualified +pointer to the value content if successful, otherwise null is returned. If passed a value or +reference, it returns a copy of the value content if successful, otherwise a +bad_any_cast exception is thrown. +

+ +


+

Portability

+ +To date the code and test harness have been compiled and tested successfully using Borland C++ 5.5, +Microsoft Visual C++ 6.0, and GNU g++ 2.95. +

+ +


+ +
© Copyright Kevlin Henney, 2001
+ + + 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.