1. 程式人生 > >Boost中DataStruct模組的any

Boost中DataStruct模組的any

標頭檔案

boost/any.hpp

作用

將基本資料型別,自定義類型別,指標型別,轉化為any,同時通過any_cast函式,將any型別,轉為需要的型別,如果轉化失敗,則會丟擲bad_any_cast異常。

any類,有如下API

swap:交換,與std::swap一致。

empty:判斷any是否為空,如果構造一個無慘的any,則為 true

clear:將 有引數型別的any,清空,清空之後,empty為true

type:獲取引數的資料型別。

全域性函式

swap:同以上swap

any_cast:將any轉化為指定的型別,如果失敗,則丟擲bad_any_cast異常。

舉例

test.hpp

namespace any_tests // 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 any_tests // failure exception used to indicate checked test failures
{
    class failure : public std::exception
    {
    public: // struction (default cases are OK)

        failure(const std::string & why) throw()
          : reason(why)
        {
        }

              ~failure() throw() {}

    public: // usage

        virtual const char * what() const throw()
        {
            return reason.c_str();
        }

    private: // representation

        std::string reason;

    };
}

namespace any_tests // 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 any_tests // 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 ::any_tests::failure(description); \
    } \
    catch(exception &) \
    { \
    }

namespace any_tests // 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 any_tests // 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 &);

    };
    
#if defined(__GNUC__) && defined(__SGI_STL_PORT) && (__GNUC__ < 3)
    // function scope using declarations don't work:
    using namespace std;
#endif

    template<typename test_iterator>
    bool tester<test_iterator>::operator()()
    {
        using std::cerr;
        using std::endl;
        using std::ends;
        using std::exception;
        using std::flush;
        using std::string;

        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();
#ifdef BOOST_NO_STRINGSTREAM
                    std::ostrstream report;
#else
                    std::ostringstream report;
#endif
                    report << "new/delete ("
                           << allocated << " allocated, "
                           << deallocated << " deallocated)"
                           << ends;
                    const string text = report.str();
#ifdef BOOST_NO_STRINGSTREAM
                    report.freeze(false);
#endif
                    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;
    }
}

any_test.cpp

#include <cstdlib>
#include <string>
#include <vector>
#include <utility>

#include <boost/any.hpp>
#include "test.hpp"

namespace any_tests
{
    typedef test<const char *, void (*)()> test_case;
    typedef const test_case * test_case_iterator;

    extern const test_case_iterator begin, end;
}

int main()
{
    using namespace any_tests;
    tester<test_case_iterator> 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();
    void test_cast_to_reference();
    void test_with_array();
    void test_with_func();
    void test_clear();
    void test_vectors();
    void test_addressof();

    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      },
        { "cast to reference types",        test_cast_to_reference },
        { "storing an array inside",        test_with_array        },
        { "implicit cast of returned value",test_with_func         },
        { "clear() methods",                test_clear             },
        { "testing with vectors",           test_vectors           },
        { "class with operator&()",         test_addressof         }
    };

    const test_case_iterator begin = test_cases;
    const test_case_iterator end =
        test_cases + (sizeof test_cases / sizeof *test_cases);

    

    struct copy_counter
    {

    public:

        copy_counter() {}
        copy_counter(const copy_counter&) { ++count; }
        copy_counter& operator=(const copy_counter&) { ++count; return *this; }
        static int get_count() { return count; }

    private:

        static int count;

    };

    int copy_counter::count = 0;
}

namespace any_tests // test definitions
{
    using namespace boost;

    void test_default_ctor()
    {
        const any value;

        check_true(value.empty(), "empty");
        check_null(any_cast<int>(&value), "any_cast<int>");
        check_equal(value.type(), boost::typeindex::type_id<void>(), "type");
    }

    void test_converting_ctor()
    {
        std::string text = "test message";
        any value = text;

        check_false(value.empty(), "empty");
        check_equal(value.type(), boost::typeindex::type_id<std::string>(), "type");
        check_null(any_cast<int>(&value), "any_cast<int>");
        check_non_null(any_cast<std::string>(&value), "any_cast<std::string>");
        check_equal(
            any_cast<std::string>(value), text,
            "comparing cast copy against original text");
        check_unequal(
            any_cast<std::string>(&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(boost::typeindex::type_index(original.type()), copy.type(), "type");
        check_equal(
            any_cast<std::string>(original), any_cast<std::string>(copy),
            "comparing cast copy against original");
        check_equal(
            text, any_cast<std::string>(copy),
            "comparing cast copy against original text");
        check_unequal(
            any_cast<std::string>(&original),
            any_cast<std::string>(&copy),
            "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(boost::typeindex::type_index(original.type()), copy.type(), "type");
        check_equal(
            any_cast<std::string>(original), any_cast<std::string>(copy),
            "comparing cast copy against cast original");
        check_equal(
            text, any_cast<std::string>(copy),
            "comparing cast copy against original text");
        check_unequal(
            any_cast<std::string>(&original),
            any_cast<std::string>(&copy),
            "comparing address in copy against original");
        check_equal(assign_result, &copy, "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(), boost::typeindex::type_id<std::string>(), "type");
        check_null(any_cast<int>(&value), "any_cast<int>");
        check_non_null(any_cast<std::string>(&value), "any_cast<std::string>");
        check_equal(
            any_cast<std::string>(value), text,
            "comparing cast copy against original text");
        check_unequal(
            any_cast<std::string>(&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<const char *>(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<std::string>(&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(), boost::typeindex::type_id<std::string>(), "type");
        check_equal(
            text, any_cast<std::string>(swapped),
            "comparing swapped copy against original text");
        check_non_null(original_ptr, "address in pre-swapped original");
        check_equal(
            original_ptr,
            any_cast<std::string>(&swapped),
            "comparing address in swapped against original");
        check_equal(swap_result, &original, "address of swap result");

        any copy1 = copy_counter();
        any copy2 = copy_counter();
        int count = copy_counter::get_count();
        swap(copy1, copy2);
        check_equal(count, copy_counter::get_count(), "checking that free swap doesn't make any copies.");
    }

    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");
    }

    void test_cast_to_reference()
    {
        any a(137);
        const any b(a);

        int &                ra    = any_cast<int &>(a);
        int const &          ra_c  = any_cast<int const &>(a);
        int volatile &       ra_v  = any_cast<int volatile &>(a);
        int const volatile & ra_cv = any_cast<int const volatile&>(a);

        check_true(
            &ra == &ra_c && &ra == &ra_v && &ra == &ra_cv,
            "cv references to same obj");

        int const &          rb_c  = any_cast<int const &>(b);
        int const volatile & rb_cv = any_cast<int const volatile &>(b);

        check_true(&rb_c == &rb_cv, "cv references to copied const obj");
        check_true(&ra != &rb_c, "copies hold different objects");

        ++ra;
        int incremented = any_cast<int>(a);
        check_true(incremented == 138, "increment by reference changes value");

        TEST_CHECK_THROW(
            any_cast<char &>(a),
            bad_any_cast,
            "any_cast to incorrect reference type");

        TEST_CHECK_THROW(
            any_cast<const char &>(b),
            bad_any_cast,
            "any_cast to incorrect const reference type");
    }

    void test_with_array()
    {
        any value1("Char array");
        any value2;
        value2 = "Char array";

        check_false(value1.empty(), "type");
        check_false(value2.empty(), "type");

        check_equal(value1.type(), boost::typeindex::type_id<const char*>(), "type");
        check_equal(value2.type(), boost::typeindex::type_id<const char*>(), "type");
        
        check_non_null(any_cast<const char*>(&value1), "any_cast<const char*>");
        check_non_null(any_cast<const char*>(&value2), "any_cast<const char*>");
    }

    const std::string& returning_string1() 
    {
        static const std::string ret("foo"); 
        return ret;
    }

    std::string returning_string2() 
    {
        static const std::string ret("foo"); 
        return ret;
    }

    void test_with_func()
    {
        std::string s;
        s = any_cast<std::string>(returning_string1());
        s = any_cast<const std::string&>(returning_string1());

        s = any_cast<std::string>(returning_string2());
        s = any_cast<const std::string&>(returning_string2());

#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 
#if !defined(__INTEL_COMPILER) && !defined(__ICL) && (!defined(_MSC_VER) || _MSC_VER != 1600)
        // Intel compiler thinks that it must choose the `any_cast(const any&)` function 
        // instead of the `any_cast(const any&&)`.
        // Bug was not reported because of missing premier support account + annoying 
        // registrations requirements.

        // MSVC-10 had a bug:
        //
        // any.hpp(291) : error C2440: 'return' : cannot convert.
        // Conversion loses qualifiers
        // any_test.cpp(304) : see reference to function template instantiation
        //
        // This issue was fixed in MSVC-11.

        s = any_cast<std::string&&>(returning_string1());
#endif

        s = any_cast<std::string&&>(returning_string2());
#endif
    }

    
    void test_clear()
    {
        std::string text = "test message";
        any value = text;

        check_false(value.empty(), "empty");
        
        value.clear();
        check_true(value.empty(), "non-empty after clear");

        value.clear();
        check_true(value.empty(), "non-empty after second clear");

        value = text;
        check_false(value.empty(), "empty");
        
        value.clear();
        check_true(value.empty(), "non-empty after clear");
    }

    // Following tests cover the case from #9462
    // https://svn.boost.org/trac/boost/ticket/9462
    boost::any makeVec() 
    {
        return std::vector<int>(100 /*size*/, 7 /*value*/);
    }

    void test_vectors() 
    {
        const std::vector<int>& vec = boost::any_cast<std::vector<int> >(makeVec()); 
        check_equal(vec.size(), 100u, "size of vector extracted from boost::any"); 
        check_equal(vec.back(), 7, "back value of vector extracted from boost::any");
        check_equal(vec.front(), 7, "front value of vector extracted from boost::any");

        std::vector<int> vec1 = boost::any_cast<std::vector<int> >(makeVec()); 
        check_equal(vec1.size(), 100u, "size of second vector extracted from boost::any"); 
        check_equal(vec1.back(), 7, "back value of second vector extracted from boost::any");
        check_equal(vec1.front(), 7, "front value of second vector extracted from boost::any");

    }

    template<typename T>
    class class_with_address_op {
    public:
        class_with_address_op(const T* p)
            : ptr(p)
        {}

	    const T** operator &() {
            return &ptr;
        }

        const T* get() const {
            return ptr;
        }

    private:
        const T* ptr;
    };

    void test_addressof()
    {
        int val = 10;
        const int* ptr = &val;
        class_with_address_op<int> obj(ptr);
        boost::any test_val(obj);

        class_with_address_op<int> returned_obj = boost::any_cast<class_with_address_op<int> >(test_val);
        check_equal(&val, returned_obj.get(), "any_cast incorrectly works with type that has operator&(): addresses differ");

        check_true(!!boost::any_cast<class_with_address_op<int> >(&test_val), "any_cast incorrectly works with type that has operator&()");
        check_equal(boost::unsafe_any_cast<class_with_address_op<int> >(&test_val)->get(), ptr, "unsafe_any_cast incorrectly works with type that has operator&()");
    }

}

原始碼

namespace boost
{
    class any
    {
    public: // structors

        any() BOOST_NOEXCEPT
          : content(0)
        {
        }

        template<typename ValueType>
        any(const ValueType & value)
          : content(new holder<
                BOOST_DEDUCED_TYPENAME remove_cv<BOOST_DEDUCED_TYPENAME decay<const ValueType>::type>::type
            >(value))
        {
        }

        any(const any & other)
          : content(other.content ? other.content->clone() : 0)
        {
        }

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
        // Move constructor
        any(any&& other) BOOST_NOEXCEPT
          : content(other.content)
        {
            other.content = 0;
        }

        // Perfect forwarding of ValueType
        template<typename ValueType>
        any(ValueType&& value
            , typename boost::disable_if<boost::is_same<any&, ValueType> >::type* = 0 // disable if value has type `any&`
            , typename boost::disable_if<boost::is_const<ValueType> >::type* = 0) // disable if value has type `const ValueType&&`
          : content(new holder< typename decay<ValueType>::type >(static_cast<ValueType&&>(value)))
        {
        }
#endif

        ~any() BOOST_NOEXCEPT
        {
            delete content;
        }

    public: // modifiers

        any & swap(any & rhs) BOOST_NOEXCEPT
        {
            std::swap(content, rhs.content);
            return *this;
        }


#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
        template<typename ValueType>
        any & operator=(const ValueType & rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

        any & operator=(any rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

#else 
        any & operator=(const any& rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

        // move assignement
        any & operator=(any&& rhs) BOOST_NOEXCEPT
        {
            rhs.swap(*this);
            any().swap(rhs);
            return *this;
        }

        // Perfect forwarding of ValueType
        template <class ValueType>
        any & operator=(ValueType&& rhs)
        {
            any(static_cast<ValueType&&>(rhs)).swap(*this);
            return *this;
        }
#endif

    public: // queries

        bool empty() const BOOST_NOEXCEPT
        {
            return !content;
        }

        void clear() BOOST_NOEXCEPT
        {
            any().swap(*this);
        }

        const boost::typeindex::type_info& type() const BOOST_NOEXCEPT
        {
            return content ? content->type() : boost::typeindex::type_id<void>().type_info();
        }

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
    private: // types
#else
    public: // types (public so any_cast can be non-friend)
#endif

        class placeholder
        {
        public: // structors

            virtual ~placeholder()
            {
            }

        public: // queries

            virtual const boost::typeindex::type_info& type() const BOOST_NOEXCEPT = 0;

            virtual placeholder * clone() const = 0;

        };

        template<typename ValueType>
        class holder : public placeholder
        {
        public: // structors

            holder(const ValueType & value)
              : held(value)
            {
            }

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
            holder(ValueType&& value)
              : held(static_cast< ValueType&& >(value))
            {
            }
#endif
        public: // queries

            virtual const boost::typeindex::type_info& type() const BOOST_NOEXCEPT
            {
                return boost::typeindex::type_id<ValueType>().type_info();
            }

            virtual placeholder * clone() const
            {
                return new holder(held);
            }

        public: // representation

            ValueType held;

        private: // intentionally left unimplemented
            holder & operator=(const holder &);
        };

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

    private: // representation

        template<typename ValueType>
        friend ValueType * any_cast(any *) BOOST_NOEXCEPT;

        template<typename ValueType>
        friend ValueType * unsafe_any_cast(any *) BOOST_NOEXCEPT;

#else

    public: // representation (public so any_cast can be non-friend)

#endif

        placeholder * content;

    };
 
    inline void swap(any & lhs, any & rhs) BOOST_NOEXCEPT
    {
        lhs.swap(rhs);
    }

    class BOOST_SYMBOL_VISIBLE bad_any_cast :
#ifndef BOOST_NO_RTTI
        public std::bad_cast
#else
        public std::exception
#endif
    {
    public:
        virtual const char * what() const BOOST_NOEXCEPT_OR_NOTHROW
        {
            return "boost::bad_any_cast: "
                   "failed conversion using boost::any_cast";
        }
    };

    template<typename ValueType>
    ValueType * any_cast(any * operand) BOOST_NOEXCEPT
    {
        return operand && operand->type() == boost::typeindex::type_id<ValueType>()
            ? boost::addressof(
                static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
              )
            : 0;
    }

    template<typename ValueType>
    inline const ValueType * any_cast(const any * operand) BOOST_NOEXCEPT
    {
        return any_cast<ValueType>(const_cast<any *>(operand));
    }

    template<typename ValueType>
    ValueType any_cast(any & operand)
    {
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;


        nonref * result = any_cast<nonref>(boost::addressof(operand));
        if(!result)
            boost::throw_exception(bad_any_cast());

        // Attempt to avoid construction of a temporary object in cases when 
        // `ValueType` is not a reference. Example:
        // `static_cast<std::string>(*result);` 
        // which is equal to `std::string(*result);`
        typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<
            boost::is_reference<ValueType>,
            ValueType,
            BOOST_DEDUCED_TYPENAME boost::add_reference<ValueType>::type
        >::type ref_type;

#ifdef BOOST_MSVC
#   pragma warning(push)
#   pragma warning(disable: 4172) // "returning address of local variable or temporary" but *result is not local!
#endif
        return static_cast<ref_type>(*result);
#ifdef BOOST_MSVC
#   pragma warning(pop)
#endif
    }

    template<typename ValueType>
    inline ValueType any_cast(const any & operand)
    {
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;
        return any_cast<const nonref &>(const_cast<any &>(operand));
    }

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
    template<typename ValueType>
    inline ValueType any_cast(any&& operand)
    {
        BOOST_STATIC_ASSERT_MSG(
            boost::is_rvalue_reference<ValueType&&>::value /*true if ValueType is rvalue or just a value*/
            || boost::is_const< typename boost::remove_reference<ValueType>::type >::value,
            "boost::any_cast shall not be used for getting nonconst references to temporary objects" 
        );
        return any_cast<ValueType>(operand);
    }
#endif


    // Note: The "unsafe" versions of any_cast are not part of the
    // public interface and may be removed at any time. They are
    // required where we know what type is stored in the any and can't
    // use typeid() comparison, e.g., when our types may travel across
    // different shared libraries.
    template<typename ValueType>
    inline ValueType * unsafe_any_cast(any * operand) BOOST_NOEXCEPT
    {
        return boost::addressof(
            static_cast<any::holder<ValueType> *>(operand->content)->held
        );
    }

    template<typename ValueType>
    inline const ValueType * unsafe_any_cast(const any * operand) BOOST_NOEXCEPT
    {
        return unsafe_any_cast<ValueType>(const_cast<any *>(operand));
    }
}