406 lines
13 KiB
C++
406 lines
13 KiB
C++
// what: unit tests for variant type boost::any
|
|
// who: contributed by Kevlin Henney
|
|
// when: July 2001, 2013, 2014
|
|
// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95
|
|
|
|
#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&()");
|
|
}
|
|
|
|
}
|
|
|
|
// Copyright Kevlin Henney, 2000, 2001. All rights reserved.
|
|
// Copyright Antony Polukhin, 2013-2021.
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See
|
|
// accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|