[mapbox-variant] 01/05: Imported Upstream version 1.1.4
Bas Couwenberg
sebastic at debian.org
Wed Nov 23 19:21:25 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository mapbox-variant.
commit a7491b3ad547d55c8339275894bc7ab26af2474a
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Wed Nov 23 17:19:12 2016 +0100
Imported Upstream version 1.1.4
---
Makefile | 10 ++-
README.md | 168 +++++++++++++++++++++++++++++++++++--
include/mapbox/variant.hpp | 39 +++++++++
include/mapbox/variant_visitor.hpp | 38 +++++++++
test/hashable_test.cpp | 158 ++++++++++++++++++++++++++++++++++
test/lambda_overload_test.cpp | 127 ++++++++++++++++++++++++++++
6 files changed, 533 insertions(+), 7 deletions(-)
diff --git a/Makefile b/Makefile
index fc554b8..10ab7b5 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ LDFLAGS := $(LDFLAGS)
ALL_HEADERS = $(shell find include/mapbox/ '(' -name '*.hpp' ')')
-all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test
+all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test out/lambda_overload_test out/hashable_test
mason_packages:
git submodule update --init .mason
@@ -47,6 +47,14 @@ out/binary_visitor_test: Makefile mason_packages test/binary_visitor_test.cpp
mkdir -p ./out
$(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS)
+out/lambda_overload_test: Makefile mason_packages test/lambda_overload_test.cpp
+ mkdir -p ./out
+ $(CXX) -o out/lambda_overload_test test/lambda_overload_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS)
+
+out/hashable_test: Makefile mason_packages test/hashable_test.cpp
+ mkdir -p ./out
+ $(CXX) -o out/hashable_test test/hashable_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS)
+
bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test
./out/bench-variant 100000
./out/unique_ptr_test 100000
diff --git a/README.md b/README.md
index 1de54de..0e274b0 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,169 @@
# Mapbox Variant
-An alternative to `boost::variant` for C++11 and C++14
+An header-only alternative to `boost::variant` for C++11 and C++14
[](https://travis-ci.org/mapbox/variant)
[](https://ci.appveyor.com/project/Mapbox/variant)
[](https://coveralls.io/r/mapbox/variant?branch=master)
+## Introduction
+
+Variant's basic building blocks are:
+- `variant<Ts...>` - a type-safe representation for sum-types / discriminated unions
+- `recursive_wrapper<T>` - a helper type to represent recursive "tree-like" variants
+- `apply_visitor(visitor, myVariant)` - to invoke a custom visitor on the variant's underlying type
+- `get<T>()` - a function to directly unwrap a variant's underlying type
+- `.match([](Type){})` - a variant convenience member function taking an arbitrary number of lambdas creating a visitor behind the scenes and applying it to the variant
+
+
+### Basic Usage - HTTP API Example
+
+Suppose you want to represent a HTTP API response which is either a JSON result or an error:
+
+```c++
+struct Result {
+ Json object;
+};
+
+struct Error {
+ int32_t code;
+ string message;
+};
+```
+
+You can represent this at type level using a variant which is either an `Error` or a `Result`:
+
+```c++
+using Response = variant<Error, Result>;
+
+Response makeRequest() {
+ return Error{501, "Not Implemented"};
+}
+
+Response ret = makeRequest();
+```
+
+To see which type the `Response` holds you pattern match on the variant unwrapping the underlying value:
+
+```c++
+ret.match([] (Result r) { print(r.object); }
+ [] (Error e) { print(e.message); });
+```
+
+Instead of using the variant's convenience `.match` pattern matching function you can create a type visitor functor and use `apply_visitor` manually:
+
+```c++
+struct ResponseVisitor {
+ void operator()(Result r) const {
+ print(r.object);
+ }
+
+ void operator()(Error e) const {
+ print(e.message);
+ }
+};
+
+ResponseVisitor visitor;
+apply_visitor(visitor, ret);
+```
+
+In both cases the compiler makes sure you handle all types the variant can represent at compile.
+
+
+### Recursive Variants - JSON Example
+
+[JSON](http://www.json.org/) consists of types `String`, `Number`, `True`, `False`, `Null`, `Array` and `Object`.
+
+
+```c++
+struct String { string value; };
+struct Number { double value; };
+struct True { };
+struct False { };
+struct Null { };
+struct Array { vector<?> values; };
+struct Object { unordered_map<string, ?> values; };
+```
+
+This works for primitive types but how do we represent recursive types such as `Array` which can hold multiple elements and `Array` itself, too?
+
+For these use cases Variant provides a `recursive_wrapper` helper type which lets you express recursive Variants.
+
+```c++
+struct String { string value; };
+struct Number { double value; };
+struct True { };
+struct False { };
+struct Null { };
+
+// Forward declarations only
+struct Array;
+struct Object;
+
+using Value = variant<String, Number, True, False, Null, recursive_wrapper<Array>, recursive_wrapper<Object>>;
+
+struct Array {
+ vector<Value> values;
+};
+
+struct Object {
+ unordered_map<string, Value> values;
+};
+```
+
+For walkig the JSON representation you can again either create a `JSONVisitor`:
+
+```c++
+struct JSONVisitor {
+
+ void operator()(Null) const {
+ print("null");
+ }
+
+ // same for all other JSON types
+};
+
+JSONVisitor visitor;
+apply_visitor(visitor, json);
+```
+
+Or use the convenience `.match` pattern matching function:
+
+```c++
+json.match([] (Null) { print("null"); },
+ ...);
+```
+
+To summarize: use `recursive_wrapper` to represent recursive "tree-like" representations:
+
+```c++
+struct Empty { };
+struct Node;
+
+using Tree = variant<Empty, recursive_wrapper<Node>>;
+
+struct Node {
+ uint64_t value;
+}
+```
+### Advanced Usage Tips
+
+Creating type aliases for variants is a great way to reduce repetition.
+Keep in mind those type aliases are not checked at type level, though.
+We recommend creating a new type for all but basic variant usage:
+
+```c++
+// the compiler can't tell the following two apart
+using APIResult = variant<Error, Result>;
+using FilesystemResult = variant<Error, Result>;
+
+// new type
+struct APIResult : variant<Error, Result> {
+ using Base = variant<Error, Result>;
+ using Base::Base;
+}
+```
+
## Why use Mapbox Variant?
@@ -45,6 +203,9 @@ implementations](doc/other_implementations.md).
Want to know more about the upcoming standard? Have a look at our
[overview](doc/standards_effort.md).
+Most modern high-level languages provide ways to express sum types directly.
+If you're curious have a look at Haskell's pattern matching or Rust's and Swift's enums.
+
## Depends
@@ -63,11 +224,6 @@ Tested with:
- clang++-3.9
- Visual Studio 2015
-## Usage
-
-There is nothing to build, just include `variant.hpp` in your project. Include `variant_io.hpp` if you need
-the `operator<<` overload for variant.
-
## Unit Tests
diff --git a/include/mapbox/variant.hpp b/include/mapbox/variant.hpp
index afdf36a..fb0f77e 100644
--- a/include/mapbox/variant.hpp
+++ b/include/mapbox/variant.hpp
@@ -10,8 +10,10 @@
#include <type_traits>
#include <typeinfo>
#include <utility>
+#include <functional>
#include <mapbox/recursive_wrapper.hpp>
+#include <mapbox/variant_visitor.hpp>
// clang-format off
// [[deprecated]] is only available in C++14, use this for the time being
@@ -531,6 +533,16 @@ private:
Variant const& lhs_;
};
+// hashing visitor
+struct hasher
+{
+ template <typename T>
+ std::size_t operator()(const T& hashable) const
+ {
+ return std::hash<T>{}(hashable);
+ }
+};
+
} // namespace detail
struct no_init
@@ -861,6 +873,22 @@ public:
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
}
+ // match
+ // unary
+ template <typename... Fs>
+ auto VARIANT_INLINE match(Fs&&... fs) const
+ -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward<Fs>(fs)...)))
+ {
+ return variant::visit(*this, ::mapbox::util::make_visitor(std::forward<Fs>(fs)...));
+ }
+ // non-const
+ template <typename... Fs>
+ auto VARIANT_INLINE match(Fs&&... fs)
+ -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward<Fs>(fs)...)))
+ {
+ return variant::visit(*this, ::mapbox::util::make_visitor(std::forward<Fs>(fs)...));
+ }
+
~variant() noexcept // no-throw destructor
{
helper_type::destroy(type_index, &data);
@@ -971,4 +999,15 @@ ResultType const& get_unchecked(T const& var)
} // namespace util
} // namespace mapbox
+// hashable iff underlying types are hashable
+namespace std {
+template <typename... Types>
+struct hash< ::mapbox::util::variant<Types...>> {
+ std::size_t operator()(const ::mapbox::util::variant<Types...>& v) const noexcept
+ {
+ return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v);
+ }
+};
+}
+
#endif // MAPBOX_UTIL_VARIANT_HPP
diff --git a/include/mapbox/variant_visitor.hpp b/include/mapbox/variant_visitor.hpp
new file mode 100644
index 0000000..481eb65
--- /dev/null
+++ b/include/mapbox/variant_visitor.hpp
@@ -0,0 +1,38 @@
+#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP
+#define MAPBOX_UTIL_VARIANT_VISITOR_HPP
+
+namespace mapbox {
+namespace util {
+
+template <typename... Fns>
+struct visitor;
+
+template <typename Fn>
+struct visitor<Fn> : Fn
+{
+ using type = Fn;
+ using Fn::operator();
+
+ visitor(Fn fn) : Fn(fn) {}
+};
+
+template <typename Fn, typename... Fns>
+struct visitor<Fn, Fns...> : Fn, visitor<Fns...>
+{
+ using type = visitor;
+ using Fn::operator();
+ using visitor<Fns...>::operator();
+
+ visitor(Fn fn, Fns... fns) : Fn(fn), visitor<Fns...>(fns...) {}
+};
+
+template <typename... Fns>
+visitor<Fns...> make_visitor(Fns... fns)
+{
+ return visitor<Fns...>(fns...);
+}
+
+} // namespace util
+} // namespace mapbox
+
+#endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP
diff --git a/test/hashable_test.cpp b/test/hashable_test.cpp
new file mode 100644
index 0000000..f3393a9
--- /dev/null
+++ b/test/hashable_test.cpp
@@ -0,0 +1,158 @@
+#include <cstddef>
+#include <cstdlib>
+#include <functional>
+#include <iostream>
+#include <string>
+#include <unordered_set>
+#include <utility>
+
+#include <mapbox/variant.hpp>
+
+using namespace mapbox::util;
+
+void test_singleton()
+{
+ using V = variant<int>;
+
+ V singleton = 5;
+
+ if (std::hash<V>{}(singleton) != std::hash<int>{}(5))
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+void test_default_hashable()
+{
+ using V = variant<int, double, std::string>;
+
+ V var;
+
+ // Check int hashes
+ var = 1;
+
+ if (std::hash<V>{}(var) != std::hash<int>{}(1))
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+
+ // Check double hashes
+ var = 23.4;
+
+ if (std::hash<V>{}(var) != std::hash<double>{}(23.4))
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+
+ // Check string hashes
+ var = std::string{"Hello, World!"};
+
+ if (std::hash<V>{}(var) != std::hash<std::string>{}("Hello, World!"))
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+struct Hashable
+{
+ static const constexpr auto const_hash = 5;
+};
+
+namespace std {
+template <>
+struct hash<Hashable>
+{
+ std::size_t operator()(const Hashable&) const noexcept
+ {
+ return Hashable::const_hash;
+ }
+};
+}
+
+void test_custom_hasher()
+{
+ using V = variant<int, Hashable, double>;
+
+ V var;
+
+ var = Hashable{};
+
+ if (std::hash<V>{}(var) != Hashable::const_hash)
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+void test_hashable_in_container()
+{
+ using V = variant<int, std::string, double>;
+
+ // won't compile if V is not Hashable
+ std::unordered_set<V> vs;
+
+ vs.insert(1);
+ vs.insert(2.3);
+ vs.insert("4");
+}
+
+struct Empty
+{
+};
+
+struct Node;
+
+using Tree = variant<Empty, recursive_wrapper<Node>>;
+
+struct Node
+{
+ Node(Tree left_, Tree right_) : left(std::move(left_)), right(std::move(right_)) {}
+
+ Tree left = Empty{};
+ Tree right = Empty{};
+};
+
+namespace std {
+template <>
+struct hash<Empty>
+{
+ std::size_t operator()(const Empty&) const noexcept
+ {
+ return 3;
+ }
+};
+
+template <>
+struct hash<Node>
+{
+ std::size_t operator()(const Node& n) const noexcept
+ {
+ return 5 + std::hash<Tree>{}(n.left) + std::hash<Tree>{}(n.right);
+ }
+};
+}
+
+void test_recursive_hashable()
+{
+
+ Tree tree = Node{Node{Empty{}, Empty{}}, Empty{}};
+
+ if (std::hash<Tree>{}(tree) != ((5 + (5 + (3 + 3))) + 3))
+ {
+ std::cerr << "Expected variant hash to be the same as hash of its value\n";
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+int main()
+{
+ test_singleton();
+ test_default_hashable();
+ test_custom_hasher();
+ test_hashable_in_container();
+ test_recursive_hashable();
+}
diff --git a/test/lambda_overload_test.cpp b/test/lambda_overload_test.cpp
new file mode 100644
index 0000000..9e76d99
--- /dev/null
+++ b/test/lambda_overload_test.cpp
@@ -0,0 +1,127 @@
+#include <iostream>
+#include <vector>
+
+#include <mapbox/variant.hpp>
+#include <mapbox/variant_visitor.hpp>
+
+#if __cplusplus >= 201402L
+#define HAS_CPP14_SUPPORT
+#endif
+
+using namespace mapbox::util;
+
+template <typename Left, typename Right>
+using Either = mapbox::util::variant<Left, Right>;
+
+struct Response
+{
+};
+
+struct Error
+{
+};
+
+void test_lambda_overloads()
+{
+ Either<Error, Response> rv;
+
+ rv = Response{};
+
+ auto visitor = make_visitor([](Response) { std::cout << "Response\n"; }, //
+ [](Error) { std::cout << "Error\n"; }); //
+ apply_visitor(visitor, rv);
+}
+
+void test_lambda_overloads_capture()
+{
+ Either<Error, Response> rv;
+
+ rv = Error{};
+
+ int ok = 0;
+ int err = 0;
+
+ auto visitor = make_visitor([&](Response) { ok += 1; }, //
+ [&](Error) { err += 1; }); //
+ apply_visitor(visitor, rv);
+
+ std::cout << "Got " << ok << " ok, " << err << " err" << std::endl;
+}
+
+void test_singleton_variant()
+{
+
+ variant<int> singleton;
+ apply_visitor(make_visitor([](int) {}), singleton);
+}
+
+void test_lambda_overloads_sfinae()
+#ifdef HAS_CPP14_SUPPORT
+{
+ variant<int, float, std::vector<int>> var;
+
+ auto visitor = make_visitor([](auto range) -> decltype(std::begin(range), void()) {
+ for (auto each : range)
+ std::cout << each << ' '; },
+ [](auto x) -> decltype(std::cout << x, void()) {
+ std::cout << x << std::endl;
+ });
+
+ var = 1;
+ apply_visitor(visitor, var);
+
+ var = 2.f;
+ apply_visitor(visitor, var);
+
+ var = std::vector<int>{4, 5, 6};
+ apply_visitor(visitor, var);
+}
+#else
+{
+}
+#endif
+
+void test_match_singleton()
+{
+ variant<int> singleton = 5;
+ singleton.match([](int) {});
+}
+
+void test_match_overloads()
+{
+ Either<Error, Response> rv;
+
+ rv = Response{};
+
+ rv.match([](Response) { std::cout << "Response\n"; }, //
+ [](Error) { std::cout << "Error\n"; }); //
+}
+
+void test_match_overloads_capture()
+{
+ Either<Error, Response> rv;
+
+ rv = Error{};
+
+ int ok = 0;
+ int err = 0;
+
+ rv.match([&](Response) { ok += 1; }, //
+ [&](Error) { err += 1; }); //
+
+ std::cout << "Got " << ok << " ok, " << err << " err" << std::endl;
+}
+
+int main()
+{
+ test_lambda_overloads();
+ test_singleton_variant();
+ test_lambda_overloads_capture();
+ test_lambda_overloads_sfinae();
+
+ test_match_singleton();
+ test_match_overloads();
+ test_match_overloads_capture();
+}
+
+#undef HAS_CPP14_SUPPORT
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapbox-variant.git
More information about the Pkg-grass-devel
mailing list