[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
 
 [![Build Status](https://secure.travis-ci.org/mapbox/variant.svg)](https://travis-ci.org/mapbox/variant)
 [![Build status](https://ci.appveyor.com/api/projects/status/v9tatx21j1k0fcgy)](https://ci.appveyor.com/project/Mapbox/variant)
 [![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master&service=github)](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