#include "catch.hpp" #include #include #include #include #include #include #include #include #include #include #include // Hack to make nullptr work with Catch namespace std { template std::basic_ostream& operator<<(std::basic_ostream& os, std::nullptr_t) { return os << (void*)nullptr; } } TEST_CASE("variant can be moved into vector", "[variant]") { using variant_type = mapbox::util::variant; variant_type v(std::string("test")); std::vector vec; vec.emplace_back(std::move(v)); REQUIRE(v.get() != std::string("test")); REQUIRE(vec.at(0).get() == std::string("test")); } TEST_CASE("variant should support built-in types", "[variant]") { SECTION("bool") { mapbox::util::variant v(true); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == true); v.set(false); REQUIRE(v.get() == false); v = true; REQUIRE(v == mapbox::util::variant(true)); } SECTION("nullptr") { using value_type = std::nullptr_t; mapbox::util::variant v(nullptr); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == nullptr); REQUIRE(v == mapbox::util::variant(nullptr)); } SECTION("unique_ptr") { using value_type = std::unique_ptr; mapbox::util::variant v(value_type(new std::string("hello"))); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(*v.get().get() == *value_type(new std::string("hello")).get()); REQUIRE(*v.get() == "hello"); } SECTION("string") { using value_type = std::string; mapbox::util::variant v(value_type("hello")); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == value_type("hello")); v.set(value_type("there")); REQUIRE(v.get() == value_type("there")); v = value_type("variant"); REQUIRE(v == mapbox::util::variant(value_type("variant"))); } SECTION("size_t") { using value_type = std::size_t; mapbox::util::variant v(std::numeric_limits::max()); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == std::numeric_limits::max()); v.set(value_type(0)); REQUIRE(v.get() == value_type(0)); v = value_type(1); REQUIRE(v == mapbox::util::variant(value_type(1))); } SECTION("int8_t") { using value_type = std::int8_t; mapbox::util::variant v(std::numeric_limits::max()); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == std::numeric_limits::max()); v.set(0); REQUIRE(v.get() == value_type(0)); v = value_type(1); REQUIRE(v == mapbox::util::variant(value_type(1))); } SECTION("int16_t") { using value_type = std::int16_t; mapbox::util::variant v(std::numeric_limits::max()); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == std::numeric_limits::max()); v.set(0); REQUIRE(v.get() == value_type(0)); v = value_type(1); REQUIRE(v == mapbox::util::variant(value_type(1))); } SECTION("int32_t") { using value_type = std::int32_t; mapbox::util::variant v(std::numeric_limits::max()); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == std::numeric_limits::max()); v.set(0); REQUIRE(v.get() == value_type(0)); v = value_type(1); REQUIRE(v == mapbox::util::variant(value_type(1))); } SECTION("int64_t") { using value_type = std::int64_t; mapbox::util::variant v(std::numeric_limits::max()); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == std::numeric_limits::max()); v.set(0); REQUIRE(v.get() == value_type(0)); v = value_type(1); REQUIRE(v == mapbox::util::variant(value_type(1))); } } struct MissionInteger { using value_type = uint64_t; value_type val_; public: MissionInteger(uint64_t val) : val_(val) {} bool operator==(MissionInteger const& rhs) const { return (val_ == rhs.get()); } uint64_t get() const { return val_; } }; std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs) { os << rhs.get(); return os; } TEST_CASE("variant should support custom types", "[variant]") { // http://www.missionintegers.com/integer/34838300 mapbox::util::variant v(MissionInteger(34838300)); REQUIRE(v.valid()); REQUIRE(v.is()); REQUIRE(v.which() == 0); REQUIRE(v.get() == MissionInteger(34838300)); REQUIRE(v.get().get() == MissionInteger::value_type(34838300)); // TODO: should both of the set usages below compile? v.set(MissionInteger::value_type(0)); v.set(MissionInteger(0)); REQUIRE(v.get().get() == MissionInteger::value_type(0)); v = MissionInteger(1); REQUIRE(v == mapbox::util::variant(MissionInteger(1))); } TEST_CASE("variant::which() returns zero based index of stored type", "[variant]") { using variant_type = mapbox::util::variant; // which() returns index in forward order REQUIRE(0 == variant_type(true).which()); REQUIRE(1 == variant_type(std::string("test")).which()); REQUIRE(2 == variant_type(std::uint64_t(0)).which()); REQUIRE(3 == variant_type(std::int64_t(0)).which()); REQUIRE(4 == variant_type(double(0.0)).which()); REQUIRE(5 == variant_type(float(0.0)).which()); } TEST_CASE("get with wrong type (here: double) should throw", "[variant]") { using variant_type = mapbox::util::variant; variant_type var = 5; REQUIRE(var.is()); REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5); REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); } TEST_CASE("get with wrong type (here: int) should throw", "[variant]") { using variant_type = mapbox::util::variant; variant_type var = 5.0; REQUIRE(var.is()); REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5.0); REQUIRE(mapbox::util::get(var) == 5.0); REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(var); }, mapbox::util::bad_variant_access&); } TEST_CASE("get on const varint with wrong type (here: int) should throw", "[variant]") { using variant_type = mapbox::util::variant; const variant_type var = 5.0; REQUIRE(var.is()); REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5.0); REQUIRE(mapbox::util::get(var) == 5.0); REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(var); }, mapbox::util::bad_variant_access&); } TEST_CASE("get with any type should throw if not initialized", "[variant]") { mapbox::util::variant var{mapbox::util::no_init()}; REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); } TEST_CASE("no_init variant can be copied and moved from", "[variant]") { using variant_type = mapbox::util::variant; variant_type v1{mapbox::util::no_init()}; variant_type v2{42}; variant_type v3{23}; REQUIRE(v2.get() == 42); v2 = v1; REQUIRE_THROWS_AS({ v2.get(); }, mapbox::util::bad_variant_access&); REQUIRE(v3.get() == 23); v3 = std::move(v1); REQUIRE_THROWS_AS({ v3.get(); }, mapbox::util::bad_variant_access&); } TEST_CASE("no_init variant can be copied and moved to", "[variant]") { using variant_type = mapbox::util::variant; variant_type v1{42}; variant_type v2{mapbox::util::no_init()}; variant_type v3{mapbox::util::no_init()}; REQUIRE_THROWS_AS({ v2.get(); }, mapbox::util::bad_variant_access&); REQUIRE(v1.get() == 42); v2 = v1; REQUIRE(v2.get() == 42); REQUIRE(v1.get() == 42); REQUIRE_THROWS_AS({ v3.get(); }, mapbox::util::bad_variant_access&); v3 = std::move(v1); REQUIRE(v3.get() == 42); } TEST_CASE("implicit conversion", "[variant][implicit conversion]") { using variant_type = mapbox::util::variant; variant_type var(5.0); // converted to int REQUIRE(var.get() == 5); var = 6.0; // works for operator=, too REQUIRE(var.get() == 6); } TEST_CASE("implicit conversion to first type in variant type list", "[variant][implicit conversion]") { using variant_type = mapbox::util::variant; variant_type var = 5l; // converted to long REQUIRE(var.get() == 5); REQUIRE_THROWS_AS({ var.get(); }, mapbox::util::bad_variant_access&); } TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]") { using variant_type = mapbox::util::variant; variant_type var = 100.0; CHECK(var.get() == static_cast(100.0)); CHECK(var.get() == static_cast(static_cast(100.0))); } struct dummy { }; TEST_CASE("implicit conversion to a suitable type", "[variant][implicit conversion]") { using mapbox::util::variant; CHECK_NOTHROW((variant(123)).get()); CHECK_NOTHROW((variant("foo")).get()); } TEST_CASE("value_traits for non-convertible type", "[variant::detail]") { namespace detail = mapbox::util::detail; using target_type = detail::value_traits::target_type; CHECK((std::is_same::value) == true); } TEST_CASE("Type indexing should work with variants with duplicated types", "[variant::detail]") { // Index is in reverse order REQUIRE((mapbox::util::detail::value_traits::index == 3)); REQUIRE((mapbox::util::detail::value_traits::index == 3)); REQUIRE((mapbox::util::detail::value_traits::index == 2)); REQUIRE((mapbox::util::detail::value_traits::index == 2)); REQUIRE((mapbox::util::detail::value_traits::index == 1)); REQUIRE((mapbox::util::detail::value_traits::index == 3)); REQUIRE((mapbox::util::detail::value_traits::index == 0)); REQUIRE((mapbox::util::detail::value_traits::index == mapbox::util::detail::invalid_value)); REQUIRE((mapbox::util::detail::value_traits, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value)); } TEST_CASE("variant default constructor", "[variant][default constructor]") { // By default variant is initialised with (default constructed) first type in template parameters pack // As a result first type in Types... must be default constructable // NOTE: index in reverse order -> index = N - 1 using variant_type = mapbox::util::variant; REQUIRE(variant_type{}.which() == 0); REQUIRE(variant_type{}.valid()); REQUIRE_FALSE(variant_type{mapbox::util::no_init()}.valid()); } TEST_CASE("variant printer", "[visitor][unary visitor][printer]") { using variant_type = mapbox::util::variant; std::vector var = {2.1, 123, "foo", 456}; std::stringstream out; std::copy(var.begin(), var.end(), std::ostream_iterator(out, ",")); out << var[2]; REQUIRE(out.str() == "2.1,123,foo,456,foo"); } TEST_CASE("swapping variants should do the right thing", "[variant]") { using variant_type = mapbox::util::variant; variant_type a = 7; variant_type b = 3; variant_type c = 3.141; variant_type d = "foo"; variant_type e = "a long string that is longer than small string optimization"; using std::swap; swap(a, b); REQUIRE(a.get() == 3); REQUIRE(a.which() == 0); REQUIRE(b.get() == 7); REQUIRE(b.which() == 0); swap(b, c); REQUIRE(b.get() == Approx(3.141)); REQUIRE(b.which() == 1); REQUIRE(c.get() == 7); REQUIRE(c.which() == 0); swap(b, d); REQUIRE(b.get() == "foo"); REQUIRE(b.which() == 2); REQUIRE(d.get() == Approx(3.141)); REQUIRE(d.which() == 1); swap(b, e); REQUIRE(b.get() == "a long string that is longer than small string optimization"); REQUIRE(b.which() == 2); REQUIRE(e.get() == "foo"); REQUIRE(e.which() == 2); } TEST_CASE("variant should work with equality operators") { using variant_type = mapbox::util::variant; variant_type a{1}; variant_type b{1}; variant_type c{2}; variant_type s{"foo"}; REQUIRE(a == a); REQUIRE(a == b); REQUIRE_FALSE(a == c); REQUIRE_FALSE(a == s); REQUIRE_FALSE(c == s); REQUIRE_FALSE(a != a); REQUIRE_FALSE(a != b); REQUIRE(a != c); REQUIRE(a != s); REQUIRE(c != s); } TEST_CASE("variant should work with comparison operators") { using variant_type = mapbox::util::variant; variant_type a{1}; variant_type b{1}; variant_type c{2}; variant_type s{"foo"}; variant_type t{"bar"}; REQUIRE_FALSE(a < a); REQUIRE_FALSE(a < b); REQUIRE(a < c); REQUIRE(a < s); REQUIRE(c < s); REQUIRE(t < s); REQUIRE_FALSE(a > a); REQUIRE_FALSE(a > b); REQUIRE_FALSE(a > c); REQUIRE_FALSE(a > s); REQUIRE_FALSE(c > s); REQUIRE_FALSE(t > s); REQUIRE(a <= a); REQUIRE(a <= b); REQUIRE(a <= c); REQUIRE(a <= s); REQUIRE(c <= s); REQUIRE(t <= s); REQUIRE(a >= a); REQUIRE(a >= b); REQUIRE_FALSE(a >= c); REQUIRE_FALSE(a >= s); REQUIRE_FALSE(c >= s); REQUIRE_FALSE(t >= s); } TEST_CASE("storing reference wrappers works") { using variant_type = mapbox::util::variant, std::reference_wrapper>; int a = 1; variant_type v{std::ref(a)}; REQUIRE(v.get() == 1); REQUIRE(mapbox::util::get(v) == 1); REQUIRE_THROWS_AS({ v.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(v); }, mapbox::util::bad_variant_access&); a = 2; REQUIRE(v.get() == 2); v.get() = 3; REQUIRE(a == 3); double b = 3.141; v = std::ref(b); REQUIRE(v.get() == Approx(3.141)); REQUIRE(mapbox::util::get(v) == Approx(3.141)); REQUIRE_THROWS_AS({ v.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(v); }, mapbox::util::bad_variant_access&); b = 2.718; REQUIRE(v.get() == Approx(2.718)); a = 3; REQUIRE(v.get() == Approx(2.718)); v.get() = 4.1; REQUIRE(b == Approx(4.1)); REQUIRE_THROWS_AS({ v.get() = 4; }, mapbox::util::bad_variant_access&); } TEST_CASE("storing reference wrappers to consts works") { using variant_type = mapbox::util::variant, std::reference_wrapper>; int a = 1; variant_type v{std::cref(a)}; REQUIRE(v.get() == 1); REQUIRE(v.get() == 1); REQUIRE(mapbox::util::get(v) == 1); REQUIRE(mapbox::util::get(v) == 1); REQUIRE_THROWS_AS({ v.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(v); }, mapbox::util::bad_variant_access&); double b = 3.141; v = std::cref(b); REQUIRE(v.get() == Approx(3.141)); REQUIRE(mapbox::util::get(v) == Approx(3.141)); REQUIRE_THROWS_AS({ v.get(); }, mapbox::util::bad_variant_access&); REQUIRE_THROWS_AS({ mapbox::util::get(v); }, mapbox::util::bad_variant_access&); } TEST_CASE("recursive wrapper") { using variant_type = mapbox::util::variant>; variant_type v(1); REQUIRE(v.is()); REQUIRE(v.get() == 1); } TEST_CASE("variant : direct_type helper should match T, references (T&) and const references (T const&) to the original type T)") { using value = mapbox::util::variant; std::uint64_t u(1234); REQUIRE(value(u).is()); // matches T std::uint64_t& ur(u); REQUIRE(value(ur).is()); // matches T& std::uint64_t const& ucr(u); REQUIRE(value(ucr).is()); // matches T const& }