Merge commit '62e8601919faca57a0fa4be1a910458390450cc9' as 'third_party/variant'

This commit is contained in:
Patrick Niklaus
2016-03-24 21:32:27 +01:00
51 changed files with 15816 additions and 0 deletions
+185
View File
@@ -0,0 +1,185 @@
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <boost/timer/timer.hpp>
#include <boost/variant.hpp>
#include "variant.hpp"
#define TEXT_SHORT "Test"
#define TEXT_LONG "Testing various variant implementations with a longish string ........................................."
#define NUM_SAMPLES 3
//#define BOOST_VARIANT_MINIMIZE_SIZE
using namespace mapbox;
namespace test {
template <typename V>
struct Holder
{
typedef V value_type;
std::vector<value_type> data;
template <typename T>
void append_move(T&& obj)
{
data.emplace_back(std::forward<T>(obj));
}
template <typename T>
void append(T const& obj)
{
data.push_back(obj);
}
};
} // namespace test
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
template <typename V>
struct dummy : boost::static_visitor<>
{
dummy(V& v)
: v_(v) {}
template <typename T>
void operator()(T&& val) const
{
v_ = std::move(val);
}
V& v_;
};
template <typename V>
struct dummy2
{
dummy2(V& v)
: v_(v) {}
template <typename T>
void operator()(T&& val) const
{
v_ = std::move(val);
}
V& v_;
};
void run_boost_test(std::size_t runs)
{
test::Holder<boost::variant<int, double, std::string>> h;
h.data.reserve(runs);
for (std::size_t i = 0; i < runs; ++i)
{
h.append_move(std::string(TEXT_SHORT));
h.append_move(std::string(TEXT_LONG));
h.append_move(123);
h.append_move(3.14159);
}
boost::variant<int, double, std::string> v;
for (auto const& v2 : h.data)
{
dummy<boost::variant<int, double, std::string>> d(v);
boost::apply_visitor(d, v2);
}
}
void run_variant_test(std::size_t runs)
{
test::Holder<util::variant<int, double, std::string>> h;
h.data.reserve(runs);
for (std::size_t i = 0; i < runs; ++i)
{
h.append_move(std::string(TEXT_SHORT));
h.append_move(std::string(TEXT_LONG));
h.append_move(123);
h.append_move(3.14159);
}
util::variant<int, double, std::string> v;
for (auto const& v2 : h.data)
{
dummy2<util::variant<int, double, std::string>> d(v);
util::apply_visitor(d, v2);
}
}
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage:" << argv[0] << " <num-runs>" << std::endl;
return 1;
}
#ifndef SINGLE_THREADED
const std::size_t THREADS = 4;
#endif
const std::size_t NUM_RUNS = static_cast<std::size_t>(std::stol(argv[1]));
#ifdef SINGLE_THREADED
for (std::size_t j = 0; j < NUM_SAMPLES; ++j)
{
{
std::cerr << "custom variant: ";
boost::timer::auto_cpu_timer t;
run_variant_test(NUM_RUNS);
}
{
std::cerr << "boost variant: ";
boost::timer::auto_cpu_timer t;
run_boost_test(NUM_RUNS);
}
}
#else
for (std::size_t j = 0; j < NUM_SAMPLES; ++j)
{
{
typedef std::vector<std::unique_ptr<std::thread>> thread_group;
typedef thread_group::value_type value_type;
thread_group tg;
std::cerr << "custom variant: ";
boost::timer::auto_cpu_timer timer;
for (std::size_t i = 0; i < THREADS; ++i)
{
tg.emplace_back(new std::thread(run_variant_test, NUM_RUNS));
}
std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); });
}
{
typedef std::vector<std::unique_ptr<std::thread>> thread_group;
typedef thread_group::value_type value_type;
thread_group tg;
std::cerr << "boost variant: ";
boost::timer::auto_cpu_timer timer;
for (std::size_t i = 0; i < THREADS; ++i)
{
tg.emplace_back(new std::thread(run_boost_test, NUM_RUNS));
}
std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); });
}
}
#endif
return EXIT_SUCCESS;
}
+138
View File
@@ -0,0 +1,138 @@
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "variant.hpp"
#include "variant_io.hpp"
using namespace mapbox;
namespace test {
template <typename T>
struct string_to_number
{
};
template <>
struct string_to_number<double>
{
double operator()(std::string const& str) const
{
return std::stod(str);
}
};
template <>
struct string_to_number<std::int64_t>
{
std::int64_t operator()(std::string const& str) const
{
return std::stoll(str);
}
};
template <>
struct string_to_number<std::uint64_t>
{
std::uint64_t operator()(std::string const& str) const
{
return std::stoull(str);
}
};
template <>
struct string_to_number<bool>
{
bool operator()(std::string const& str) const
{
bool result;
std::istringstream(str) >> std::boolalpha >> result;
return result;
}
};
struct javascript_equal_visitor
{
template <typename T>
bool operator()(T lhs, T rhs) const
{
return lhs == rhs;
}
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
bool operator()(T lhs, std::string const& rhs) const
{
return lhs == string_to_number<T>()(rhs);
}
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
bool operator()(std::string const& lhs, T rhs) const
{
return string_to_number<T>()(lhs) == rhs;
}
template <typename T0, typename T1>
bool operator()(T0 lhs, T1 rhs) const
{
return lhs == static_cast<T0>(rhs);
}
};
template <typename T>
struct javascript_equal
{
javascript_equal(T const& lhs)
: lhs_(lhs) {}
bool operator()(T const& rhs) const
{
return util::apply_visitor(test::javascript_equal_visitor(), lhs_, rhs);
}
T const& lhs_;
};
} // namespace test
int main()
{
typedef util::variant<bool, std::int64_t, std::uint64_t, double, std::string> variant_type;
variant_type v0(3.14159);
variant_type v1(std::string("3.14159"));
variant_type v2(std::uint64_t(1));
std::cerr << v0 << " == " << v1 << " -> "
<< std::boolalpha << util::apply_visitor(test::javascript_equal_visitor(), v0, v1) << std::endl;
std::vector<variant_type> vec;
vec.emplace_back(std::string("1"));
vec.push_back(variant_type(std::uint64_t(2)));
vec.push_back(variant_type(std::uint64_t(3)));
vec.push_back(std::string("3.14159"));
vec.emplace_back(3.14159);
//auto itr = std::find_if(vec.begin(), vec.end(), [&v0](variant_type const& val) {
// return util::apply_visitor(test::javascript_equal_visitor(), v0, val);
// });
auto itr = std::find_if(vec.begin(), vec.end(), test::javascript_equal<variant_type>(v2));
if (itr != std::end(vec))
{
std::cout << "found " << *itr << std::endl;
}
else
{
std::cout << "can't find " << v2 << '\n';
}
return EXIT_SUCCESS;
}
+20
View File
@@ -0,0 +1,20 @@
#include <boost/variant.hpp>
#include <stdexcept>
struct check : boost::static_visitor<>
{
template <typename T>
void operator()(T const& val) const
{
if (val != 0) throw std::runtime_error("invalid");
}
};
int main()
{
typedef boost::variant<bool, int, double> variant_type;
variant_type v(0);
boost::apply_visitor(check(), v);
return 0;
}
@@ -0,0 +1,22 @@
// @EXPECTED: First type in variant must be default constructible to allow default construction of variant
#include <variant.hpp>
// Checks that the first type in a variant must be default constructible to
// make the variant default constructible.
struct no_def_constructor
{
int value;
no_def_constructor() = delete;
no_def_constructor(int v) : value(v) {}
};
int main()
{
mapbox::util::variant<no_def_constructor> x;
}
@@ -0,0 +1,11 @@
// @EXPECTED: Template parameter type list of variant can not be empty
#include <variant.hpp>
// Empty type list should not work.
int main()
{
mapbox::util::variant<> x;
}
@@ -0,0 +1,11 @@
// @EXPECTED:
#include <variant.hpp>
int main()
{
mapbox::util::variant<int> x;
mapbox::util::variant<double> y;
x == y;
}
@@ -0,0 +1,10 @@
// @EXPECTED: enable_if
#include <variant.hpp>
int main()
{
mapbox::util::variant<int, double> x;
x.get<std::string>();
}
@@ -0,0 +1,10 @@
// @EXPECTED: invalid type in T in `is<T>()` for this variant
#include <variant.hpp>
int main()
{
mapbox::util::variant<int, double> x;
x.is<std::string>();
}
@@ -0,0 +1,24 @@
// @EXPECTED: const int
#include <variant.hpp>
struct mutating_visitor
{
mutating_visitor(int val)
: val_(val) {}
void operator()(int& val) const
{
val = val_;
}
int val_;
};
int main()
{
const mapbox::util::variant<int> var(123);
const mutating_visitor visitor(456);
mapbox::util::apply_visitor(visitor, var);
}
@@ -0,0 +1,9 @@
// @EXPECTED: Variant can not hold reference types
#include <variant.hpp>
int main()
{
mapbox::util::variant<double, int&, long> x{mapbox::util::no_init()};
}
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
#include "variant.hpp"
#include <stdexcept>
struct check
{
template <typename T>
void operator()(T const& val) const
{
if (val != 0) throw std::runtime_error("invalid");
}
};
int main()
{
typedef mapbox::util::variant<bool, int, double> variant_type;
variant_type v(0);
mapbox::util::apply_visitor(check(), v);
return 0;
}
+125
View File
@@ -0,0 +1,125 @@
#include <cstdlib>
#include <iostream>
#include <string>
#include <typeinfo>
#include <utility>
#include <boost/timer/timer.hpp>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct add;
struct sub;
template <typename OpTag>
struct binary_op;
typedef util::variant<int,
util::recursive_wrapper<binary_op<add>>,
util::recursive_wrapper<binary_op<sub>>>
expression;
template <typename Op>
struct binary_op
{
expression left; // variant instantiated here...
expression right;
binary_op(expression&& lhs, expression&& rhs)
: left(std::move(lhs)), right(std::move(rhs))
{
}
};
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
struct test
{
template <typename T>
std::string operator()(T const& obj) const
{
return std::string("TYPE_ID=") + typeid(obj).name();
}
};
struct calculator
{
public:
int operator()(int value) const
{
return value;
}
int operator()(binary_op<add> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) + util::apply_visitor(calculator(), binary.right);
}
int operator()(binary_op<sub> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) - util::apply_visitor(calculator(), binary.right);
}
};
struct to_string
{
public:
std::string operator()(int value) const
{
return std::to_string(value);
}
std::string operator()(binary_op<add> const& binary) const
{
return util::apply_visitor(to_string(), binary.left) + std::string("+") + util::apply_visitor(to_string(), binary.right);
}
std::string operator()(binary_op<sub> const& binary) const
{
return util::apply_visitor(to_string(), binary.left) + std::string("-") + util::apply_visitor(to_string(), binary.right);
}
};
} // namespace test
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage" << argv[0] << " <num-iter>" << std::endl;
return EXIT_FAILURE;
}
const std::size_t NUM_ITER = static_cast<std::size_t>(std::stol(argv[1]));
test::expression sum(test::binary_op<test::add>(2, 3));
test::expression result(test::binary_op<test::sub>(std::move(sum), 4));
std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl;
int total = 0;
{
boost::timer::auto_cpu_timer t;
for (std::size_t i = 0; i < NUM_ITER; ++i)
{
total += util::apply_visitor(test::calculator(), result);
}
}
std::cerr << "total=" << total << std::endl;
std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl;
return EXIT_SUCCESS;
}
+76
View File
@@ -0,0 +1,76 @@
#include <cstdlib>
#include <functional>
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct point
{
public:
point(double x_, double y_)
: x(x_), y(y_) {}
double x;
double y;
};
struct line_string : std::vector<point>
{
};
struct polygon : std::vector<line_string>
{
};
using variant = util::variant<std::reference_wrapper<const point>,
std::reference_wrapper<const line_string>,
std::reference_wrapper<const polygon>>;
struct print
{
using result_type = void;
void operator()(point const& pt) const
{
std::cerr << "Point(" << pt.x << "," << pt.y << ")" << std::endl;
}
void operator()(line_string const& line) const
{
std::cerr << "Line(";
for (auto const& pt : line)
{
std::cerr << pt.x << " " << pt.y << ",";
}
std::cerr << ")" << std::endl;
}
template <typename T>
void operator()(T const& val) const
{
std::cerr << typeid(T).name() << std::endl;
}
};
}
int main()
{
std::cerr << sizeof(test::polygon) << std::endl;
std::cerr << sizeof(test::variant) << std::endl;
test::point pt(123, 456);
test::variant var = std::cref(pt);
util::apply_visitor(test::print(), var);
test::line_string line;
line.push_back(pt);
line.push_back(pt);
line.push_back(test::point(999, 333));
var = std::cref(line);
util::apply_visitor(test::print(), var);
std::cerr << "Is line (cref) ? " << var.is<std::reference_wrapper<test::line_string const>>() << std::endl;
auto const& line2 = var.get<test::line_string>(); // accessing underlying type of std::reference_wrapper<T>
test::print printer;
printer(line2);
return EXIT_SUCCESS;
}
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " i-d"
using variant_type = mapbox::util::variant<int, double>;
#include "binary_visitor_impl.hpp"
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-d"
using variant_type = mapbox::util::variant<bool, int, double>;
#include "binary_visitor_impl.hpp"
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " i-d-b"
using variant_type = mapbox::util::variant<int, double, bool>;
#include "binary_visitor_impl.hpp"
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-d-c"
using variant_type = mapbox::util::variant<bool, int, double, char>;
#include "binary_visitor_impl.hpp"
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-c-d-i"
using variant_type = mapbox::util::variant<bool, int, char, double, int>;
#include "binary_visitor_impl.hpp"
+7
View File
@@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-i-d-c-u"
using variant_type = mapbox::util::variant<bool, int, int, double, char, short int>;
#include "binary_visitor_impl.hpp"
+204
View File
@@ -0,0 +1,204 @@
#include <type_traits>
#include "catch.hpp"
#include "variant_io.hpp"
struct add_visitor
{
add_visitor() {}
template <typename A, typename B>
double operator()(A a, B b) const
{
return a + b;
}
};
TEST_CASE("const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a{7};
const variant_type b = 3;
const variant_type c{7.1};
const variant_type d = 2.9;
const add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("non-const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 7;
const variant_type b = 3;
const variant_type c = 7.1;
const variant_type d = 2.9;
add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 7;
variant_type b = 3;
variant_type c = 7.1;
variant_type d = 2.9;
const add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("non-const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 7;
variant_type b = 3;
variant_type c = 7.1;
variant_type d = 2.9;
add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("rvalue binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 7;
const variant_type b = 3;
const variant_type c = 7.1;
const variant_type d = 2.9;
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, a) == Approx(9.9));
}
struct sum_mul_visitor
{
double sum;
sum_mul_visitor() : sum(0.0) {}
template <typename A, typename B>
double operator()(A a, B b)
{
double m = a * b;
sum += m;
return m;
}
};
TEST_CASE("mutable binary visitor works" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 2;
const variant_type b = 3;
const variant_type c = 0.1;
const variant_type d = 0.2;
sum_mul_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(6));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(0.02));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(0.2));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(0.4));
REQUIRE(v.sum == Approx(6.62));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(6));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(0.02));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(0.2));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(0.4));
}
struct swap_visitor
{
swap_visitor(){};
template <typename A, typename B>
void operator()(A& a, B& b) const
{
using T = typename std::common_type<A, B>::type;
T tmp = a;
a = b;
b = tmp;
}
};
TEST_CASE("static mutating visitor on mutable variants works" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 2;
variant_type b = 3;
variant_type c = 0.1;
variant_type d = 0.2;
const swap_visitor v;
SECTION("swap a and b")
{
mapbox::util::apply_visitor(v, a, b);
REQUIRE(a.get<int>() == 3);
REQUIRE(b.get<int>() == 2);
}
SECTION("swap c and d")
{
mapbox::util::apply_visitor(v, c, d);
REQUIRE(c.get<double>() == Approx(0.2));
REQUIRE(d.get<double>() == Approx(0.1));
}
SECTION("swap a and c")
{
mapbox::util::apply_visitor(v, a, c);
REQUIRE(a.get<int>() == 0);
REQUIRE(c.get<double>() == Approx(2.0));
}
SECTION("swap c and a")
{
mapbox::util::apply_visitor(v, c, a);
REQUIRE(a.get<int>() == 0);
REQUIRE(c.get<double>() == Approx(2.0));
}
}
+48
View File
@@ -0,0 +1,48 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
// https://github.com/mapbox/variant/issues/21
static int count;
struct t1
{
int value;
t1(int v) : value(v)
{
++count;
}
~t1()
{
--count;
}
};
struct t2
{
int value;
t2(int v) : value(v)
{ // constructor fails
throw std::runtime_error("fail");
}
};
TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]")
{
using variant_type = mapbox::util::variant<t1, t2>;
count = 0;
{
variant_type v{42};
REQUIRE(v.is<t1>());
REQUIRE(v.get<t1>().value == 42);
REQUIRE_THROWS({
v.set<t2>(13);
});
}
REQUIRE(count == 0);
}
+36
View File
@@ -0,0 +1,36 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <string>
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 visitation", "[visitor][unary visitor]")
{
mapbox::util::variant<int, double, std::string> var(123);
REQUIRE(var.get<int>() == 123);
int val = 456;
const mutating_visitor<int> visitor(val);
mapbox::util::apply_visitor(visitor, var);
REQUIRE(var.get<int>() == 456);
}
+102
View File
@@ -0,0 +1,102 @@
#include "catch.hpp"
#include "optional.hpp"
struct dummy
{
dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {}
int m_1;
int m_2;
};
TEST_CASE("optional can be instantiated with a POD type", "[optional]")
{
mapbox::util::optional<int> dbl_opt;
REQUIRE(!dbl_opt);
dbl_opt = 3;
REQUIRE(dbl_opt);
REQUIRE(dbl_opt.get() == 3);
REQUIRE(*dbl_opt == 3);
}
TEST_CASE("copy c'tor", "[optional]")
{
mapbox::util::optional<int> dbl_opt;
REQUIRE(!dbl_opt);
dbl_opt = 3;
REQUIRE(dbl_opt);
mapbox::util::optional<int> other = dbl_opt;
REQUIRE(other.get() == 3);
REQUIRE(*other == 3);
}
TEST_CASE("const operator*, const get()", "[optional]")
{
const mapbox::util::optional<int> dbl_opt = 3;
REQUIRE(dbl_opt);
auto pi1 = dbl_opt.get();
auto pi2 = *dbl_opt;
REQUIRE(pi1 == 3);
REQUIRE(pi2 == 3);
}
TEST_CASE("non-const operator*, non-const get()", "[optional]")
{
mapbox::util::optional<int> dbl_opt = 3;
REQUIRE(dbl_opt);
auto pi1 = dbl_opt.get();
auto pi2 = *dbl_opt;
REQUIRE(pi1 == 3);
REQUIRE(pi2 == 3);
}
TEST_CASE("emplace initialization, reset", "[optional]")
{
mapbox::util::optional<dummy> dummy_opt;
REQUIRE(!dummy_opt);
// rvalues, baby!
dummy_opt.emplace(1, 2);
REQUIRE(dummy_opt);
REQUIRE(dummy_opt.get().m_1 == 1);
REQUIRE((*dummy_opt).m_2 == 2);
dummy_opt.reset();
REQUIRE(!dummy_opt);
}
TEST_CASE("assignment", "[optional]")
{
mapbox::util::optional<int> a;
mapbox::util::optional<int> b;
a = 1;
b = 3;
REQUIRE(a.get() == 1);
REQUIRE(b.get() == 3);
b = a;
REQUIRE(a.get() == b.get());
REQUIRE(b.get() == 1);
}
TEST_CASE("self assignment", "[optional]")
{
mapbox::util::optional<int> a;
a = 1;
REQUIRE(a.get() == 1);
a = a;
REQUIRE(a.get() == 1);
}
+158
View File
@@ -0,0 +1,158 @@
#include "catch.hpp"
#include "recursive_wrapper.hpp"
#include <type_traits>
#include <utility>
using rwi = mapbox::util::recursive_wrapper<int>;
using rwp = mapbox::util::recursive_wrapper<std::pair<int, int>>;
static_assert(std::is_same<rwi::type, int>::value, "type check failed");
TEST_CASE("recursive wrapper of int")
{
SECTION("construct with value")
{
rwi a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
a = 8;
REQUIRE(a.get() == 8);
rwi b{a};
REQUIRE(b.get() == 8);
rwi c;
c = b;
REQUIRE(b.get() == 8);
REQUIRE(c.get() == 8);
c = 9;
REQUIRE(c.get() == 9);
int x = 10;
c = x;
REQUIRE(c.get() == 10);
b = std::move(c);
REQUIRE(b.get() == 10);
}
SECTION("construct with const reference")
{
int i = 7;
rwi a{i};
REQUIRE(a.get() == 7);
}
SECTION("implicit conversion to reference of underlying type")
{
SECTION("const")
{
rwi const a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
rwi::type const& underlying = a;
REQUIRE(underlying == 7);
}
SECTION("non const")
{
rwi a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
rwi::type& underlying = a;
REQUIRE(underlying == 7);
a = 8;
REQUIRE(underlying == 8);
}
}
}
TEST_CASE("move of recursive wrapper")
{
rwi a{1};
SECTION("move constructor")
{
rwi b{std::move(a)};
REQUIRE(b.get() == 1);
}
SECTION("operator= on rvalue")
{
rwi b{2};
b = std::move(a);
REQUIRE(b.get() == 1);
}
}
TEST_CASE("swap")
{
rwi a{1};
rwi b{2};
REQUIRE(a.get() == 1);
REQUIRE(b.get() == 2);
using std::swap;
swap(a, b);
REQUIRE(a.get() == 2);
REQUIRE(b.get() == 1);
}
TEST_CASE("recursive wrapper of pair<int, int>")
{
SECTION("default constructed")
{
rwp a;
REQUIRE(a.get().first == 0);
REQUIRE(a.get().second == 0);
}
SECTION("construct with value")
{
rwp a{std::make_pair(1, 2)};
REQUIRE(a.get().first == 1);
REQUIRE(a.get().second == 2);
REQUIRE(a.get_pointer()->first == 1);
REQUIRE(a.get_pointer()->second == 2);
a = {3, 4};
REQUIRE(a.get().first == 3);
REQUIRE(a.get().second == 4);
rwp b{a};
REQUIRE(b.get().first == 3);
REQUIRE(b.get().second == 4);
rwp c;
c = b;
REQUIRE(b.get().first == 3);
REQUIRE(b.get().second == 4);
REQUIRE(c.get().first == 3);
REQUIRE(c.get().second == 4);
c = {5, 6};
REQUIRE(c.get().first == 5);
REQUIRE(c.get().second == 6);
b = std::move(c);
REQUIRE(b.get().first == 5);
REQUIRE(b.get().second == 6);
// REQUIRE(c.get_pointer() == nullptr);
}
}
+52
View File
@@ -0,0 +1,52 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
struct some_struct
{
int a;
bool b;
std::string c;
};
using variant_internal_index_type = size_t;
TEST_CASE("size of variants")
{
constexpr const auto min_overhead = sizeof(variant_internal_index_type);
using namespace std; // workaround for bug in GCC <= 4.8 where max_align_t is not in std
constexpr const auto max_overhead = alignof(max_align_t) + min_overhead;
using v1 = mapbox::util::variant<int>;
using v2 = mapbox::util::variant<int, bool, int64_t>;
using v3 = mapbox::util::variant<int, std::string>;
using v4 = mapbox::util::variant<std::string, std::string>;
using v5 = mapbox::util::variant<some_struct>;
constexpr const auto si = sizeof(int);
constexpr const auto sb = sizeof(bool);
constexpr const auto si64 = sizeof(int64_t);
constexpr const auto sd = sizeof(double);
constexpr const auto sstr = sizeof(std::string);
constexpr const auto spi = sizeof(std::pair<int, int>);
constexpr const auto ss = sizeof(some_struct);
REQUIRE(sizeof(v1) <= max_overhead + si);
REQUIRE(sizeof(v2) <= max_overhead + std::max({si, sb, si64}));
REQUIRE(sizeof(v3) <= max_overhead + std::max({si, sstr}));
REQUIRE(sizeof(v4) <= max_overhead + sstr);
REQUIRE(sizeof(v5) <= max_overhead + ss);
REQUIRE(sizeof(v1) >= min_overhead + si);
REQUIRE(sizeof(v2) >= min_overhead + std::max({si, sb, si64}));
REQUIRE(sizeof(v3) >= min_overhead + std::max({si, sstr}));
REQUIRE(sizeof(v4) >= min_overhead + sstr);
REQUIRE(sizeof(v5) >= min_overhead + ss);
}
+127
View File
@@ -0,0 +1,127 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <string>
struct some_visitor
{
int var_;
some_visitor(int init)
: var_(init) {}
int operator()(int val) const
{
return var_ + val;
}
int operator()(double val) const
{
return var_ + int(val);
}
int operator()(const std::string&) const
{
return 0;
}
};
TEST_CASE("non-const visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
some_visitor visitor{1};
REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0);
}
TEST_CASE("const visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
const some_visitor visitor{1};
REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0);
}
TEST_CASE("rvalue visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var3) == 0);
}
TEST_CASE("visitor works on rvalue variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{123}) == 124);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{3.2}) == 4);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{"foo"}) == 0);
}
struct total_sizeof
{
total_sizeof() : total_(0) {}
template <class Value>
int operator()(const Value&) const
{
total_ += int(sizeof(Value));
return total_;
}
int result() const
{
return total_;
}
mutable int total_;
}; // total_sizeof
TEST_CASE("changes in visitor should be visible", "[visitor][unary visitor]")
{
using variant_type = mapbox::util::variant<int, std::string, double>;
variant_type v;
total_sizeof ts;
v = 5.9;
REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(double));
REQUIRE(ts.result() == sizeof(double));
}
TEST_CASE("changes in const visitor (with mutable internals) should be visible", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, std::string, double>;
variant_type v{"foo"};
const total_sizeof ts;
REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(std::string));
REQUIRE(ts.result() == sizeof(std::string));
}
+570
View File
@@ -0,0 +1,570 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
// Hack to make nullptr work with Catch
namespace std {
template <class C, class T>
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, std::nullptr_t)
{
return os << (void*)nullptr;
}
}
TEST_CASE("variant can be moved into vector", "[variant]")
{
using variant_type = mapbox::util::variant<bool, std::string>;
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")
{
mapbox::util::variant<bool> v(true);
REQUIRE(v.valid());
REQUIRE(v.is<bool>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<bool>() == true);
v.set<bool>(false);
REQUIRE(v.get<bool>() == false);
v = true;
REQUIRE(v == mapbox::util::variant<bool>(true));
}
SECTION("nullptr")
{
using value_type = std::nullptr_t;
mapbox::util::variant<value_type> v(nullptr);
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == nullptr);
REQUIRE(v == mapbox::util::variant<value_type>(nullptr));
}
SECTION("unique_ptr")
{
using value_type = std::unique_ptr<std::string>;
mapbox::util::variant<value_type> v(value_type(new std::string("hello")));
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(*v.get<value_type>().get() == *value_type(new std::string("hello")).get());
REQUIRE(*v.get<value_type>() == "hello");
}
SECTION("string")
{
using value_type = std::string;
mapbox::util::variant<value_type> v(value_type("hello"));
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(value_type("variant")));
}
SECTION("size_t")
{
using value_type = std::size_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int8_t")
{
using value_type = std::int8_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int16_t")
{
using value_type = std::int16_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int32_t")
{
using value_type = std::int32_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int64_t")
{
using value_type = std::int64_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 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 == mapbox::util::variant<value_type>(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<MissionInteger> v(MissionInteger(34838300));
REQUIRE(v.valid());
REQUIRE(v.is<MissionInteger>());
REQUIRE(v.which() == 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 == mapbox::util::variant<MissionInteger>(MissionInteger(1)));
}
TEST_CASE("variant::which() returns zero based index of stored type", "[variant]")
{
using variant_type = mapbox::util::variant<bool, std::string, std::uint64_t, std::int64_t, double, float>;
// 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<int, double>;
variant_type var = 5;
REQUIRE(var.is<int>());
REQUIRE_FALSE(var.is<double>());
REQUIRE(var.get<int>() == 5);
REQUIRE_THROWS_AS({
var.get<double>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("get with wrong type (here: int) should throw", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type var = 5.0;
REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<int>());
REQUIRE(var.get<double>() == 5.0);
REQUIRE(mapbox::util::get<double>(var) == 5.0);
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(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<int, double>;
const variant_type var = 5.0;
REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<int>());
REQUIRE(var.get<double>() == 5.0);
REQUIRE(mapbox::util::get<double>(var) == 5.0);
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(var);
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("get with any type should throw if not initialized", "[variant]")
{
mapbox::util::variant<int, double> var{mapbox::util::no_init()};
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
var.get<double>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("no_init variant can be copied and moved from", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type v1{mapbox::util::no_init()};
variant_type v2{42};
variant_type v3{23};
REQUIRE(v2.get<int>() == 42);
v2 = v1;
REQUIRE_THROWS_AS({
v2.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE(v3.get<int>() == 23);
v3 = std::move(v1);
REQUIRE_THROWS_AS({
v3.get<int>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("no_init variant can be copied and moved to", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type v1{42};
variant_type v2{mapbox::util::no_init()};
variant_type v3{mapbox::util::no_init()};
REQUIRE_THROWS_AS({
v2.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE(v1.get<int>() == 42);
v2 = v1;
REQUIRE(v2.get<int>() == 42);
REQUIRE(v1.get<int>() == 42);
REQUIRE_THROWS_AS({
v3.get<int>();
},
mapbox::util::bad_variant_access&);
v3 = std::move(v1);
REQUIRE(v3.get<int>() == 42);
}
TEST_CASE("implicit conversion", "[variant][implicit conversion]")
{
using variant_type = mapbox::util::variant<int>;
variant_type var(5.0); // converted to int
REQUIRE(var.get<int>() == 5);
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]")
{
using variant_type = mapbox::util::variant<long, char>;
variant_type var = 5.0; // converted to long
REQUIRE(var.get<long>() == 5);
REQUIRE_THROWS_AS({
var.get<char>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]")
{
using variant_type = mapbox::util::variant<unsigned char>;
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("implicit conversion to a suitable type", "[variant][implicit conversion]")
{
using mapbox::util::variant;
CHECK_NOTHROW((variant<dummy, float, std::string>(123)).get<float>());
CHECK_NOTHROW((variant<dummy, float, std::string>("foo")).get<std::string>());
}
TEST_CASE("value_traits for non-convertible type", "[variant::detail]")
{
namespace detail = mapbox::util::detail;
using target_type = detail::value_traits<dummy, int>::target_type;
CHECK((std::is_same<target_type, void>::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<bool, bool, int, double, std::string>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<bool, bool, int, double, int>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<int, bool, int, double, std::string>::index == 2));
REQUIRE((mapbox::util::detail::value_traits<int, bool, int, double, int>::index == 2));
REQUIRE((mapbox::util::detail::value_traits<double, bool, int, double, std::string>::index == 1));
REQUIRE((mapbox::util::detail::value_traits<bool, bool, int, bool, std::string>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<std::string, bool, int, double, std::string>::index == 0));
REQUIRE((mapbox::util::detail::value_traits<dummy, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value));
REQUIRE((mapbox::util::detail::value_traits<std::vector<int>, 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<int, double, std::string>;
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<int, double, std::string>;
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");
}
TEST_CASE("swapping variants should do the right thing", "[variant]")
{
using variant_type = mapbox::util::variant<int, double, std::string>;
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<int>() == 3);
REQUIRE(a.which() == 0);
REQUIRE(b.get<int>() == 7);
REQUIRE(b.which() == 0);
swap(b, c);
REQUIRE(b.get<double>() == Approx(3.141));
REQUIRE(b.which() == 1);
REQUIRE(c.get<int>() == 7);
REQUIRE(c.which() == 0);
swap(b, d);
REQUIRE(b.get<std::string>() == "foo");
REQUIRE(b.which() == 2);
REQUIRE(d.get<double>() == Approx(3.141));
REQUIRE(d.which() == 1);
swap(b, e);
REQUIRE(b.get<std::string>() == "a long string that is longer than small string optimization");
REQUIRE(b.which() == 2);
REQUIRE(e.get<std::string>() == "foo");
REQUIRE(e.which() == 2);
}
TEST_CASE("variant should work with equality operators")
{
using variant_type = mapbox::util::variant<int, std::string>;
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<int, std::string>;
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>, std::reference_wrapper<double>>;
int a = 1;
variant_type v{std::ref(a)};
REQUIRE(v.get<int>() == 1);
REQUIRE(mapbox::util::get<int>(v) == 1);
REQUIRE_THROWS_AS({
v.get<double>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double>(v);
},
mapbox::util::bad_variant_access&);
a = 2;
REQUIRE(v.get<int>() == 2);
v.get<int>() = 3;
REQUIRE(a == 3);
double b = 3.141;
v = std::ref(b);
REQUIRE(v.get<double>() == Approx(3.141));
REQUIRE(mapbox::util::get<double>(v) == Approx(3.141));
REQUIRE_THROWS_AS({
v.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(v);
},
mapbox::util::bad_variant_access&);
b = 2.718;
REQUIRE(v.get<double>() == Approx(2.718));
a = 3;
REQUIRE(v.get<double>() == Approx(2.718));
v.get<double>() = 4.1;
REQUIRE(b == Approx(4.1));
REQUIRE_THROWS_AS({
v.get<int>() = 4;
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("storing reference wrappers to consts works")
{
using variant_type = mapbox::util::variant<std::reference_wrapper<int const>, std::reference_wrapper<double const>>;
int a = 1;
variant_type v{std::cref(a)};
REQUIRE(v.get<int const>() == 1);
REQUIRE(v.get<int>() == 1); // this works (see #82)
REQUIRE(mapbox::util::get<int const>(v) == 1);
// REQUIRE(mapbox::util::get<int>(v) == 1); // this doesn't work (see #82)
REQUIRE_THROWS_AS({
v.get<double const>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double const>(v);
},
mapbox::util::bad_variant_access&);
double b = 3.141;
v = std::cref(b);
REQUIRE(v.get<double const>() == Approx(3.141));
REQUIRE(mapbox::util::get<double const>(v) == Approx(3.141));
REQUIRE_THROWS_AS({
v.get<int const>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int const>(v);
},
mapbox::util::bad_variant_access&);
}
+126
View File
@@ -0,0 +1,126 @@
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>
#include <utility>
#include <boost/timer/timer.hpp>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct add;
struct sub;
template <typename OpTag>
struct binary_op;
typedef util::variant<int,
std::unique_ptr<binary_op<add>>,
std::unique_ptr<binary_op<sub>>>
expression;
template <typename Op>
struct binary_op
{
expression left; // variant instantiated here...
expression right;
binary_op(expression&& lhs, expression&& rhs)
: left(std::move(lhs)), right(std::move(rhs))
{
}
};
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
struct test
{
template <typename T>
std::string operator()(T const& obj) const
{
return std::string("TYPE_ID=") + typeid(obj).name();
}
};
struct calculator
{
public:
int operator()(int value) const
{
return value;
}
int operator()(std::unique_ptr<binary_op<add>> const& binary) const
{
return util::apply_visitor(calculator(), binary->left) + util::apply_visitor(calculator(), binary->right);
}
int operator()(std::unique_ptr<binary_op<sub>> const& binary) const
{
return util::apply_visitor(calculator(), binary->left) - util::apply_visitor(calculator(), binary->right);
}
};
struct to_string
{
public:
std::string operator()(int value) const
{
return std::to_string(value);
}
std::string operator()(std::unique_ptr<binary_op<add>> const& binary) const
{
return util::apply_visitor(to_string(), binary->left) + std::string("+") + util::apply_visitor(to_string(), binary->right);
}
std::string operator()(std::unique_ptr<binary_op<sub>> const& binary) const
{
return util::apply_visitor(to_string(), binary->left) + std::string("-") + util::apply_visitor(to_string(), binary->right);
}
};
} // namespace test
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage" << argv[0] << " <num-iter>" << std::endl;
return EXIT_FAILURE;
}
const std::size_t NUM_ITER = static_cast<std::size_t>(std::stol(argv[1]));
test::expression sum(std::unique_ptr<test::binary_op<test::add>>(new test::binary_op<test::add>(2, 3)));
test::expression result(std::unique_ptr<test::binary_op<test::sub>>(new test::binary_op<test::sub>(std::move(sum), 4)));
std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl;
int total = 0;
{
boost::timer::auto_cpu_timer t;
for (std::size_t i = 0; i < NUM_ITER; ++i)
{
total += util::apply_visitor(test::calculator(), result);
}
}
std::cerr << "total=" << total << std::endl;
std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl;
return EXIT_SUCCESS;
}
+2
View File
@@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"