#define CATCH_CONFIG_RUNNER #include "catch.hpp" #include "variant.hpp" #include <algorithm> #include <cstdint> #include <iterator> #include <limits> #include <memory> #include <ostream> #include <sstream> #include <string> using namespace mapbox; template <typename T> struct mutating_visitor { mutating_visitor(T & val) : val_(val) {} void operator() (T & val) const { val = val_; } template <typename T1> void operator() (T1& ) const {} // no-op T & val_; }; TEST_CASE( "variant version", "[variant]" ) { unsigned int version = VARIANT_VERSION; REQUIRE(version == 100); #if VARIANT_VERSION == 100 REQUIRE(true); #else REQUIRE(false); #endif } TEST_CASE( "variant can be moved into vector", "[variant]" ) { typedef util::variant<bool,std::string> variant_type; variant_type v(std::string("test")); std::vector<variant_type> vec; vec.emplace_back(std::move(v)); REQUIRE(v.get<std::string>() != std::string("test")); REQUIRE(vec.at(0).get<std::string>() == std::string("test")); } TEST_CASE( "variant should support built-in types", "[variant]" ) { SECTION( "bool" ) { util::variant<bool> v(true); REQUIRE(v.valid()); REQUIRE(v.is<bool>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<bool>() == true); v.set<bool>(false); REQUIRE(v.get<bool>() == false); v = true; REQUIRE(v == util::variant<bool>(true)); } SECTION( "nullptr" ) { typedef std::nullptr_t value_type; util::variant<value_type> v(nullptr); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); // TODO: commented since it breaks on windows: 'operator << is ambiguous' //REQUIRE(v.get<value_type>() == nullptr); // FIXME: does not compile: ./variant.hpp:340:14: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::__1::basic_ostream<char>' and 'const nullptr_t') // https://github.com/mapbox/variant/issues/14 //REQUIRE(v == util::variant<value_type>(nullptr)); } SECTION( "unique_ptr" ) { typedef std::unique_ptr<std::string> value_type; util::variant<value_type> v(value_type(new std::string("hello"))); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(*v.get<value_type>().get() == *value_type(new std::string("hello")).get()); } SECTION( "string" ) { typedef std::string value_type; util::variant<value_type> v(value_type("hello")); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == value_type("hello")); v.set<value_type>(value_type("there")); REQUIRE(v.get<value_type>() == value_type("there")); v = value_type("variant"); REQUIRE(v == util::variant<value_type>(value_type("variant"))); } SECTION( "size_t" ) { typedef std::size_t value_type; util::variant<value_type> v(std::numeric_limits<value_type>::max()); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max()); v.set<value_type>(value_type(0)); REQUIRE(v.get<value_type>() == value_type(0)); v = value_type(1); REQUIRE(v == util::variant<value_type>(value_type(1))); } SECTION( "int8_t" ) { typedef std::int8_t value_type; util::variant<value_type> v(std::numeric_limits<value_type>::max()); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max()); v.set<value_type>(0); REQUIRE(v.get<value_type>() == value_type(0)); v = value_type(1); REQUIRE(v == util::variant<value_type>(value_type(1))); } SECTION( "int16_t" ) { typedef std::int16_t value_type; util::variant<value_type> v(std::numeric_limits<value_type>::max()); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max()); v.set<value_type>(0); REQUIRE(v.get<value_type>() == value_type(0)); v = value_type(1); REQUIRE(v == util::variant<value_type>(value_type(1))); } SECTION( "int32_t" ) { typedef std::int32_t value_type; util::variant<value_type> v(std::numeric_limits<value_type>::max()); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max()); v.set<value_type>(0); REQUIRE(v.get<value_type>() == value_type(0)); v = value_type(1); REQUIRE(v == util::variant<value_type>(value_type(1))); } SECTION( "int64_t" ) { typedef std::int64_t value_type; util::variant<value_type> v(std::numeric_limits<value_type>::max()); REQUIRE(v.valid()); REQUIRE(v.is<value_type>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max()); v.set<value_type>(0); REQUIRE(v.get<value_type>() == value_type(0)); v = value_type(1); REQUIRE(v == util::variant<value_type>(value_type(1))); } } struct MissionInteger { typedef uint64_t value_type; 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_; } }; // TODO - remove after https://github.com/mapbox/variant/issues/14 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 util::variant<MissionInteger> v(MissionInteger(34838300)); REQUIRE(v.valid()); REQUIRE(v.is<MissionInteger>()); REQUIRE(v.get_type_index() == 0); REQUIRE(v.get<MissionInteger>() == MissionInteger(34838300)); REQUIRE(v.get<MissionInteger>().get() == MissionInteger::value_type(34838300)); // TODO: should both of the set usages below compile? v.set<MissionInteger>(MissionInteger::value_type(0)); v.set<MissionInteger>(MissionInteger(0)); REQUIRE(v.get<MissionInteger>().get() == MissionInteger::value_type(0)); v = MissionInteger(1); REQUIRE(v == util::variant<MissionInteger>(MissionInteger(1))); } // Test internal api TEST_CASE( "variant should correctly index types", "[variant]" ) { typedef util::variant<bool,std::string,std::uint64_t,std::int64_t,double,float> variant_type; // Index is in reverse order REQUIRE(variant_type(true).get_type_index() == 5); REQUIRE(variant_type(std::string("test")).get_type_index() == 4); REQUIRE(variant_type(std::uint64_t(0)).get_type_index() == 3); REQUIRE(variant_type(std::int64_t(0)).get_type_index() == 2); REQUIRE(variant_type(double(0.0)).get_type_index() == 1); REQUIRE(variant_type(float(0.0)).get_type_index() == 0); } TEST_CASE( "get with type not in variant type list should throw", "[variant]" ) { typedef util::variant<int> variant_type; variant_type var = 5; REQUIRE(var.get<int>() == 5); REQUIRE_THROWS(var.get<double>()); // XXX shouldn't this be a compile time error? See https://github.com/mapbox/variant/issues/24 } TEST_CASE( "get with wrong type (here: double) should throw", "[variant]" ) { typedef util::variant<int, double> variant_type; variant_type var = 5; REQUIRE(var.get<int>() == 5); REQUIRE_THROWS(var.get<double>()); } TEST_CASE( "get with wrong type (here: int) should throw", "[variant]" ) { typedef util::variant<int, double> variant_type; variant_type var = 5.0; REQUIRE(var.get<double>() == 5.0); REQUIRE_THROWS(var.get<int>()); } TEST_CASE( "implicit conversion", "[variant][implicit conversion]" ) { typedef util::variant<int> variant_type; variant_type var(5.0); // converted to int REQUIRE(var.get<int>() == 5); REQUIRE_THROWS(var.get<double>()); var = 6.0; // works for operator=, too REQUIRE(var.get<int>() == 6); } TEST_CASE( "implicit conversion to first type in variant type list", "[variant][implicit conversion]" ) { typedef util::variant<long, char> variant_type; variant_type var = 5.0; // converted to long REQUIRE(var.get<long>() == 5); REQUIRE_THROWS(var.get<char>()); REQUIRE_THROWS(var.get<double>()); } TEST_CASE( "implicit conversion to unsigned char", "[variant][implicit conversion]" ) { typedef util::variant<unsigned char> variant_type; variant_type var = 100.0; CHECK(var.get<unsigned char>() == static_cast<unsigned char>(100.0)); CHECK(var.get<unsigned char>() == static_cast<unsigned char>(static_cast<unsigned int>(100.0))); } struct dummy {}; TEST_CASE( "variant value traits", "[variant::detail]" ) { // Users should not create variants with duplicated types // however our type indexing should still work // Index is in reverse order REQUIRE((util::detail::value_traits<bool, bool, int, double, std::string>::index == 3)); REQUIRE((util::detail::value_traits<int, bool, int, double, std::string>::index == 2)); REQUIRE((util::detail::value_traits<double, bool, int, double, std::string>::index == 1)); REQUIRE((util::detail::value_traits<std::string, bool, int, double, std::string>::index == 0)); REQUIRE((util::detail::value_traits<dummy, bool, int, double, std::string>::index == util::detail::invalid_value)); REQUIRE((util::detail::value_traits<std::vector<int>, bool, int, double, std::string>::index == 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 REQUIRE((util::variant<int, double, std::string>().get_type_index() == 2)); REQUIRE((util::variant<int, double, std::string>(util::no_init()).get_type_index() == util::detail::invalid_value)); } TEST_CASE( "variant visitation", "[visitor][unary visitor]" ) { util::variant<int, double, std::string> var(123); REQUIRE(var.get<int>() == 123); int val = 456; mutating_visitor<int> visitor(val); util::apply_visitor(visitor, var); REQUIRE(var.get<int>() == 456); } TEST_CASE( "variant printer", "[visitor][unary visitor][printer]" ) { typedef util::variant<int, double, std::string> variant_type; std::vector<variant_type> var = {2.1, 123, "foo", 456}; std::stringstream out; std::copy(var.begin(), var.end(), std::ostream_iterator<variant_type>(out, ",")); out << var[2]; REQUIRE(out.str() == "2.1,123,foo,456,foo"); } int main (int argc, char* const argv[]) { int result = Catch::Session().run(argc, argv); if (!result) printf("\x1b[1;32m ✓ \x1b[0m\n"); return result; }