Skip to content
Commits on Source (4)
......@@ -4,68 +4,69 @@ sudo: false
matrix:
include:
# clang++ 3.9 via mason with -flto and -fsanitize=cfi
# clang++ 4.0 via mason with -flto and -fsanitize=cfi
- os: linux
compiler: "clang++-39-mason"
env: CXX=clang++-3.9 CXXFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" LDFLAGS="-flto -fsanitize=cfi -fvisibility=hidden"
compiler: "clang++-40-mason"
env: CXX=clang++-4.0 CXXFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" LDFLAGS="-flto -fsanitize=cfi -fvisibility=hidden"
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_install:
- git submodule update --init
- ./.mason/mason install clang++ 3.9.1
- export PATH=$(./.mason/mason prefix clang++ 3.9.1)/bin:${PATH}
- ./.mason/mason install clang++ 4.0.1
- export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH}
- ./.mason/mason install binutils 2.27
- export PATH=$(./.mason/mason prefix binutils 2.27)/bin:${PATH}
# clang++ 3.9 via mason with -fsanitize=address
# clang++ 4.0 via mason with -fsanitize=address
- os: linux
compiler: "clang++-39-mason"
env: CXX=clang++-3.9 CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer -fno-common" LDFLAGS="-fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1
compiler: "clang++-40-mason"
env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer -fno-common" LDFLAGS="-fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1
sudo: required
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_install:
- git submodule update --init
- ./.mason/mason install clang++ 3.9.1
- export PATH=$(./.mason/mason prefix clang++ 3.9.1)/bin:${PATH}
# clang++ 3.9 via mason with -fsanitize=undefined
- ./.mason/mason install clang++ 4.0.1
- export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH}
# clang++ 4.0 via mason with -fsanitize=undefined
- os: linux
compiler: "clang++-39-mason"
env: CXX=clang++-3.9 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined"
compiler: "clang++-40-mason"
env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined"
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_install:
- git submodule update --init
- ./.mason/mason install clang++ 3.9.1
- export PATH=$(./.mason/mason prefix clang++ 3.9.1)/bin:${PATH}
# clang++ 3.9 via mason with -fsanitize=integer
- ./.mason/mason install clang++ 4.0.1
- export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH}
# clang++ 4.0 via mason with -fsanitize=integer
- os: linux
compiler: "clang++-39-mason"
env: CXX=clang++-3.9 CXXFLAGS="-fsanitize=integer" LDFLAGS="-fsanitize=integer"
compiler: "clang++-40-mason"
env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=integer" LDFLAGS="-fsanitize=integer"
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_install:
- git submodule update --init
- ./.mason/mason install clang++ 3.9.1
- export PATH=$(./.mason/mason prefix clang++ 3.9.1)/bin:${PATH}
# clang++ 3.9 via mason with -fsanitize=safe-stack
- ./.mason/mason install clang++ 4.0.1
- export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH}
# clang++ 4.0 via mason with -fsanitize=safe-stack
- os: linux
compiler: "clang++-39-mason"
env: CXX=clang++-3.9 CXXFLAGS="-fsanitize=safe-stack" LDFLAGS="-fsanitize=safe-stack"
compiler: "clang++-40-mason"
env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=safe-stack" LDFLAGS="-fsanitize=safe-stack"
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_install:
- git submodule update --init
- ./.mason/mason install clang++ 3.9.1
- export PATH=$(./.mason/mason prefix clang++ 3.9.1)/bin:${PATH}
- ./.mason/mason install clang++ 4.0.1
- export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH}
- os: osx
osx_image: xcode8
env: OSX_OLDEST_SUPPORTED=10.7 TEST_GYP_BUILD=True
......@@ -166,5 +167,5 @@ after_script:
make coverage;
./out/cov-test;
cp unit*gc* test/;
./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp;
./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp variant_cast.hpp;
fi
This diff is collapsed.
# Unofficial and incomplete build file using Boost build system.
# You should use make unless you know what you are doing.
local BOOST_DIR = "/usr/local" ;
import os ;
#using clang : : ;
lib system : : <name>boost_system <search>$(BOOST_DIR)/lib ;
lib timer : chrono : <name>boost_timer <search>$(BOOST_DIR)/lib ;
lib chrono : system : <name>boost_chrono <search>$(BOOST_DIR)/lib ;
exe variant-test
:
test/bench_variant.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./include
<include>./test/include
#<define>SINGLE_THREADED
<variant>release:<cxxflags>"-march=native -Wweak-vtables"
;
local boost_dir = [ os.environ BOOST_DIR ] ;
if ! $(boost_dir)
{
boost_dir = "/usr/local" ;
}
#using clang : : ;
exe binary-visitor-test
:
test/binary_visitor_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./include
<include>./test/include
local cxx_std = [ os.environ CXX_STD ] ;
if ! $(cxx_std)
{
cxx_std = c++11 ;
}
project mapbox_variant
: requirements
<cxxflags>-std=$(cxx_std)
<include>$(boost_dir)/include
<include>include
<include>test/include
<variant>release:<cxxflags>-march=native
<threading>single:<define>SINGLE_THREADED
: default-build
<variant>release
<optimization>speed
<threading>single
;
exe recursive-wrapper-test
:
test/recursive_wrapper_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./include
<include>./test/include
<variant>release:<cxxflags>-march=native
rule exe-test ( name : reqs * : deps * )
{
exe $(name)
: test/$(name).cpp
: $(reqs)
: $(deps)
;
explicit $(name) ;
}
exe unique-ptr-test
:
test/unique_ptr_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./include
<include>./test/include
<variant>release:<cxxflags>-march=native
exe-test bench_variant
: <variant>release:<cxxflags>-Wweak-vtables
;
exe reference_wrapper_test
:
test/reference_wrapper_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./include
<include>./test/include
<variant>release:<cxxflags>-march=native
exe-test binary_visitor_test ;
exe-test recursive_wrapper_test ;
exe-test unique_ptr_test ;
exe-test reference_wrapper_test ;
exe-test lambda_overload_test ;
exe-test hashable_test ;
install out
: bench_variant
binary_visitor_test
unique_ptr_test
reference_wrapper_test
lambda_overload_test
hashable_test
;
......@@ -99,7 +99,7 @@ out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS)
mkdir -p ./out
$(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS)
out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/issue122.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o
out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/issue122.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o out/variant_alternative.o out/nothrow_move.o
mkdir -p ./out
$(CXX) -o $@ $^ $(LDFLAGS)
......@@ -112,10 +112,10 @@ coverage:
sizes: Makefile
mkdir -p ./out
@$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(FINAL_CXXFLAGS) && du -h ./out/our_variant_hello_world.out
@$(CXX) -o ./out/boost_variant_hello_world.out $(BOOST_ROOT)/include/boost/variant.hpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && du -h ./out/boost_variant_hello_world.out
@$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) && du -h ./out/our_variant_hello_world
@$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && du -h ./out/boost_variant_hello_world
@$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world.out
@$(CXX) -o ./out/boost_variant_hello_world.out $(BOOST_ROOT)/include/boost/variant.hpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world.out
@$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world
@$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world
profile: out/bench-variant-debug
mkdir -p profiling/
......
......@@ -9,13 +9,13 @@ An header-only alternative to `boost::variant` for C++11 and C++14
## 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:
......@@ -69,12 +69,10 @@ 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; };
......@@ -111,7 +109,7 @@ struct Object {
};
```
For walkig the JSON representation you can again either create a `JSONVisitor`:
For walking the JSON representation you can again either create a `JSONVisitor`:
```c++
struct JSONVisitor {
......@@ -146,6 +144,7 @@ struct Node {
uint64_t value;
}
```
### Advanced Usage Tips
Creating type aliases for variants is a great way to reduce repetition.
......@@ -164,7 +163,6 @@ struct APIResult : variant<Error, Result> {
}
```
## Why use Mapbox Variant?
Mapbox variant has the same speedy performance of `boost::variant` but is
......@@ -180,7 +178,6 @@ Time to compile header | 185 ms | 675 ms
(Numbers from an older version of Mapbox variant.)
## Goals
Mapbox `variant` has been a very valuable, lightweight alternative for apps
......@@ -206,7 +203,6 @@ Want to know more about the upcoming standard? Have a look at our
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
- Compiler supporting `-std=c++11` or `-std=c++14`
......@@ -224,35 +220,29 @@ Tested with:
- clang++-3.9
- Visual Studio 2015
## Unit Tests
On Unix systems compile and run the unit tests with `make test`.
On Windows run `scripts/build-local.bat`.
## Limitations
* The `variant` can not hold references (something like `variant<int&>` is
- The `variant` can not hold references (something like `variant<int&>` is
not possible). You might want to try `std::reference_wrapper` instead.
## Deprecations
* The included implementation of `optional` is deprecated and will be removed
in a future version. See https://github.com/mapbox/variant/issues/64.
* Old versions of the code needed visitors to derive from `static_visitor`.
- The included implementation of `optional` is deprecated and will be removed
in a future version. See [issue #64](https://github.com/mapbox/variant/issues/64).
- Old versions of the code needed visitors to derive from `static_visitor`.
This is not needed any more and marked as deprecated. The `static_visitor`
class will be removed in future versions.
## Benchmarks
make bench
## Check object sizes
make sizes /path/to/boost/variant.hpp
mapbox-variant (1.1.5-4) UNRELEASED; urgency=medium
mapbox-variant (1.1.6-1~exp1) experimental; urgency=medium
* New upstream release.
* Bump Standards-Version to 4.3.0, no changes.
* Update watch file to limit matches to archive path.
-- Bas Couwenberg <sebastic@debian.org> Sun, 05 Aug 2018 20:26:23 +0200
-- Bas Couwenberg <sebastic@debian.org> Thu, 25 Apr 2019 17:54:02 +0200
mapbox-variant (1.1.5-3) unstable; urgency=medium
......
......@@ -11,6 +11,7 @@
#include <typeinfo>
#include <utility>
#include <functional>
#include <limits>
#include <mapbox/recursive_wrapper.hpp>
#include <mapbox/variant_visitor.hpp>
......@@ -74,19 +75,19 @@ public:
}; // class bad_variant_access
template <typename R = void>
struct MAPBOX_VARIANT_DEPRECATED static_visitor
{
using result_type = R;
protected:
static_visitor() {}
~static_visitor() {}
};
#if !defined(MAPBOX_VARIANT_MINIMIZE_SIZE)
using type_index_t = unsigned int;
#else
#if defined(MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED)
using type_index_t = std::uint_fast8_t;
#else
using type_index_t = std::uint_least8_t;
#endif
#endif
namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
static constexpr type_index_t invalid_value = type_index_t(-1);
template <typename T, typename... Types>
struct direct_type;
......@@ -94,7 +95,7 @@ struct direct_type;
template <typename T, typename First, typename... Types>
struct direct_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_same<T, First>::value
static constexpr type_index_t index = std::is_same<T, First>::value
? sizeof...(Types)
: direct_type<T, Types...>::index;
};
......@@ -102,7 +103,7 @@ struct direct_type<T, First, Types...>
template <typename T>
struct direct_type<T>
{
static constexpr std::size_t index = invalid_value;
static constexpr type_index_t index = invalid_value;
};
#if __cpp_lib_logical_traits >= 201510L
......@@ -144,7 +145,7 @@ struct convertible_type;
template <typename T, typename First, typename... Types>
struct convertible_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_convertible<T, First>::value
static constexpr type_index_t index = std::is_convertible<T, First>::value
? disjunction<std::is_convertible<T, Types>...>::value ? invalid_value : sizeof...(Types)
: convertible_type<T, Types...>::index;
};
......@@ -152,18 +153,21 @@ struct convertible_type<T, First, Types...>
template <typename T>
struct convertible_type<T>
{
static constexpr std::size_t index = invalid_value;
static constexpr type_index_t index = invalid_value;
};
template <typename T, typename... Types>
struct value_traits
{
using value_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
using value_type_wrapper = recursive_wrapper<value_type>;
static constexpr type_index_t direct_index = direct_type<value_type, Types...>::index;
static constexpr bool is_direct = direct_index != invalid_value;
static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
static constexpr type_index_t index_direct_or_wrapper = is_direct ? direct_index : direct_type<value_type_wrapper, Types...>::index;
static constexpr bool is_direct_or_wrapper = index_direct_or_wrapper != invalid_value;
static constexpr type_index_t index = is_direct_or_wrapper ? index_direct_or_wrapper : convertible_type<value_type, Types...>::index;
static constexpr bool is_valid = index != invalid_value;
static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
static constexpr type_index_t tindex = is_valid ? sizeof...(Types)-index : 0;
using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
};
......@@ -197,19 +201,19 @@ struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_t
using type = typename F::result_type;
};
template <std::size_t arg1, std::size_t... others>
template <type_index_t arg1, type_index_t... others>
struct static_max;
template <std::size_t arg>
template <type_index_t arg>
struct static_max<arg>
{
static const std::size_t value = arg;
static const type_index_t value = arg;
};
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
template <type_index_t arg1, type_index_t arg2, type_index_t... others>
struct static_max<arg1, arg2, others...>
{
static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
static const type_index_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
};
template <typename... Types>
......@@ -218,7 +222,7 @@ struct variant_helper;
template <typename T, typename... Types>
struct variant_helper<T, Types...>
{
VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
VARIANT_INLINE static void destroy(const type_index_t type_index, void* data)
{
if (type_index == sizeof...(Types))
{
......@@ -230,7 +234,7 @@ struct variant_helper<T, Types...>
}
}
VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
VARIANT_INLINE static void move(const type_index_t old_type_index, void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
......@@ -242,7 +246,7 @@ struct variant_helper<T, Types...>
}
}
VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
VARIANT_INLINE static void copy(const type_index_t old_type_index, const void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
......@@ -258,9 +262,9 @@ struct variant_helper<T, Types...>
template <>
struct variant_helper<>
{
VARIANT_INLINE static void destroy(const std::size_t, void*) {}
VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
VARIANT_INLINE static void destroy(const type_index_t, void*) {}
VARIANT_INLINE static void move(const type_index_t, void*, void*) {}
VARIANT_INLINE static void copy(const type_index_t, const void*, void*) {}
};
template <typename T>
......@@ -558,16 +562,15 @@ struct hasher
} // namespace detail
struct no_init
{
};
struct no_init {};
template <typename... Types>
class variant
{
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty.");
static_assert(!detail::disjunction<std::is_reference<Types>...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?");
static_assert(!detail::disjunction<std::is_array<Types>...>::value, "Variant can not hold array types.");
static_assert(sizeof...(Types) < std::numeric_limits<type_index_t>::max(), "Internal index type must be able to accommodate all alternatives.");
private:
static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;
......@@ -579,14 +582,18 @@ private:
using data_type = typename std::aligned_storage<data_size, data_align>::type;
using helper_type = detail::variant_helper<Types...>;
std::size_t type_index;
type_index_t type_index;
#ifdef __clang_analyzer__
data_type data {};
#else
data_type data;
#endif
public:
VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
: type_index(sizeof...(Types)-1)
{
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant.");
new (&data) first_type();
}
......@@ -634,6 +641,9 @@ private:
public:
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
// note we check for nothrow-constructible, not nothrow-assignable, since
// move_assign uses move-construction via placement new.
noexcept(detail::conjunction<std::is_nothrow_move_constructible<Types>...>::value)
{
move_assign(std::move(other));
return *this;
......@@ -647,8 +657,13 @@ public:
// conversions
// move-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
template <typename T, typename Traits = detail::value_traits<T, Types...>,
typename Enable = typename std::enable_if<Traits::is_valid && !std::is_same<variant<Types...>, typename Traits::value_type>::value>::type >
VARIANT_INLINE variant<Types...>& operator=(T&& rhs)
// not that we check is_nothrow_constructible<T>, not is_nothrow_move_assignable<T>,
// since we construct a temporary
noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value
&& std::is_nothrow_move_assignable<variant<Types...>>::value)
{
variant<Types...> temp(std::forward<T>(rhs));
move_assign(std::move(temp));
......@@ -838,7 +853,7 @@ public:
// This function is deprecated because it returns an internal index field.
// Use which() instead.
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE type_index_t get_type_index() const
{
return type_index;
}
......@@ -1010,6 +1025,78 @@ ResultType const& get_unchecked(T const& var)
{
return var.template get_unchecked<ResultType>();
}
// variant_size
template <typename T>
struct variant_size;
//variable templates is c++14
//template <typename T>
//constexpr std::size_t variant_size_v = variant_size<T>::value;
template <typename T>
struct variant_size<const T>
: variant_size<T> {};
template <typename T>
struct variant_size<volatile T>
: variant_size<T> {};
template <typename T>
struct variant_size<const volatile T>
: variant_size<T> {};
template <typename... Types>
struct variant_size<variant<Types...>>
: std::integral_constant<std::size_t, sizeof...(Types)> {};
// variant_alternative
template <std::size_t Index, typename T>
struct variant_alternative;
#if defined(__clang__)
#if __has_builtin(__type_pack_element)
#define has_type_pack_element
#endif
#endif
#if defined(has_type_pack_element)
template <std::size_t Index, typename ...Types>
struct variant_alternative<Index, variant<Types...>>
{
static_assert(sizeof...(Types) > Index , "Index out of range");
using type = __type_pack_element<Index, Types...>;
};
#else
template <std::size_t Index, typename First, typename...Types>
struct variant_alternative<Index, variant<First, Types...>>
: variant_alternative<Index - 1, variant<Types...>>
{
static_assert(sizeof...(Types) > Index -1 , "Index out of range");
};
template <typename First, typename...Types>
struct variant_alternative<0, variant<First, Types...>>
{
using type = First;
};
#endif
template <size_t Index, typename T>
using variant_alternative_t = typename variant_alternative<Index, T>::type;
template <size_t Index, typename T>
struct variant_alternative<Index, const T>
: std::add_const<variant_alternative<Index, T>> {};
template <size_t Index, typename T>
struct variant_alternative<Index, volatile T>
: std::add_volatile<variant_alternative<Index, T>> {};
template <size_t Index, typename T>
struct variant_alternative<Index, const volatile T>
: std::add_cv<variant_alternative<Index, T>> {};
} // namespace util
} // namespace mapbox
......@@ -1022,6 +1109,7 @@ struct hash< ::mapbox::util::variant<Types...>> {
return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v);
}
};
}
#endif // MAPBOX_UTIL_VARIANT_HPP
#ifndef VARIANT_CAST_HPP
#define VARIANT_CAST_HPP
#include <type_traits>
namespace mapbox {
namespace util {
namespace detail {
template <class T>
class static_caster
{
public:
template <class V>
T& operator()(V& v) const
{
return static_cast<T&>(v);
}
};
template <class T>
class dynamic_caster
{
public:
using result_type = T&;
template <class V>
T& operator()(V& v, typename std::enable_if<!std::is_polymorphic<V>::value>::type* = nullptr) const
{
throw std::bad_cast();
}
template <class V>
T& operator()(V& v, typename std::enable_if<std::is_polymorphic<V>::value>::type* = nullptr) const
{
return dynamic_cast<T&>(v);
}
};
template <class T>
class dynamic_caster<T*>
{
public:
using result_type = T*;
template <class V>
T* operator()(V& v, typename std::enable_if<!std::is_polymorphic<V>::value>::type* = nullptr) const
{
return nullptr;
}
template <class V>
T* operator()(V& v, typename std::enable_if<std::is_polymorphic<V>::value>::type* = nullptr) const
{
return dynamic_cast<T*>(&v);
}
};
}
template <class T, class V>
typename detail::dynamic_caster<T>::result_type
dynamic_variant_cast(V& v)
{
return mapbox::util::apply_visitor(detail::dynamic_caster<T>(), v);
}
template <class T, class V>
typename detail::dynamic_caster<const T>::result_type
dynamic_variant_cast(const V& v)
{
return mapbox::util::apply_visitor(detail::dynamic_caster<const T>(), v);
}
template <class T, class V>
T& static_variant_cast(V& v)
{
return mapbox::util::apply_visitor(detail::static_caster<T>(), v);
}
template <class T, class V>
const T& static_variant_cast(const V& v)
{
return mapbox::util::apply_visitor(detail::static_caster<const T>(), v);
}
}
}
#endif // VARIANT_CAST_HPP
#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP
#define MAPBOX_UTIL_VARIANT_VISITOR_HPP
#include <utility>
namespace mapbox {
namespace util {
......@@ -10,26 +12,29 @@ struct visitor;
template <typename Fn>
struct visitor<Fn> : Fn
{
using type = Fn;
using Fn::operator();
visitor(Fn fn) : Fn(fn) {}
template<typename T>
visitor(T&& fn) : Fn(std::forward<T>(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 T, typename... Ts>
visitor(T&& fn, Ts&&... fns)
: Fn(std::forward<T>(fn))
, visitor<Fns...>(std::forward<Ts>(fns)...) {}
};
template <typename... Fns>
visitor<Fns...> make_visitor(Fns... fns)
visitor<typename std::decay<Fns>::type...> make_visitor(Fns&&... fns)
{
return visitor<Fns...>(fns...);
return visitor<typename std::decay<Fns>::type...>
(std::forward<Fns>(fns)...);
}
} // namespace util
......
{
"name": "variant",
"version": "1.3.0",
"version": "1.1.6",
"description": "C++11/C++14 variant",
"main": "./package.json",
"repository" : {
......
This diff is collapsed.
......@@ -85,6 +85,9 @@ void test_match_singleton()
{
variant<int> singleton = 5;
singleton.match([](int) {});
auto lambda = [](int) {};
singleton.match(lambda);
}
void test_match_overloads()
......@@ -112,6 +115,84 @@ void test_match_overloads_capture()
std::cout << "Got " << ok << " ok, " << err << " err" << std::endl;
}
struct MovableOnly
{
MovableOnly() = default;
MovableOnly(MovableOnly&&) = default;
MovableOnly& operator=(MovableOnly&&) = default;
};
struct MovableCopyable
{
MovableCopyable() = default;
MovableCopyable(MovableCopyable&&) = default;
MovableCopyable& operator=(MovableCopyable&&) = default;
MovableCopyable(const MovableCopyable&) = default;
MovableCopyable& operator=(const MovableCopyable&) = default;
};
void test_match_overloads_init_capture()
#ifdef HAS_CPP14_SUPPORT
{
Either<Error, Response> rv;
rv = Error{};
rv.match([p = MovableOnly{}](auto&&) {});
{
auto lambda = [p = MovableCopyable{}](auto&&) {};
rv.match(lambda);
rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; },
[p = MovableOnly{}](Error) { std::cout << "Error\n"; });
}
{
auto lambda = [](Error) { std::cout << "Error\n"; };
rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; },
lambda);
rv.match(lambda,
[p = MovableOnly{}](Response) { std::cout << "Response\n"; });
}
}
#else
{
}
#endif
// See #140
void test_match_overloads_otherwise()
#ifdef HAS_CPP14_SUPPORT
{
struct Center
{
};
struct Indent
{
};
struct Justify
{
};
struct None
{
};
using Properties = mapbox::util::variant<Center, Indent, Justify, None>;
Properties props = Justify{};
props.match([&](Center) { std::cout << "Center\n"; }, //
[&](Indent) { std::cout << "Indent\n"; }, //
[&](auto&&) { std::cout << "Otherwise\n"; }); //
}
#else
{
}
#endif
int main()
{
test_lambda_overloads();
......@@ -122,6 +203,8 @@ int main()
test_match_singleton();
test_match_overloads();
test_match_overloads_capture();
test_match_overloads_init_capture();
test_match_overloads_otherwise();
}
#undef HAS_CPP14_SUPPORT
#include <cstdlib>
#include <iostream>
#include <string>
......@@ -64,12 +63,12 @@ struct calculator
int operator()(binary_op<add> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) + util::apply_visitor(calculator(), binary.right);
return util::apply_visitor(*this, binary.left) + util::apply_visitor(*this, binary.right);
}
int operator()(binary_op<sub> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) - util::apply_visitor(calculator(), binary.right);
return util::apply_visitor(*this, binary.left) - util::apply_visitor(*this, binary.right);
}
};
......@@ -83,12 +82,12 @@ struct to_string
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);
return util::apply_visitor(*this, binary.left) + std::string("+") + util::apply_visitor(*this, 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);
return util::apply_visitor(*this, binary.left) + std::string("-") + util::apply_visitor(*this, binary.right);
}
};
......
......@@ -46,9 +46,7 @@ TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]")
variant_type v = obj;
REQUIRE(v.is<t1>());
REQUIRE(v.get<t1>().value == 42);
REQUIRE_THROWS({
v.set<t2>(13);
});
REQUIRE_THROWS(v.set<t2>(13));
}
REQUIRE(count == 0);
}
#include <typeinfo>
#include <utility>
#include <mapbox/variant.hpp>
using namespace mapbox;
namespace test {
struct t_noexcept_true_1 {
t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default;
t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default;
};
struct t_noexcept_true_2 {
t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default;
t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default;
};
struct t_noexcept_false_1 {
t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {}
t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; }
};
using should_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
static_assert(std::is_nothrow_move_assignable<should_be_no_throw_copyable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");
using should_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
static_assert(std::is_nothrow_move_constructible<should_be_no_throw_assignable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");
using should_not_be_no_throw_copyable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
static_assert(not std::is_nothrow_move_assignable<should_not_be_no_throw_copyable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");
using should_not_be_no_throw_assignable = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
static_assert(not std::is_nothrow_move_constructible<should_not_be_no_throw_assignable>::value,
"variants with no-throw move assignable types should be "
"no-throw move nothrow assignable");
// this type cannot be nothrow converted from either of its types, even the nothrow moveable one,
// because the conversion operator moves the whole variant.
using convertable_test_type = util::variant<t_noexcept_true_1, t_noexcept_false_1>;
// this type can be nothrow converted from either of its types.
using convertable_test_type_2 = util::variant<t_noexcept_true_1, t_noexcept_true_2>;
static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_true_1>::value,
"variants with noexcept(true) move constructible types should be nothrow-convertible "
"from those types only IF the variant itself is nothrow_move_assignable");
static_assert(not std::is_nothrow_assignable<convertable_test_type, t_noexcept_false_1>::value,
"variants with noexcept(false) move constructible types should not be nothrow-convertible "
"from those types");
static_assert(std::is_nothrow_assignable<convertable_test_type_2, t_noexcept_true_2>::value,
"variants with noexcept(true) move constructible types should be nothrow-convertible "
"from those types only IF the variant itself is nothrow_move_assignable");
} // namespace test
#include "catch.hpp"
#include <mapbox/variant.hpp>
#include <mapbox/recursive_wrapper.hpp>
#include <type_traits>
......@@ -155,4 +155,31 @@ TEST_CASE("recursive wrapper of pair<int, int>")
REQUIRE(b.get().second == 6);
//REQUIRE(c.get_pointer() == nullptr);
}
SECTION("Multiple recurssive wrappers of polymorphic types")
{
// https://github.com/mapbox/variant/issues/146
// (Visual Studio 2015 update 3)
using namespace mapbox::util;
struct Base;
struct Derived;
using Variant = variant<recursive_wrapper<Base>, recursive_wrapper<Derived>>;
struct Base { };
struct Derived : public Base { };
{
Base base;
Derived derived;
Variant v;
v = base;
v = derived; // compile error prior https://github.com/mapbox/variant/pull/147
CHECK(v.is<Derived>());
}
{
Derived derived;
Variant v(derived); // compile error prior https://github.com/mapbox/variant/pull/147
CHECK(v.is<Derived>());
}
}
}
#include <algorithm>
#include <cstddef>
#include <cstdint>
......@@ -15,7 +14,7 @@ struct some_struct
std::string c;
};
using variant_internal_index_type = size_t;
using variant_internal_index_type = mapbox::util::type_index_t;
TEST_CASE("size of variants")
{
......
......@@ -208,9 +208,7 @@ TEST_CASE("get with wrong type (here: double) should throw", "[variant]")
REQUIRE(var.is<int>());
REQUIRE_FALSE(var.is<double>());
REQUIRE(var.get<int>() == 5);
REQUIRE_THROWS_AS({
var.get<double>();
},
REQUIRE_THROWS_AS(var.get<double>(),
mapbox::util::bad_variant_access&);
}
......@@ -222,13 +220,9 @@ TEST_CASE("get with wrong type (here: int) should throw", "[variant]")
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>();
},
REQUIRE_THROWS_AS(var.get<int>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(var);
},
REQUIRE_THROWS_AS(mapbox::util::get<int>(var),
mapbox::util::bad_variant_access&);
}
......@@ -240,26 +234,18 @@ TEST_CASE("get on const varint with wrong type (here: int) should throw", "[vari
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>();
},
REQUIRE_THROWS_AS(var.get<int>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(var);
},
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>();
},
REQUIRE_THROWS_AS(var.get<int>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
var.get<double>();
},
REQUIRE_THROWS_AS(var.get<double>(),
mapbox::util::bad_variant_access&);
}
......@@ -273,16 +259,12 @@ TEST_CASE("no_init variant can be copied and moved from", "[variant]")
REQUIRE(v2.get<int>() == 42);
v2 = v1;
REQUIRE_THROWS_AS({
v2.get<int>();
},
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>();
},
REQUIRE_THROWS_AS(v3.get<int>(),
mapbox::util::bad_variant_access&);
}
......@@ -294,9 +276,7 @@ TEST_CASE("no_init variant can be copied and moved to", "[variant]")
variant_type v2{mapbox::util::no_init()};
variant_type v3{mapbox::util::no_init()};
REQUIRE_THROWS_AS({
v2.get<int>();
},
REQUIRE_THROWS_AS(v2.get<int>(),
mapbox::util::bad_variant_access&);
REQUIRE(v1.get<int>() == 42);
......@@ -304,9 +284,7 @@ TEST_CASE("no_init variant can be copied and moved to", "[variant]")
REQUIRE(v2.get<int>() == 42);
REQUIRE(v1.get<int>() == 42);
REQUIRE_THROWS_AS({
v3.get<int>();
},
REQUIRE_THROWS_AS(v3.get<int>(),
mapbox::util::bad_variant_access&);
v3 = std::move(v1);
......@@ -327,9 +305,7 @@ TEST_CASE("implicit conversion to first type in variant type list", "[variant][i
using variant_type = mapbox::util::variant<long, char>;
variant_type var = 5l; // converted to long
REQUIRE(var.get<long>() == 5);
REQUIRE_THROWS_AS({
var.get<char>();
},
REQUIRE_THROWS_AS(var.get<char>(),
mapbox::util::bad_variant_access&);
}
......@@ -498,13 +474,9 @@ TEST_CASE("storing reference wrappers works")
variant_type v{std::ref(a)};
REQUIRE(v.get<int>() == 1);
REQUIRE(mapbox::util::get<int>(v) == 1);
REQUIRE_THROWS_AS({
v.get<double>();
},
REQUIRE_THROWS_AS(v.get<double>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double>(v);
},
REQUIRE_THROWS_AS(mapbox::util::get<double>(v),
mapbox::util::bad_variant_access&);
a = 2;
REQUIRE(v.get<int>() == 2);
......@@ -515,13 +487,9 @@ TEST_CASE("storing reference wrappers works")
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>();
},
REQUIRE_THROWS_AS(v.get<int>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(v);
},
REQUIRE_THROWS_AS(mapbox::util::get<int>(v),
mapbox::util::bad_variant_access&);
b = 2.718;
REQUIRE(v.get<double>() == Approx(2.718));
......@@ -530,9 +498,7 @@ TEST_CASE("storing reference wrappers works")
v.get<double>() = 4.1;
REQUIRE(b == Approx(4.1));
REQUIRE_THROWS_AS({
v.get<int>() = 4;
},
REQUIRE_THROWS_AS(v.get<int>() = 4,
mapbox::util::bad_variant_access&);
}
......@@ -546,26 +512,18 @@ TEST_CASE("storing reference wrappers to consts works")
REQUIRE(v.get<int>() == 1);
REQUIRE(mapbox::util::get<int const>(v) == 1);
REQUIRE(mapbox::util::get<int>(v) == 1);
REQUIRE_THROWS_AS({
v.get<double const>();
},
REQUIRE_THROWS_AS(v.get<double const>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double const>(v);
},
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>();
},
REQUIRE_THROWS_AS(v.get<int const>(),
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int const>(v);
},
REQUIRE_THROWS_AS(mapbox::util::get<int const>(v),
mapbox::util::bad_variant_access&);
}
......
#include "catch.hpp"
#include <mapbox/variant.hpp>
#include <mapbox/variant_io.hpp>
#include <string>
TEST_CASE("variant_alternative", "[types]")
{
using variant_type = mapbox::util::variant<int, double, std::string>;
using type_0 = mapbox::util::variant_alternative<0, variant_type>::type;
using type_1 = mapbox::util::variant_alternative<1, variant_type>::type;
using type_2 = mapbox::util::variant_alternative<2, variant_type>::type;
//using type_3 = mapbox::util::variant_alternative<3, variant_type>::type; // compile error
constexpr bool check_0 = std::is_same<int, type_0>::value;
constexpr bool check_1 = std::is_same<double, type_1>::value;
constexpr bool check_2 = std::is_same<std::string, type_2>::value;
CHECK(check_0);
CHECK(check_1);
CHECK(check_2);
}
TEST_CASE("variant_size", "[types]")
{
constexpr auto value_0 = mapbox::util::variant_size<mapbox::util::variant<>>::value;
constexpr auto value_1 = mapbox::util::variant_size<mapbox::util::variant<int>>::value;
constexpr auto value_2 = mapbox::util::variant_size<mapbox::util::variant<int, std::string>>::value;
CHECK(value_0 == 0);
CHECK(value_1 == 1);
CHECK(value_2 == 2);
}