Boost中DataStruct模組的any
阿新 • • 發佈:2018-12-03
標頭檔案
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>(©), "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>(©), "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(), 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));
}
}