mirror of
https://github.com/boostorg/variant2.git
synced 2025-06-25 03:51:34 +02:00
Compare commits
49 Commits
pre-review
...
feature/ad
Author | SHA1 | Date | |
---|---|---|---|
58b4a21deb | |||
5192a345ab | |||
74e6a04a60 | |||
2af66dd815 | |||
a15364bd81 | |||
12758c076d | |||
a60f432b64 | |||
bc73c4b822 | |||
566bde5fcc | |||
2e831fbdb2 | |||
2738f081cd | |||
f5d9662946 | |||
adeb970be6 | |||
6b617ae3a8 | |||
04f655e76a | |||
c4f7f2d63a | |||
504abbe8eb | |||
8cb1789167 | |||
69b25cb42a | |||
6b3a2b2b4d | |||
2d990f774a | |||
a083667a96 | |||
e686dab3ea | |||
3041fff85c | |||
cacd99d69d | |||
64ea067c79 | |||
6390b5ed20 | |||
8691721a9c | |||
7f7c74522b | |||
c3f9beaadc | |||
475ad691d6 | |||
5011dd8e1c | |||
f2980f97fc | |||
c8ebfd0481 | |||
6f190133be | |||
c138488551 | |||
6b69a1deca | |||
70fbd17be9 | |||
e8e56139a8 | |||
2d6385031e | |||
9f7e525984 | |||
2e8ae0c796 | |||
89986c7634 | |||
1da3892239 | |||
3808c9b122 | |||
f1b1e7cade | |||
6d7be90ce0 | |||
9875cfdc98 | |||
688849f9e8 |
95
.travis.yml
95
.travis.yml
@ -6,7 +6,7 @@ language: cpp
|
||||
|
||||
sudo: false
|
||||
|
||||
python: "2.7"
|
||||
dist: xenial
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -78,6 +78,16 @@ matrix:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- os: linux
|
||||
compiler: g++-9
|
||||
env: TOOLSET=gcc COMPILER=g++-9 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- os: linux
|
||||
compiler: g++-8
|
||||
env: UBSAN=1 TOOLSET=gcc COMPILER=g++-8 CXXSTD=11,14,17,2a UBSAN_OPTIONS=print_stacktrace=1 LINKFLAGS=-fuse-ld=gold
|
||||
@ -98,7 +108,6 @@ matrix:
|
||||
- libstdc++-4.9-dev
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.5
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.6
|
||||
@ -109,7 +118,6 @@ matrix:
|
||||
- clang-3.6
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.6
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.7
|
||||
@ -120,7 +128,6 @@ matrix:
|
||||
- clang-3.7
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.7
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.8
|
||||
@ -131,7 +138,6 @@ matrix:
|
||||
- clang-3.8
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.8
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.9
|
||||
@ -142,7 +148,6 @@ matrix:
|
||||
- clang-3.9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.9
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-4.0
|
||||
@ -153,7 +158,6 @@ matrix:
|
||||
- clang-4.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-5.0
|
||||
@ -164,7 +168,6 @@ matrix:
|
||||
- clang-5.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-5.0
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-6.0
|
||||
@ -175,7 +178,6 @@ matrix:
|
||||
- clang-6.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-6.0
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-7
|
||||
@ -186,20 +188,45 @@ matrix:
|
||||
- clang-7
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-7
|
||||
- llvm-toolchain-xenial-7
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-6.0
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++-6.0 CXXSTD=11,14,17,2a UBSAN_OPTIONS=print_stacktrace=1
|
||||
compiler: clang++-8
|
||||
env: TOOLSET=clang COMPILER=clang++-8 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-6.0
|
||||
- clang-8
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-6.0
|
||||
- llvm-toolchain-xenial-8
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang++-9
|
||||
env: TOOLSET=clang COMPILER=clang++-9 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-8
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++-8 CXXSTD=11,14,17,2a UBSAN_OPTIONS=print_stacktrace=1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-8
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-xenial-8
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: clang++-libc++
|
||||
env: TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,1z
|
||||
addons:
|
||||
@ -208,6 +235,7 @@ matrix:
|
||||
- libc++-dev
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: clang++-libc++
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,1z UBSAN_OPTIONS=print_stacktrace=1
|
||||
addons:
|
||||
@ -219,13 +247,48 @@ matrix:
|
||||
compiler: clang++
|
||||
env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z
|
||||
|
||||
- os: osx
|
||||
compiler: clang++
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z UBSAN_OPTIONS=print_stacktrace=1
|
||||
|
||||
- os: linux
|
||||
env: CMAKE_TEST=1
|
||||
script:
|
||||
- mkdir __build__ && cd __build__
|
||||
- cmake -DBOOST_ENABLE_CMAKE=1 -DBoost_VERBOSE=1 -DBOOST_INCLUDE_LIBRARIES=variant2 ..
|
||||
- ctest --output-on-failure -R boost_variant2
|
||||
|
||||
- os: linux
|
||||
compiler: g++
|
||||
env: CMAKE_SUBDIR_TEST=1
|
||||
install:
|
||||
- BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true
|
||||
- git clone -b $BOOST_BRANCH https://github.com/boostorg/assert.git ../assert
|
||||
- git clone -b $BOOST_BRANCH https://github.com/boostorg/config.git ../config
|
||||
- git clone -b $BOOST_BRANCH https://github.com/boostorg/core.git ../core
|
||||
- git clone -b $BOOST_BRANCH https://github.com/boostorg/mp11.git ../mp11
|
||||
script:
|
||||
- cd test/cmake_subdir_test && mkdir __build__ && cd __build__
|
||||
- cmake ..
|
||||
- cmake --build .
|
||||
- cmake --build . --target check
|
||||
|
||||
- os: linux
|
||||
env: CMAKE_INSTALL_TEST=1
|
||||
script:
|
||||
- mkdir __build__ && cd __build__
|
||||
- cmake -DBOOST_ENABLE_CMAKE=1 -DBoost_VERBOSE=1 -DBOOST_INCLUDE_LIBRARIES="variant2;config;mp11" -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
- cmake --build . --target install
|
||||
- cd ../libs/variant2/test/cmake_install_test && mkdir __build__ && cd __build__
|
||||
- cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
- cmake --build .
|
||||
- cmake --build . --target check
|
||||
|
||||
install:
|
||||
- BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true
|
||||
- cd ..
|
||||
- git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
- cd boost-root
|
||||
- git submodule update --init tools/build
|
||||
- git submodule update --init libs/config
|
||||
- git submodule update --init tools/boostdep
|
||||
- mkdir -p libs/variant2
|
||||
- cp -r $TRAVIS_BUILD_DIR/* libs/variant2
|
||||
|
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2018, 2019 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
|
||||
project(boost_variant2 VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
add_library(boost_variant2 INTERFACE)
|
||||
add_library(Boost::variant2 ALIAS boost_variant2)
|
||||
|
||||
target_include_directories(boost_variant2 INTERFACE include)
|
||||
|
||||
target_link_libraries(boost_variant2
|
||||
INTERFACE
|
||||
Boost::config
|
||||
Boost::mp11
|
||||
)
|
||||
|
||||
if(BOOST_SUPERPROJECT_VERSION)
|
||||
|
||||
include(BoostInstall)
|
||||
boost_install(TARGETS boost_variant2 HEADER_DIRECTORY include/)
|
||||
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
endif()
|
61
README.md
61
README.md
@ -1,64 +1,21 @@
|
||||
# variant2
|
||||
|
||||
This repository contains a never-valueless C++11/14/17 implementation of
|
||||
[std::variant](http://en.cppreference.com/w/cpp/utility/variant) in
|
||||
[variant.hpp](include/boost/variant2/variant.hpp) and an implementation of
|
||||
`expected<T, E...>` in [expected.hpp](include/boost/variant2/expected.hpp)
|
||||
that is an extended version of `expected<T, E>` as proposed in
|
||||
[P0323R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0323r1.pdf)
|
||||
and the subsequent
|
||||
[D0323R2](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md).
|
||||
This repository contains a never-valueless, strong guarantee, C++11/14/17
|
||||
implementation of [std::variant](http://en.cppreference.com/w/cpp/utility/variant).
|
||||
See [the documentation](https://www.boost.org/libs/variant2)
|
||||
for more information.
|
||||
|
||||
The code requires [Boost.Mp11](https://github.com/boostorg/mp11) and
|
||||
Boost.Config.
|
||||
|
||||
The repository is intended to be placed into the `libs/variant2` directory of
|
||||
a Boost clone or release, but the header `variant.hpp` will also work
|
||||
[standalone](https://godbolt.org/z/CTZztA).
|
||||
The library is part of Boost, starting from release 1.71, but the header
|
||||
`variant.hpp` will also work [standalone](https://godbolt.org/z/nVUNKX).
|
||||
|
||||
Supported compilers:
|
||||
|
||||
* g++ 4.8 or later with `-std=c++11` or above
|
||||
* clang++ 3.5 or later with `-std=c++11` or above
|
||||
* Visual Studio 2015, 2017
|
||||
* Visual Studio 2015, 2017, 2019
|
||||
|
||||
Tested on [Travis](https://travis-ci.org/pdimov/variant2/) and
|
||||
[Appveyor](https://ci.appveyor.com/project/pdimov/variant2/).
|
||||
|
||||
## variant.hpp
|
||||
|
||||
The class `boost::variant2::variant<T...>` is an almost conforming
|
||||
implementation of `std::variant` with the following differences:
|
||||
|
||||
* A converting constructor from, e.g. `variant<int, float>` to
|
||||
`variant<float, double, int>` is provided as an extension;
|
||||
* The reverse operation, going from `variant<float, double, int>` to
|
||||
`variant<int, float>` is provided as the member function `subset<U...>`.
|
||||
(This operation can throw if the current state of the variant cannot be
|
||||
represented.)
|
||||
* `variant<T...>` is not trivial when all contained types are trivial.
|
||||
|
||||
To avoid going into a valueless-by-exception state, this implementation falls
|
||||
back to using double storage unless
|
||||
|
||||
* one of the alternatives is the type `monostate`,
|
||||
* one of the alternatives has a nonthrowing default constructor, or
|
||||
* all the contained types are nothrow move constructible.
|
||||
|
||||
If the first two bullets don't hold, but the third does, the variant uses
|
||||
single storage, but `emplace` constructs a temporary and moves it into place
|
||||
if the construction of the object can throw. In case this is undesirable, one
|
||||
can force `emplace` into always constructing in-place by adding `monostate` as
|
||||
one of the alternatives.
|
||||
|
||||
## expected.hpp
|
||||
|
||||
The class `boost::variant2::expected<T, E...>` represents the return type of
|
||||
an operation that may potentially fail. It contains either the expected result
|
||||
of type `T`, or a reason for the failure, of one of the error types in `E...`.
|
||||
Internally, this is stored as `variant<T, E...>`.
|
||||
|
||||
See [its documentation](doc/expected.md) for more information.
|
||||
|
||||
Note that, while `variant` is production quality, `expected` is still a work
|
||||
in progress and has no test suite yet.
|
||||
Tested on [Travis](https://travis-ci.org/boostorg/variant2/) and
|
||||
[Appveyor](https://ci.appveyor.com/project/pdimov/variant2-fkab9).
|
||||
|
1
doc/.gitignore
vendored
1
doc/.gitignore
vendored
@ -1 +1,2 @@
|
||||
/pdf/
|
||||
/html/
|
||||
|
305
doc/expected.md
305
doc/expected.md
@ -1,305 +0,0 @@
|
||||
# expected<T, E...>
|
||||
|
||||
## Description
|
||||
|
||||
The class `expected<T, E...>` presented here is an extended version of `expected<T, E>` as
|
||||
proposed in [P0323R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0323r1.pdf)
|
||||
and the subsequent [D0323R2](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md).
|
||||
|
||||
The main difference is that this class takes more than one error type, which makes it more
|
||||
flexible. One example of a type of the `expected` family, [`outcome<T>`](https://ned14.github.io/boost.outcome/),
|
||||
on failure can store either an error of type `std::error_code`, or an exception in the form of `std::exception_ptr`.
|
||||
This can be represented naturally in this implementation via `expected<T, std::error_code, std::exception_ptr>`.
|
||||
|
||||
In addition, libraries would generally differ in their choice of error types. It would be a
|
||||
common need in practice of having to combine the results of calling two different libraries,
|
||||
each with its own error type. Library 1 may use `lib1::error`:
|
||||
|
||||
namespace lib1
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
other_error
|
||||
};
|
||||
|
||||
expected<double, error> div( double x, double y );
|
||||
|
||||
} // namespace lib1
|
||||
|
||||
while Library 2 might define its own `lib2::error`:
|
||||
|
||||
namespace lib2
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
negative_logarithm
|
||||
};
|
||||
|
||||
expected<double, error> log( double x );
|
||||
|
||||
} // namespace lib2
|
||||
|
||||
In this proposal, combining the results of `lib1::div` and `lib2::log` can be achieved via
|
||||
simple composition:
|
||||
|
||||
expected<double, lib1::error, lib2::error> log_div_mul( double x, double y, double m )
|
||||
{
|
||||
auto r1 = lib1::div( x, y );
|
||||
if( !r1 ) return r1.unexpected();
|
||||
|
||||
auto r2 = lib2::log( r1.value() );
|
||||
if( !r2 ) return r2.unexpected();
|
||||
|
||||
return m * r2.value();
|
||||
}
|
||||
|
||||
An alternative approach that requires more effort is also supported:
|
||||
|
||||
enum class common_error
|
||||
{
|
||||
division_by_zero,
|
||||
negative_logarithm,
|
||||
other_error,
|
||||
unknown_error
|
||||
};
|
||||
|
||||
common_error make_common_error( lib1::error e );
|
||||
common_error make_common_error( lib2::error e );
|
||||
|
||||
expected<double, common_error> log_div_mul2( double x, double y, double m )
|
||||
{
|
||||
static const auto rm = []( auto x ) { return make_common_error(x); };
|
||||
|
||||
auto r1 = lib1::div( x, y ).remap_errors( rm );
|
||||
if( !r1 ) return r1.unexpected();
|
||||
|
||||
auto r2 = lib2::log( r1.value() ).remap_errors( rm );
|
||||
if( !r2 ) return r2.unexpected();
|
||||
|
||||
return m * r2.value();
|
||||
}
|
||||
|
||||
`std::error_code` is a very good choice for a common error type, and it's supported
|
||||
natively by the overload of `.remap_errors()` that takes no arguments, which uses
|
||||
calls to `make_error_code` to translate the errors.
|
||||
|
||||
When an attempt to access the value via `r.value()` is made and an error is present,
|
||||
an exception is thrown. By default, this exception is of type `bad_expected_access<E>`,
|
||||
as in D0323R2, but there are two differences. First, `bad_expected_access<E>` objects
|
||||
derive from a common base `bad_expected_access<void>` so that they can be caught at
|
||||
points where the set of possible `E` is unknown.
|
||||
|
||||
Second, the thrown exception can be customized. The implementation calls
|
||||
`throw_on_unexpected(e)` unqualified, where `e` is the error object, and the user can
|
||||
define such a function in the namespace of the type of `e`. Two specialized overloads
|
||||
of `throw_on_unexpected` are provided, one for `std::error_code`, which throws the
|
||||
corresponding `std::system_error`, and one for `std::exception_ptr`, which rethrows
|
||||
the exception stored in it.
|
||||
|
||||
For example, `lib1` from above may customize the exceptions associated with `lib1::error`
|
||||
via the following:
|
||||
|
||||
namespace lib1
|
||||
{
|
||||
|
||||
enum class error
|
||||
{
|
||||
division_by_zero,
|
||||
other_error
|
||||
};
|
||||
|
||||
class exception: public std::exception
|
||||
{
|
||||
private:
|
||||
|
||||
error e_;
|
||||
|
||||
public:
|
||||
|
||||
explicit exception( error e ): e_( e ) {}
|
||||
virtual const char * what() const noexcept;
|
||||
};
|
||||
|
||||
void throw_on_unexpected( error e )
|
||||
{
|
||||
throw exception( e );
|
||||
}
|
||||
|
||||
} // namespace lib1
|
||||
|
||||
In this implementation, `unexpected_type<E...>` has been called `unexpected_<T...>` and is
|
||||
an alias for `variant<T...>`. It is unfortunately not possible to use the name `unexpected<T...>`,
|
||||
because a function `std::unexpected` already exists.
|
||||
|
||||
The `make_...` helper functions have been omitted as unnecessary; class template argument deduction
|
||||
as in `expected{ 1.0 }` or `unexpected_{ lib1::division_by_zero }` suffices.
|
||||
|
||||
Other functions have also been dropped as unnecessary, not providing sufficient value, dangerous, or
|
||||
a combination of the three, although the decision of what to include isn't final at this point. The aim
|
||||
is to produce a minimal interface that still covers the use cases.
|
||||
|
||||
`expected<T, E1...>` can be converted to `expected<T, E2...>` if all error types in `E1...` are
|
||||
also in `E2...`. This allows composition as in the example above. Whether value convertibility ought
|
||||
to also be supported is an open question.
|
||||
|
||||
A single monadic operation ("bind") is supported in the form of `operator>>`, allowing
|
||||
|
||||
auto log_div_mul3( double x, double y, double m )
|
||||
{
|
||||
return lib1::div( x, y ) >> [&]( auto && r1 ) {
|
||||
|
||||
return lib2::log( r1 ) >> [&]( auto && r2 ) -> expected<double, lib1::error, lib2::error> {
|
||||
|
||||
return m * r2;
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
as well as the more concise in this example, although limited in utility for real world scenarios,
|
||||
|
||||
auto log_div_mul3( double x, double y, double m )
|
||||
{
|
||||
return lib1::div( x, y ) >> std::bind<expected<double, lib1::error, lib2::error>>( lib2::log, _1 ) >> m * _1;
|
||||
}
|
||||
|
||||
The more traditional name `then` was also a candidate for this operation, but `operator>>` has two advantages;
|
||||
it avoids the inevitable naming debates and does not require parentheses around the continuation lambda.
|
||||
|
||||
## Synopsis
|
||||
|
||||
// unexpected_
|
||||
|
||||
template<class... E> using unexpected_ = variant<E...>;
|
||||
|
||||
// bad_expected_access
|
||||
|
||||
template<class E = void> class bad_expected_access;
|
||||
|
||||
template<> class bad_expected_access<void>: public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
bad_expected_access() noexcept;
|
||||
char const * what() const noexcept;
|
||||
};
|
||||
|
||||
template<class E> class bad_expected_access: public bad_expected_access<void>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit bad_expected_access( E const& e );
|
||||
E error() const;
|
||||
};
|
||||
|
||||
// throw_on_unexpected
|
||||
|
||||
template<class E> void throw_on_unexpected( E const& e );
|
||||
void throw_on_unexpected( std::error_code const& e );
|
||||
void throw_on_unexpected( std::exception_ptr const& e );
|
||||
|
||||
// expected
|
||||
|
||||
template<class T, class... E> class expected
|
||||
{
|
||||
public:
|
||||
|
||||
// value constructors
|
||||
|
||||
constexpr expected() noexcept( /*see below*/ );
|
||||
|
||||
constexpr expected( T const& t ) noexcept( /*see below*/ );
|
||||
constexpr expected( T&& t ) noexcept( /*see below*/ );
|
||||
|
||||
// unexpected constructor
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( unexpected_<E2...> const& x );
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( unexpected_<E2...>&& x );
|
||||
|
||||
// conversion constructor
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( expected<T, E2...> const& x );
|
||||
|
||||
template<class... E2>
|
||||
constexpr expected( expected<T, E2...>&& x );
|
||||
|
||||
// emplace
|
||||
|
||||
template<class... A> void emplace( A&&... a );
|
||||
template<class V, class... A> void emplace( std::initializer_list<V> il, A&&... a );
|
||||
|
||||
// swap
|
||||
|
||||
void swap( expected& r ) noexcept( /*see below*/ );
|
||||
|
||||
// value queries
|
||||
|
||||
constexpr bool has_value() const noexcept;
|
||||
constexpr explicit operator bool() const noexcept;
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &;
|
||||
constexpr T const& value() const&;
|
||||
constexpr T&& value() &&;
|
||||
constexpr T const&& value() const&&;
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept;
|
||||
T const* operator->() const noexcept;
|
||||
|
||||
T& operator*() & noexcept;
|
||||
T const& operator*() const & noexcept;
|
||||
T&& operator*() && noexcept;
|
||||
T const&& operator*() const && noexcept;
|
||||
|
||||
// error queries
|
||||
|
||||
template<class E2> constexpr bool has_error() const noexcept;
|
||||
constexpr bool has_error() const noexcept;
|
||||
|
||||
// error access
|
||||
|
||||
unexpected_<E...> unexpected() const;
|
||||
|
||||
template<class E2> constexpr E2 error() const noexcept;
|
||||
constexpr /*see below*/ error() const noexcept;
|
||||
|
||||
// error mapping
|
||||
|
||||
template<class F> /*see below*/ remap_errors( F&& f ) const;
|
||||
expected<T, std::error_code> remap_errors() const;
|
||||
|
||||
// then
|
||||
|
||||
template<class F> /*see below*/ operator>>( F&& f ) const;
|
||||
};
|
||||
|
||||
template<class T, class... E>
|
||||
inline constexpr bool operator==( expected<T, E...> const& x1, expected<T, E...> const& x2 );
|
||||
|
||||
template<class T, class... E>
|
||||
inline constexpr bool operator!=( expected<T, E...> const& x1, expected<T, E...> const& x2 );
|
||||
|
||||
template<class T, class... E>
|
||||
inline void swap( expected<T, E...>& x1, expected<T, E...>& x2 ) noexcept( /*see below*/ );
|
||||
|
||||
// is_expected
|
||||
|
||||
template<class T> struct is_expected;
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
## Reference
|
||||
|
||||
...
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
<style>
|
||||
|
||||
*:not(pre)>code { background: none; color: #600000; }
|
||||
:not(pre):not([class^=L])>code { background: none; color: #600000; }
|
||||
|
||||
</style>
|
||||
|
@ -17,6 +17,9 @@ Peter Dimov
|
||||
:leveloffset: +1
|
||||
|
||||
include::variant2/overview.adoc[]
|
||||
include::variant2/changelog.adoc[]
|
||||
include::variant2/design.adoc[]
|
||||
include::variant2/implementation.adoc[]
|
||||
include::variant2/reference.adoc[]
|
||||
include::variant2/copyright.adoc[]
|
||||
|
||||
|
18
doc/variant2/changelog.adoc
Normal file
18
doc/variant2/changelog.adoc
Normal file
@ -0,0 +1,18 @@
|
||||
////
|
||||
Copyright 2019 Peter Dimov
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#changelog]
|
||||
# Revision History
|
||||
:idprefix: changelog_
|
||||
|
||||
## Changes in 1.71.0
|
||||
|
||||
After the Boost formal review, the implementation has been
|
||||
changed to provide the strong exception safety guarantee,
|
||||
instead of basic. `expected` has been removed.
|
187
doc/variant2/design.adoc
Normal file
187
doc/variant2/design.adoc
Normal file
@ -0,0 +1,187 @@
|
||||
////
|
||||
Copyright 2018, 2019 Peter Dimov
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#design]
|
||||
# Design
|
||||
:idprefix: design_
|
||||
|
||||
## Features
|
||||
|
||||
This `variant` implementation has two distinguishing features:
|
||||
|
||||
* It's never "valueless", that is, `variant<T1, T2, ..., Tn>` has an
|
||||
invariant that it always contains a valid value of one of the types
|
||||
`T1`, `T2`, ..., `Tn`.
|
||||
* It provides the strong exception safety guarantee on assignment and
|
||||
`emplace`.
|
||||
|
||||
This is achieved with the use of double storage, unless all of the
|
||||
contained types have a non-throwing move constructor.
|
||||
|
||||
## Rationale
|
||||
|
||||
### Never Valueless
|
||||
|
||||
It makes intuitive sense that `variant<X, Y, Z>` can hold only values
|
||||
of type `X`, type `Y`, or type `Z`, and nothing else.
|
||||
|
||||
If we think of `variant` as an extension of `union`, since a `union`
|
||||
has a state called "no active member", an argument can be made that a
|
||||
`variant<X, Y, Z>` should also have such an additional state, holding
|
||||
none of `X`, `Y`, `Z`.
|
||||
|
||||
This however makes `variant` less convenient in practice and less useful
|
||||
as a building block. If we really need a variable that only holds `X`,
|
||||
`Y`, or `Z`, the additional empty state creates complications that need
|
||||
to be worked around. And in the case where we do need this additional
|
||||
empty state, we can just use `variant<empty, X, Y, Z>`, with a suitable
|
||||
`struct empty {};`.
|
||||
|
||||
From a pure design perspective, the case for no additional empty state is
|
||||
solid. Implementation considerations, however, argue otherwise.
|
||||
|
||||
When we replace the current value of the `variant` (of, say, type `X`) with
|
||||
another (of type `Y`), since the new value needs to occupy the same storage
|
||||
as the old one, we need to destroy the old `X` first, then construct a new
|
||||
`Y` in its place. But since this is {cpp}, the construction can fail with an
|
||||
exception. At this point the `variant` is in the "has no active member"
|
||||
state that we've agreed it cannot be in.
|
||||
|
||||
This is a legitimate problem, and it is this problem that makes having
|
||||
an empty/valueless state so appealing. We just leave the `variant` empty on
|
||||
exception and we're done.
|
||||
|
||||
As explained, though, this is undesirable from a design perspective as it
|
||||
makes the component less useful and less elegant.
|
||||
|
||||
There are several ways around the issue. The most straightforward one is to
|
||||
just disallow types whose construction can throw. Since we can always create
|
||||
a temporary value first, then use the move constructor to initialize the one
|
||||
in the `variant`, it's enough to require a nonthrowing move constructor,
|
||||
rather than all constructors to be nonthrowing.
|
||||
|
||||
Unfortunately, under at least one popular standard library implementation,
|
||||
node based containers such as `std::list` and `std::map` have a potentially
|
||||
throwing move constructor. Disallowing `variant<X, std::map<Y, Z>>` is hardly
|
||||
practical, so the exceptional case cannot be avoided.
|
||||
|
||||
On exception, we could also construct some other value, leaving the `variant`
|
||||
valid; but in the general case, that construction can also throw. If one of
|
||||
the types has a nonthrowing default constructor, we can use it; but if not,
|
||||
we can't.
|
||||
|
||||
The approach Boost.Variant takes here is to allocate a temporary copy of
|
||||
the value on the heap. On exception, a pointer to that temporary copy can be
|
||||
stored into the `variant`. Pointer operations don't throw.
|
||||
|
||||
Another option is to use double buffering. If our `variant` occupies twice
|
||||
the storage, we can construct the new value in the unused half, then, once
|
||||
the construction succeeds, destroy the old value in the other half.
|
||||
|
||||
When `std::variant` was standardized, none of those approaches was deemed
|
||||
palatable, as all of them either introduce overhead or are too restrictive
|
||||
with respect to the types a `variant` can contain. So as a compromise,
|
||||
`std::variant` took a way that can (noncharitably) be described as "having
|
||||
your cake and eating it too."
|
||||
|
||||
Since the described exceptional situation is relatively rare, `std::variant`
|
||||
has a special case, called "valueless", into which it goes on exception,
|
||||
but the interface acknowledges its existence as little as possible, allowing
|
||||
users to pretend that it doesn't exist.
|
||||
|
||||
This is, arguably, not that bad from a practical point of view, but it leaves
|
||||
many of us wanting. Rare states that "never" occur are undertested and when
|
||||
that "never" actually happens, it's usually in the most inconvenient of times.
|
||||
|
||||
This implementation does not follow `std::variant`; it statically guarantees
|
||||
that `variant` is never in a valueless state. The function
|
||||
`valueless_by_exception` is provided for compatibility, but it always returns
|
||||
`false`.
|
||||
|
||||
Instead, if the contained types are such that it's not possible to avoid an
|
||||
exceptional situation when changing the contained value, double storage is
|
||||
used.
|
||||
|
||||
### Strong Exception Safety
|
||||
|
||||
The initial submission only provided the basic exception safety guarantee.
|
||||
If an attempt to change the contained value (via assignment or `emplace`)
|
||||
failed with an exception, and a type with a nonthrowing default constructor
|
||||
existed among the alternatives, a value of that type was created into the
|
||||
`variant`. The upside of this decision was that double storage was needed
|
||||
less frequently.
|
||||
|
||||
The reviewers were fairly united in hating it. Constructing a random type
|
||||
was deemed too unpredictable and not complying with the spirit of the
|
||||
basic guarantee. The default constructor of the chosen type, even if
|
||||
nonthrowing, may still have undesirable side effects. Or, if not that, a
|
||||
value of that type may have special significance for the surrounding code.
|
||||
Therefore, some argued, the `variant` should either remain with its
|
||||
old value, or transition into the new one, without synthesizing other
|
||||
states.
|
||||
|
||||
At the other side of the spectrum, there were those who considered double
|
||||
storage unacceptable. But they considered it unacceptable in principle,
|
||||
regardless of the frequency with which it was used.
|
||||
|
||||
As a result, providing the strong exception safety guarantee on assignment
|
||||
and `emplace` was declared an acceptance condition.
|
||||
|
||||
In retrospect, this was the right decision. The reason the strong guarantee
|
||||
is generally not provided is because it doesn't compose. When `X` and `Y`
|
||||
provide the basic guarantee on assignment, so does `struct { X x; Y y; };`.
|
||||
Similarly, when `X` and `Y` have nonthrowing assignments, so does the
|
||||
`struct`. But this doesn't hold for the strong guarantee.
|
||||
|
||||
The usual practice is to provide the basic guarantee on assignment and
|
||||
let the user synthesize a "strong" assignment out of either a nonthrowing
|
||||
`swap` or a nonthrowing move assignment. That is, given `x1` and `x2` of
|
||||
type `X`, instead of the "basic" `x1 = x2;`, use either `X(x2).swap(x1);`
|
||||
or `x1 = X(x2);`.
|
||||
|
||||
Nearly all types provide a nonthrowing `swap` or a nonthrowing move
|
||||
assignment, so this works well. Nearly all, except `variant`, which in the
|
||||
general case has neither a nonthrowing `swap` nor a nonthrowing move
|
||||
assignment. If `variant` does not provide the strong guarantee itself, it's
|
||||
impossible for the user to synthesize it.
|
||||
|
||||
So it should, and so it does.
|
||||
|
||||
## Differences with std::variant
|
||||
|
||||
The main differences between this implementation and `std::variant` are:
|
||||
|
||||
* No valueless-by-exception state: `valueless_by_exception()` always
|
||||
returns `false`.
|
||||
* Strong exception safety guarantee on assignment and `emplace`.
|
||||
* `emplace` first constructs the new value and then destroys the old one;
|
||||
in the single storage case, this translates to constructing a temporary
|
||||
and then moving it into place.
|
||||
* A converting constructor from, e.g. `variant<int, float>` to
|
||||
`variant<float, double, int>` is provided as an extension.
|
||||
* The reverse operation, going from `variant<float, double, int>` to
|
||||
`variant<int, float>` is provided as the member function `subset<U...>`.
|
||||
(This operation can throw if the current state of the variant cannot be
|
||||
represented.)
|
||||
* `variant<T...>` is not (yet) trivial when all contained types are trivial,
|
||||
as mandated by {cpp}17.
|
||||
* The {cpp}20 additions and changes to `std::variant` have not yet been
|
||||
implemented.
|
||||
|
||||
## Differences with Boost.Variant
|
||||
|
||||
This library is API compatible with `std::variant`. As such, its interface
|
||||
is different from Boost.Variant's. For example, visitation is performed via
|
||||
`visit` instead of `apply_visitor`.
|
||||
|
||||
Recursive variants are not supported.
|
||||
|
||||
Double storage is used instead of temporary heap backup. This `variant` is
|
||||
always "stack-based", it never allocates, and never throws `bad_alloc` on
|
||||
its own.
|
25
doc/variant2/implementation.adoc
Normal file
25
doc/variant2/implementation.adoc
Normal file
@ -0,0 +1,25 @@
|
||||
////
|
||||
Copyright 2019 Peter Dimov
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#implementation]
|
||||
# Implementation
|
||||
:idprefix: implementation_
|
||||
|
||||
## Dependencies
|
||||
|
||||
This implementation only depends on Boost.Config and Boost.Mp11.
|
||||
|
||||
## Supported Compilers
|
||||
|
||||
* GCC 4.8 or later with `-std=c++11` or above
|
||||
* Clang 3.5 or later with `-std=c++11` or above
|
||||
* Visual Studio 2015, 2017, 2019
|
||||
|
||||
Tested on https://travis-ci.org/boostorg/variant2/[Travis] and
|
||||
https://ci.appveyor.com/project/pdimov/variant2-fkab9[Appveyor].
|
@ -1,5 +1,5 @@
|
||||
////
|
||||
Copyright 2018 Peter Dimov
|
||||
Copyright 2018, 2019 Peter Dimov
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
@ -9,32 +9,327 @@ http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
[#overview]
|
||||
# Overview
|
||||
:idprefix:
|
||||
:idprefix: overview_
|
||||
|
||||
This library implements a type-safe discriminated union (variant) type,
|
||||
`variant<T...>`, that almost conforms to the {cpp}17 Standard's
|
||||
http://en.cppreference.com/w/cpp/utility/variant[`std::variant<T...>`]. The
|
||||
main differences between the two are:
|
||||
## Description
|
||||
|
||||
* `variant<T...>` does not have a valueless-by-exception state;
|
||||
* A converting constructor from, e.g. `variant<int, float>` to
|
||||
`variant<float, double, int>` is provided as an extension;
|
||||
* The reverse operation, going from `variant<float, double, int>` to
|
||||
`variant<int, float>` is provided as the member function `subset<U...>`.
|
||||
(This operation can throw if the current state of the variant cannot be
|
||||
represented.)
|
||||
* `variant<T...>` is not trivial when all contained types are trivial, as
|
||||
mandated by {cpp}17.
|
||||
This library implements a type-safe discriminated/tagged union type,
|
||||
`variant<T...>`, that is API-compatible with the {cpp}17 Standard's
|
||||
http://en.cppreference.com/w/cpp/utility/variant[`std::variant<T...>`].
|
||||
|
||||
To avoid the valueless-by-exception state, this implementation falls
|
||||
back to using double storage unless
|
||||
A `variant<T1, T2, ..., Tn>` variable can hold a value of any of the
|
||||
types `T1`, `T2`, ..., `Tn`. For example,
|
||||
`variant<int64_t, double, std::string>` can hold an `int64_t` value, a
|
||||
`double` value, or a `string` value.
|
||||
|
||||
* one of the alternatives is the type `monostate`,
|
||||
* one of the alternatives has a nonthrowing default constructor, or
|
||||
* all the contained types are nothrow move constructible.
|
||||
Such a type is sometimes called a "tagged union", because it's roughly
|
||||
equivalent to
|
||||
|
||||
If the first two bullets don't hold, but the third does, the variant uses
|
||||
single storage, but `emplace` constructs a temporary and moves it into place
|
||||
if the construction of the object can throw. In case this is undesirable, one
|
||||
can force `emplace` into always constructing in-place by adding `monostate` as
|
||||
one of the alternatives.
|
||||
```
|
||||
struct V
|
||||
{
|
||||
enum tag { tag_int64_t, tag_double, tag_string };
|
||||
|
||||
tag tag_;
|
||||
|
||||
union
|
||||
{
|
||||
int64_t i_;
|
||||
double d_;
|
||||
std::string s_;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Variants can be used to represent dynamically-typed values. A configuration
|
||||
file of the form
|
||||
|
||||
```
|
||||
server.host=test.example.com
|
||||
server.port=9174
|
||||
cache.max_load=0.7
|
||||
```
|
||||
|
||||
can be represented as `std::map<std::string, variant<int64_t, double,
|
||||
std::string>>`.
|
||||
|
||||
Variants can also represent polymorphism. To take a classic example, a
|
||||
polymorphic collection of shapes:
|
||||
|
||||
```
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
|
||||
class Shape
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Shape() = default;
|
||||
virtual double area() const = 0;
|
||||
};
|
||||
|
||||
class Rectangle: public Shape
|
||||
{
|
||||
private:
|
||||
|
||||
double width_, height_;
|
||||
|
||||
public:
|
||||
|
||||
Rectangle( double width, double height ):
|
||||
width_( width ), height_( height ) {}
|
||||
|
||||
virtual double area() const { return width_ * height_; }
|
||||
};
|
||||
|
||||
class Circle: public Shape
|
||||
{
|
||||
private:
|
||||
|
||||
double radius_;
|
||||
|
||||
public:
|
||||
|
||||
explicit Circle( double radius ): radius_( radius ) {}
|
||||
virtual double area() const { return M_PI * radius_ * radius_; }
|
||||
};
|
||||
|
||||
double total_area( std::vector<std::unique_ptr<Shape>> const & v )
|
||||
{
|
||||
double s = 0.0;
|
||||
|
||||
for( auto const& p: v )
|
||||
{
|
||||
s += p->area();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::vector<std::unique_ptr<Shape>> v;
|
||||
|
||||
v.push_back( std::unique_ptr<Shape>( new Circle( 1.0 ) ) );
|
||||
v.push_back( std::unique_ptr<Shape>( new Rectangle( 2.0, 3.0 ) ) );
|
||||
|
||||
std::cout << "Total area: " << total_area( v ) << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
can instead be represented as a collection of `variant<Rectangle, Circle>`
|
||||
values. This requires the possible `Shape` types be known in advance, as is
|
||||
often the case. In return, we no longer need virtual functions, or to allocate
|
||||
the values on the heap with `new Rectangle` and `new Circle`:
|
||||
|
||||
```
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
using namespace boost::variant2;
|
||||
|
||||
struct Rectangle
|
||||
{
|
||||
double width_, height_;
|
||||
double area() const { return width_ * height_; }
|
||||
};
|
||||
|
||||
struct Circle
|
||||
{
|
||||
double radius_;
|
||||
double area() const { return M_PI * radius_ * radius_; }
|
||||
};
|
||||
|
||||
double total_area( std::vector<variant<Rectangle, Circle>> const & v )
|
||||
{
|
||||
double s = 0.0;
|
||||
|
||||
for( auto const& x: v )
|
||||
{
|
||||
s += visit( []( auto const& y ){ return y.area(); }, x );
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::vector<variant<Rectangle, Circle>> v;
|
||||
|
||||
v.push_back( Circle{ 1.0 } );
|
||||
v.push_back( Rectangle{ 2.0, 3.0 } );
|
||||
|
||||
std::cout << "Total area: " << total_area( v ) << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
## Construction and Assignment
|
||||
|
||||
If we look at the
|
||||
|
||||
```
|
||||
v.push_back( Circle{ 1.0 } );
|
||||
```
|
||||
|
||||
line, we can deduce that `variant<Rectangle, Circle>` can be (implicitly)
|
||||
constructed from `Circle` (and `Rectangle`), and indeed it can. It can also
|
||||
be assigned a `Circle` or a `Rectangle`:
|
||||
|
||||
```
|
||||
variant<Rectangle, Circle> v = Circle{ 1.0 }; // v holds Circle
|
||||
v = Rectangle{ 2.0, 3.0 }; // v now holds Rectangle
|
||||
```
|
||||
|
||||
If we try to construct `variant<int, float>` from something that is neither
|
||||
`int` nor `float`, say, `(short)1`, the behavior is "as if" the `variant` has
|
||||
declared two constructors,
|
||||
|
||||
```
|
||||
variant::variant(int x);
|
||||
variant::variant(float x);
|
||||
```
|
||||
|
||||
and the standard overload resolution rules are used to pick the one that will
|
||||
be used. So `variant<int, float>((short)1)` will hold an `int`.
|
||||
|
||||
## Inspecting the Value
|
||||
|
||||
Putting values into a `variant` is easy, but taking them out is necessarily a
|
||||
bit more convoluted. It's not possible for `variant<int, float>` to define a
|
||||
member function `get() const`, because such a function will need its return
|
||||
type fixed at compile time, and whether the correct return type is `int` or
|
||||
`float` will only become known at run time.
|
||||
|
||||
There are a few ways around that. First, there is the accessor member function
|
||||
|
||||
```
|
||||
std::size_t variant::index() const noexcept;
|
||||
```
|
||||
|
||||
that returns the zero-based index of the current type. For `variant<int,
|
||||
float>`, it will return `0` for `int` and `1` for `float`.
|
||||
|
||||
Once we have the index, we can use the free function `get<N>` to obtain the
|
||||
value. Since we're passing the type index to `get`, it knows what to return.
|
||||
`get<0>(v)` will return `int`, and `get<1>(v)` will return `float`:
|
||||
|
||||
```
|
||||
void f( variant<int, float> const& v )
|
||||
{
|
||||
switch( v.index() )
|
||||
{
|
||||
case 0:
|
||||
|
||||
// use get<0>(v)
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
// use get<1>(v)
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
assert(false); // never happens
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we call `get<0>(v)`, and `v.index()` is not currently `0`, an exception
|
||||
(of type `bad_variant_access`) will be thrown.
|
||||
|
||||
An alternative approach is to use `get<int>(v)` or `get<float>(v)`. This
|
||||
works similarly.
|
||||
|
||||
Another alternative that avoids the possibility of `bad_variant_access` is
|
||||
to use `get_if`. Instead of a reference to the contained value, it returns
|
||||
a pointer to it, returning `nullptr` to indicate type mismatch. `get_if`
|
||||
takes a pointer to the `variant`, so in our example we'll use something along
|
||||
the following lines:
|
||||
|
||||
```
|
||||
void f( variant<int, float> const& v )
|
||||
{
|
||||
if( int const * p = get_if<int>(&v) )
|
||||
{
|
||||
// use *p
|
||||
}
|
||||
else if( float const * p = get_if<float>(&v) )
|
||||
{
|
||||
// use *p
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false); // never happens
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Visitation
|
||||
|
||||
Last but not least, there's `visit`. `visit(f, v)` calls the a function object
|
||||
`f` with the value contained in the `variant` `v` and returns the result. When
|
||||
`v` is `variant<int, float>`, it will call `f` with either an `int` or a
|
||||
`float`. The function object must be prepared to accept both.
|
||||
|
||||
In practice, this can be achieved by having the function take a type that can
|
||||
be passed either `int` or `float`, such as `double`:
|
||||
|
||||
```
|
||||
double f( double x ) { return x; }
|
||||
|
||||
double g( variant<int, float> const& v )
|
||||
{
|
||||
return visit( f, v );
|
||||
}
|
||||
```
|
||||
|
||||
By using a function object with an overloaded `operator()`:
|
||||
|
||||
```
|
||||
struct F
|
||||
{
|
||||
void operator()(int x) const { /* use x */ }
|
||||
void operator()(float x) const { /* use x */ }
|
||||
};
|
||||
|
||||
void g( variant<int, float> const& v )
|
||||
{
|
||||
visit( F(), v );
|
||||
}
|
||||
```
|
||||
|
||||
Or by using a polymorphic lambda, as we did in our `Circle`/`Rectangle`
|
||||
example:
|
||||
|
||||
```
|
||||
void g( variant<int, float> const& v )
|
||||
{
|
||||
visit( [&]( auto const& x ){ std::cout << x << std::endl; }, v );
|
||||
}
|
||||
```
|
||||
|
||||
`visit` can also take more than one `variant`. `visit(f, v1, v2)` calls
|
||||
`f(x1, x2)`, where `x1` is the value contained in `v1` and `x2` is the value
|
||||
in `v2`.
|
||||
|
||||
## Default Construction
|
||||
|
||||
The default constructor of `variant` value-initializes the first type in
|
||||
the list. `variant<int, float>{}` holds `0` (of type `int`), and
|
||||
`variant<float, int>{}` holds `0.0f`.
|
||||
|
||||
This is usually the desired behavior. However, in cases such as
|
||||
`variant<std::mutex, std::recursive_mutex>`, one might legitimately wish to
|
||||
avoid constructing a `std::mutex` by default. A provided type, `monostate`,
|
||||
can be used as the first type in those scenarios. `variant<monostate,
|
||||
std::mutex, std::recursive_mutex>` will default-construct a `monostate`,
|
||||
which is basically a no-op, as `monostate` is effectively an empty `struct`.
|
||||
|
@ -247,6 +247,9 @@ public:
|
||||
} // namespace boost
|
||||
```
|
||||
|
||||
In the descriptions that follow, let `i` be in the range `[0, sizeof...(T))`,
|
||||
and `Ti` be the `i`-th type in `T...`.
|
||||
|
||||
#### Constructors
|
||||
|
||||
```
|
||||
@ -383,24 +386,21 @@ Remarks: :: This function does not participate in overload resolution unless
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Effects: :: Destroys the currently contained value.
|
||||
Effects: ::
|
||||
Destroys the currently contained value.
|
||||
|
||||
#### Assignment
|
||||
|
||||
```
|
||||
constexpr variant& operator=( const variant& r )
|
||||
noexcept( mp_all<std::is_nothrow_copy_constructible<T>...,
|
||||
std::is_nothrow_copy_assignable<T>...>::value );
|
||||
noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Let `j` be `r.index()`.
|
||||
|
||||
Effects: ::
|
||||
- If `index() == j`, assigns the value contained in `r` to the value
|
||||
contained in `*this`.
|
||||
- Otherwise, equivalent to `emplace<j>(get<j>(r))`.
|
||||
Effects: :: `emplace<j>(get<j>(r))`.
|
||||
Returns: :: `*this`.
|
||||
Ensures: :: `index() == r.index()`.
|
||||
Remarks: :: This operator does not participate in overload resolution unless
|
||||
@ -409,18 +409,14 @@ Remarks: :: This operator does not participate in overload resolution unless
|
||||
|
||||
```
|
||||
constexpr variant& operator=( variant&& r )
|
||||
noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
|
||||
std::is_nothrow_move_assignable<T>...>::value );
|
||||
noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Let `j` be `r.index()`.
|
||||
|
||||
Effects: ::
|
||||
- If `index() == j`, assigns the value contained in `std::move(r)` to the
|
||||
value contained in `*this`.
|
||||
- Otherwise, equivalent to `emplace<j>(get<j>(std::move(r)))`.
|
||||
Effects: :: `emplace<j>(get<j>(std::move(r)))`.
|
||||
Returns: :: `*this`.
|
||||
Ensures: :: `index() == r.index()`.
|
||||
Remarks: :: This operator does not participate in overload resolution unless
|
||||
@ -439,18 +435,14 @@ Let `Tj` be a type that is determined as follows: build an imaginary function
|
||||
overload resolution for the expression `FUN(std::forward<U>(u))` defines the
|
||||
alternative `Tj` which is the type of the contained value after construction.
|
||||
|
||||
Effects: ::
|
||||
- If `index() == j`, assigns `std::forward<U>(u)` to the value contained in
|
||||
`*this`.
|
||||
- Otherwise, equivalent to `emplace<j>(std::forward<U>(u))`.
|
||||
Effects: :: `emplace<j>(std::forward<U>(u))`.
|
||||
Returns: :: `*this`.
|
||||
Ensures: :: `index() == j`.
|
||||
Remarks: ::
|
||||
The expression inside `noexcept` is `std::is_nothrow_constructible_v<Tj, U>
|
||||
&& std::is_nothrow_assignable_v<Tj&, U>`.
|
||||
The expression inside `noexcept` is `std::is_nothrow_constructible_v<Tj, U&&>`.
|
||||
This operator does not participate in overload resolution unless
|
||||
- `std::is_same_v<std::remove_cvref_t<T>, variant>` is `false`,
|
||||
- `std::is_constructible_v<Tj, U> && std::is_assignable_v<Tj&, U>` is
|
||||
- `std::is_constructible_v<Tj, U&&> && std::is_assignable_v<Tj&, U&&>` is
|
||||
`true`, and
|
||||
- the expression `FUN(std::forward<U>(u))` (with `FUN` being the
|
||||
above-mentioned set of imaginary functions) is well-formed.
|
||||
@ -469,7 +461,7 @@ Let `I` be the zero-based index of `U` in `T...`.
|
||||
Effects: :: Equivalent to: `return emplace<I>(std::forward<A>(a)...);`
|
||||
Remarks: ::
|
||||
This function shall not participate in overload resolution unless
|
||||
`std::is_constructible_v<U, A...>` is `true` and `U` occurs exactly once
|
||||
`std::is_constructible_v<U, A&&...>` is `true` and `U` occurs exactly once
|
||||
in `T...`.
|
||||
|
||||
```
|
||||
@ -484,7 +476,7 @@ Let `I` be the zero-based index of `U` in `T...`.
|
||||
Effects: :: Equivalent to: `return emplace<I>(il, std::forward<A>(a)...);`
|
||||
Remarks: ::
|
||||
This function shall not participate in overload resolution unless
|
||||
`std::is_constructible_v<U, std::initializer_list<V>&, A...>` is `true`
|
||||
`std::is_constructible_v<U, std::initializer_list<V>&, A&&...>` is `true`
|
||||
and `U` occurs exactly once in `T...`.
|
||||
|
||||
|
||||
@ -502,9 +494,12 @@ Effects: ::
|
||||
value as if using the expression `Ti(std::forward<A>(a)...)`.
|
||||
Ensures: :: `index() == I`.
|
||||
Returns: :: A reference to the new contained value.
|
||||
Throws: ::
|
||||
Nothing unless the initialization of the new contained value throws.
|
||||
Exception Safety: :: Strong. On exception, the contained value is unchanged.
|
||||
Remarks: ::
|
||||
This function shall not participate in overload resolution unless
|
||||
`std::is_constructible_v<Ti, A...>` is `true`.
|
||||
`std::is_constructible_v<Ti, A&&...>` is `true`.
|
||||
|
||||
```
|
||||
template<size_t I, class V, class... A>
|
||||
@ -520,9 +515,12 @@ Effects: ::
|
||||
value as if using the expression `Ti(il, std::forward<A>(a)...)`.
|
||||
Ensures: :: `index() == I`.
|
||||
Returns: :: A reference to the new contained value.
|
||||
Throws: ::
|
||||
Nothing unless the initialization of the new contained value throws.
|
||||
Exception Safety: :: Strong. On exception, the contained value is unchanged.
|
||||
Remarks: ::
|
||||
This function shall not participate in overload resolution unless
|
||||
`std::is_constructible_v<Ti, std::initializer_list<V>&, A...>` is `true`.
|
||||
`std::is_constructible_v<Ti, std::initializer_list<V>&, A&&...>` is `true`.
|
||||
|
||||
#### Value Status
|
||||
|
||||
@ -534,13 +532,16 @@ constexpr bool valueless_by_exception() const noexcept;
|
||||
+
|
||||
Returns: :: `false`.
|
||||
|
||||
NOTE: This function is provided purely for compatibility with `std::variant`.
|
||||
|
||||
```
|
||||
constexpr size_t index() const noexcept;
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: The zero-based index of the active alternative.
|
||||
Returns: ::
|
||||
The zero-based index of the active alternative.
|
||||
|
||||
#### Swap
|
||||
|
||||
@ -709,6 +710,8 @@ template<size_t I, class... T>
|
||||
+
|
||||
Effects: :: If `v.index()` is `I`, returns a reference to the object stored in
|
||||
the variant. Otherwise, throws `bad_variant_access`.
|
||||
Remarks: :: These functions do not participate in overload resolution
|
||||
unless `I` < `sizeof...(T)`.
|
||||
|
||||
```
|
||||
template<class U, class... T>
|
||||
@ -749,9 +752,10 @@ template<size_t I, class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Requires: :: `I < sizeof...(U)`. Otherwise, the program is ill-formed.
|
||||
Effects: :: A pointer to the value stored in the variant, if
|
||||
`v != nullptr && v\->index() == I`. Otherwise, `nullptr`.
|
||||
Remarks: :: These functions do not participate in overload resolution
|
||||
unless `I` < `sizeof...(T)`.
|
||||
|
||||
```
|
||||
template<class U, class... T>
|
||||
@ -780,7 +784,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() == w.index && get<I>(v) == get<I>(w)`, where `I`
|
||||
Returns: :: `v.index() == w.index() && get<I>(v) == get<I>(w)`, where `I`
|
||||
is `v.index()`.
|
||||
|
||||
```
|
||||
@ -799,7 +803,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() < w.index || (v.index() == w.index && get<I>(v) < get<I>(w))`,
|
||||
Returns: :: `v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w))`,
|
||||
where `I` is `v.index()`.
|
||||
|
||||
```
|
||||
@ -818,7 +822,7 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `v.index() < w.index || (v.index() == w.index && get<I>(v) \<= get<I>(w))`,
|
||||
Returns: :: `v.index() < w.index() || (v.index() == w.index() && get<I>(v) \<= get<I>(w))`,
|
||||
where `I` is `v.index()`.
|
||||
|
||||
```
|
||||
@ -828,7 +832,8 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `w \<= v`.
|
||||
Returns: ::
|
||||
`w \<= v`.
|
||||
|
||||
### visit
|
||||
|
||||
@ -851,7 +856,8 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Effects: :: Equivalent to `v.swap(w)`.
|
||||
Effects: ::
|
||||
Equivalent to `v.swap(w)`.
|
||||
|
||||
### bad_variant_access
|
||||
|
||||
|
@ -1,446 +0,0 @@
|
||||
#ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#endif
|
||||
#include <boost/core/demangle.hpp>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <cassert>
|
||||
|
||||
//
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace variant2
|
||||
{
|
||||
|
||||
// unexpected_
|
||||
|
||||
template<class... E> using unexpected_ = variant<E...>;
|
||||
|
||||
// bad_expected_access
|
||||
|
||||
template<class E = void> class bad_expected_access;
|
||||
|
||||
template<> class bad_expected_access<void>: public std::exception
|
||||
{
|
||||
private:
|
||||
|
||||
std::string msg_;
|
||||
|
||||
public:
|
||||
|
||||
bad_expected_access() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
explicit bad_expected_access( std::string&& msg ) noexcept: msg_( std::move(msg) ) // extension
|
||||
{
|
||||
}
|
||||
|
||||
char const * what() const noexcept
|
||||
{
|
||||
return msg_.empty()? "bad_expected_access<>": msg_.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class E, class En = std::enable_if_t<!std::is_enum<E>::value>> std::string add_value( E const& /*e*/ )
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template<class E, class E1 = void, class E2 = std::enable_if_t<std::is_enum<E>::value>> std::string add_value( E const& e )
|
||||
{
|
||||
return ": " + std::to_string( static_cast<int>(e) );
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class E> class bad_expected_access: public bad_expected_access<void>
|
||||
{
|
||||
private:
|
||||
|
||||
E e_;
|
||||
|
||||
public:
|
||||
|
||||
explicit bad_expected_access( E const& e )
|
||||
: bad_expected_access<void>( "bad_expected_access<" + boost::core::demangle( typeid(E).name() ) + ">" + variant2::detail::add_value( e ) ), e_( e )
|
||||
{
|
||||
}
|
||||
|
||||
E error() const
|
||||
{
|
||||
return e_;
|
||||
}
|
||||
};
|
||||
|
||||
// throw_on_unexpected
|
||||
|
||||
template<class E> void throw_on_unexpected( E const& /*e*/ )
|
||||
{
|
||||
}
|
||||
|
||||
void throw_on_unexpected( std::error_code const & e )
|
||||
{
|
||||
throw std::system_error( e );
|
||||
}
|
||||
|
||||
void throw_on_unexpected( std::exception_ptr const & e )
|
||||
{
|
||||
if( e )
|
||||
{
|
||||
std::rethrow_exception( e );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_expected_access<>( "bad_expected_access<>: null exception_ptr" );
|
||||
}
|
||||
}
|
||||
|
||||
// expected
|
||||
|
||||
template<class T, class... E> class expected;
|
||||
|
||||
template<class T> struct is_expected: std::false_type {};
|
||||
template<class T, class... E> struct is_expected<expected<T, E...>>: std::true_type {};
|
||||
|
||||
template<class T, class... E> class expected
|
||||
{
|
||||
private:
|
||||
|
||||
variant<T, E...> v_;
|
||||
|
||||
private:
|
||||
|
||||
void _bad_access() const
|
||||
{
|
||||
mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I )
|
||||
{
|
||||
if( I == 0 )
|
||||
{
|
||||
throw bad_expected_access<>( "bad_expected_access<>: value present on error request" );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const & e = get<I>(v_);
|
||||
|
||||
throw_on_unexpected( e );
|
||||
throw bad_expected_access<std::decay_t<decltype(e)>>( e );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// value constructors
|
||||
|
||||
constexpr expected() noexcept( std::is_nothrow_default_constructible<T>::value )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr expected( T const& t ) noexcept( std::is_nothrow_copy_constructible<T>::value ): v_( in_place_index<0>, t )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr expected( T && t ) noexcept( std::is_nothrow_move_constructible<T>::value ): v_( in_place_index<0>, std::move(t) )
|
||||
{
|
||||
}
|
||||
|
||||
// template<class U> constexpr expected( U && u ); where U in E...?
|
||||
|
||||
// in-place constructor?
|
||||
|
||||
// unexpected constructor
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_copy_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( unexpected_<E2...> const & x ): v_( x )
|
||||
{
|
||||
}
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_move_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( unexpected_<E2...> && x ): v_( std::move(x) )
|
||||
{
|
||||
}
|
||||
|
||||
// conversion constructor
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_copy_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( expected<T, E2...> const & x ): v_( x.v_ )
|
||||
{
|
||||
}
|
||||
|
||||
template<class... E2,
|
||||
class En = mp_if<mp_all<std::is_move_constructible<E2>..., mp_contains<mp_list<E...>, E2>...>, void>>
|
||||
constexpr expected( expected<T, E2...> && x ): v_( std::move(x.v_) )
|
||||
{
|
||||
}
|
||||
|
||||
// emplace
|
||||
|
||||
template<class... A> void emplace( A&&... a )
|
||||
{
|
||||
v_.emplace( std::forward<A>(a)... );
|
||||
}
|
||||
|
||||
template<class V, class... A> void emplace( std::initializer_list<V> il, A&&... a )
|
||||
{
|
||||
v_.emplace( il, std::forward<A>(a)... );
|
||||
}
|
||||
|
||||
// swap
|
||||
|
||||
void swap( expected & r ) noexcept( noexcept( v_.swap( r.v_ ) ) )
|
||||
{
|
||||
v_.swap( r.v_ );
|
||||
}
|
||||
|
||||
// value queries
|
||||
|
||||
constexpr bool has_value() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &
|
||||
{
|
||||
if( !has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
|
||||
constexpr T const& value() const&
|
||||
{
|
||||
if( !has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
|
||||
constexpr T&& value() &&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
constexpr T const&& value() const&&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T const* operator->() const noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T& operator*() & noexcept
|
||||
{
|
||||
T* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T const& operator*() const & noexcept
|
||||
{
|
||||
T const* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T&& operator*() && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
T const&& operator*() const && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
// error queries
|
||||
|
||||
template<class E2> constexpr bool has_error() const noexcept
|
||||
{
|
||||
using I = mp_find<expected, E2>;
|
||||
return v_.index() == I::value;
|
||||
}
|
||||
|
||||
constexpr bool has_error() const noexcept
|
||||
{
|
||||
static_assert( sizeof...(E) == 1, "has_error() is only valid when there is a single E" );
|
||||
return has_error<mp_first<expected>>();
|
||||
}
|
||||
|
||||
// error access
|
||||
|
||||
unexpected_<E...> unexpected() const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return v_.template subset<E...>();
|
||||
}
|
||||
|
||||
template<class E2> constexpr E2 error() const noexcept
|
||||
{
|
||||
using I = mp_find<expected, E2>;
|
||||
|
||||
if( v_.index() != I::value )
|
||||
{
|
||||
_bad_access();
|
||||
}
|
||||
|
||||
return get<I>( v_ );
|
||||
}
|
||||
|
||||
constexpr mp_first<expected> error() const noexcept
|
||||
{
|
||||
static_assert( sizeof...(E) == 1, "error() is only valid when there is a single E" );
|
||||
return error<mp_first<expected>>();
|
||||
}
|
||||
|
||||
// error mapping
|
||||
|
||||
private:
|
||||
|
||||
template<class F> struct Qret
|
||||
{
|
||||
template<class... A> using fn = decltype( std::declval<F>()( std::declval<A>()... ) );
|
||||
};
|
||||
|
||||
template<class F> using remapped = mp_append<expected<T>, mp_unique<mp_transform_q<Qret<F>, mp_list<E...>>>>;
|
||||
|
||||
template<class R, std::size_t I, class F, class V> static R _remap_error( mp_size_t<I>, F && f, V && v )
|
||||
{
|
||||
// return R( std::forward<F>(f)( std::forward<V>(v) ) );
|
||||
|
||||
auto e = std::forward<F>(f)( std::forward<V>(v) );
|
||||
|
||||
return unexpected_<decltype(e)>{ e };
|
||||
}
|
||||
|
||||
template<class R, class F, class V> static R _remap_error( mp_size_t<0>, F && /*f*/, V && v )
|
||||
{
|
||||
return R( std::forward<V>(v) );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<class F> remapped<F> remap_errors( F && f ) const
|
||||
{
|
||||
using R = remapped<F>;
|
||||
|
||||
return mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I ) {
|
||||
|
||||
return this->_remap_error<R>( I, f, get<I>(v_) );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
expected<T, std::error_code> remap_errors() const
|
||||
{
|
||||
using R = expected<T, std::error_code>;
|
||||
|
||||
auto f = []( auto const& e ){ return make_error_code(e); };
|
||||
|
||||
return mp_with_index<mp_size<expected>>( v_.index(), [&]( auto I ) {
|
||||
|
||||
return this->_remap_error<R>( I, f, get<I>(v_) );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// then
|
||||
|
||||
private:
|
||||
|
||||
template<class F, class U> using then_result_ = decltype( std::declval<F>()( std::declval<U>() ) );
|
||||
|
||||
template<class F, class U, class R = then_result_<F, U>> using then_result = mp_if<is_expected<R>, R, expected<R, E...>>;
|
||||
|
||||
public:
|
||||
|
||||
template<class F> then_result<F, T const&> then( F && f ) const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return std::forward<F>(f)( **this );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unexpected();
|
||||
}
|
||||
}
|
||||
|
||||
template<class F> then_result<F, T const&> operator>>( F && f ) const
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return std::forward<F>(f)( **this );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unexpected();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class... E> inline constexpr bool operator==( expected<T, E...> const & x1, expected<T, E...> const & x2 )
|
||||
{
|
||||
return x1.v_ == x2.v_;
|
||||
}
|
||||
|
||||
template<class T, class... E> inline constexpr bool operator!=( expected<T, E...> const & x1, expected<T, E...> const & x2 )
|
||||
{
|
||||
return x1.v_ != x2.v_;
|
||||
}
|
||||
|
||||
template<class T, class... E> inline void swap( expected<T, E...> & x1, expected<T, E...> & x2 ) noexcept( noexcept( x1.swap( x2 ) ) )
|
||||
{
|
||||
x1.swap( x2 );
|
||||
}
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED
|
@ -1,259 +0,0 @@
|
||||
#ifndef BOOST_VARIANT2_OUTCOME_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_OUTCOME_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#endif
|
||||
#include <system_error>
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
//
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace variant2
|
||||
{
|
||||
|
||||
enum class outcome_errc
|
||||
{
|
||||
not_initialized
|
||||
};
|
||||
|
||||
class outcome_error_category: public std::error_category
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char * name() const noexcept
|
||||
{
|
||||
return "boost::variant2::outcome";
|
||||
}
|
||||
|
||||
virtual std::string message( int e ) const
|
||||
{
|
||||
switch( e )
|
||||
{
|
||||
case (int)outcome_errc::not_initialized:
|
||||
|
||||
return "outcome<> not initialized";
|
||||
|
||||
default:
|
||||
|
||||
return "unknown outcome<> error";
|
||||
}
|
||||
}
|
||||
|
||||
static outcome_error_category const & instance()
|
||||
{
|
||||
static outcome_error_category cat;
|
||||
return cat;
|
||||
}
|
||||
};
|
||||
|
||||
std::error_code make_error_code( outcome_errc e )
|
||||
{
|
||||
return std::error_code( static_cast<int>( e ), outcome_error_category::instance() );
|
||||
}
|
||||
|
||||
template<class T> class outcome
|
||||
{
|
||||
private:
|
||||
|
||||
variant<T, std::error_code, std::exception_ptr> v_;
|
||||
|
||||
public:
|
||||
|
||||
// constructors
|
||||
|
||||
constexpr outcome() noexcept: v_( make_error_code( outcome_errc::not_initialized ) )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr outcome( T const& t ): v_( t )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr outcome( T&& t ): v_( std::move(t) )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr outcome( std::error_code const & ec ) noexcept: v_( ec )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr outcome( std::exception_ptr const & ep ) noexcept: v_( ep )
|
||||
{
|
||||
}
|
||||
|
||||
// queries
|
||||
|
||||
constexpr bool has_value() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
constexpr bool has_error() const noexcept
|
||||
{
|
||||
return v_.index() == 1;
|
||||
}
|
||||
|
||||
constexpr bool has_exception() const noexcept
|
||||
{
|
||||
return v_.index() == 2;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::system_error( *get_if<1>(&v_) );
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T const& value() const&
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::system_error( *get_if<1>(&v_) );
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T&& value() &&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
constexpr T const&& value() const&&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T const* operator->() const noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T& operator*() & noexcept
|
||||
{
|
||||
T* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T const& operator*() const & noexcept
|
||||
{
|
||||
T const* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T&& operator*() && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
T const&& operator*() const && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
// error access
|
||||
|
||||
/*constexpr*/ std::error_code error() const noexcept
|
||||
{
|
||||
if( has_error() )
|
||||
{
|
||||
return *get_if<1>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::error_code();
|
||||
}
|
||||
}
|
||||
|
||||
// exception access
|
||||
|
||||
std::exception_ptr exception() const noexcept
|
||||
{
|
||||
if( has_exception() )
|
||||
{
|
||||
return *get_if<2>(&v_);
|
||||
}
|
||||
else if( has_value() )
|
||||
{
|
||||
return std::exception_ptr();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_exception_ptr( std::system_error( *get_if<1>(&v_) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
void set_value( T const& t ) noexcept( std::is_nothrow_copy_constructible<T>::value )
|
||||
{
|
||||
v_.emplace<0>( t );
|
||||
}
|
||||
|
||||
void set_value( T&& t ) noexcept( std::is_nothrow_move_constructible<T>::value )
|
||||
{
|
||||
v_.emplace<0>( std::move( t ) );
|
||||
}
|
||||
|
||||
void set_error( std::error_code const & e ) noexcept
|
||||
{
|
||||
v_.emplace<1>( e );
|
||||
}
|
||||
|
||||
void set_exception( std::exception_ptr const & x ) noexcept
|
||||
{
|
||||
v_.emplace<2>( x );
|
||||
}
|
||||
|
||||
// swap
|
||||
|
||||
void swap( outcome& r ) noexcept( noexcept( v_.swap( r.v_ ) ) )
|
||||
{
|
||||
v_.swap( r.v_ );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_VARIANT2_OUTCOME_HPP_INCLUDED
|
@ -1,226 +0,0 @@
|
||||
#ifndef BOOST_VARIANT2_RESULT_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_RESULT_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#endif
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
//
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace variant2
|
||||
{
|
||||
|
||||
enum class result_errc
|
||||
{
|
||||
not_initialized
|
||||
};
|
||||
|
||||
class result_error_category: public std::error_category
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char * name() const noexcept
|
||||
{
|
||||
return "boost::variant2::result";
|
||||
}
|
||||
|
||||
virtual std::string message( int e ) const
|
||||
{
|
||||
switch( e )
|
||||
{
|
||||
case (int)result_errc::not_initialized:
|
||||
|
||||
return "result<> not initialized";
|
||||
|
||||
default:
|
||||
|
||||
return "unknown result<> error";
|
||||
}
|
||||
}
|
||||
|
||||
static result_error_category const & instance()
|
||||
{
|
||||
static result_error_category cat;
|
||||
return cat;
|
||||
}
|
||||
};
|
||||
|
||||
std::error_code make_error_code( result_errc e )
|
||||
{
|
||||
return std::error_code( static_cast<int>( e ), result_error_category::instance() );
|
||||
}
|
||||
|
||||
template<class T> class result
|
||||
{
|
||||
private:
|
||||
|
||||
variant<T, std::error_code> v_;
|
||||
|
||||
public:
|
||||
|
||||
// constructors
|
||||
|
||||
constexpr result() noexcept: v_( make_error_code( result_errc::not_initialized ) )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr result( T const& t ): v_( t )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr result( T&& t ): v_( std::move(t) )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr result( std::error_code const & ec ) noexcept: v_( ec )
|
||||
{
|
||||
}
|
||||
|
||||
// queries
|
||||
|
||||
constexpr bool has_value() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
constexpr bool has_error() const noexcept
|
||||
{
|
||||
return v_.index() == 1;
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return v_.index() == 0;
|
||||
}
|
||||
|
||||
// checked value access
|
||||
|
||||
constexpr T& value() &
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::system_error( *get_if<1>(&v_) );
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T const& value() const&
|
||||
{
|
||||
if( has_value() )
|
||||
{
|
||||
return *get_if<0>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::system_error( *get_if<1>(&v_) );
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T&& value() &&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
constexpr T const&& value() const&&
|
||||
{
|
||||
return std::move( value() );
|
||||
}
|
||||
|
||||
// unchecked value access
|
||||
|
||||
T* operator->() noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T const* operator->() const noexcept
|
||||
{
|
||||
return get_if<0>(&v_);
|
||||
}
|
||||
|
||||
T& operator*() & noexcept
|
||||
{
|
||||
T* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T const& operator*() const & noexcept
|
||||
{
|
||||
T const* p = get_if<0>(&v_);
|
||||
|
||||
assert( p != 0 );
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
T&& operator*() && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
T const&& operator*() const && noexcept
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
// error access
|
||||
|
||||
/*constexpr*/ std::error_code error() const noexcept
|
||||
{
|
||||
if( has_error() )
|
||||
{
|
||||
return *get_if<1>(&v_);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::error_code();
|
||||
}
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
void set_value( T const& t ) noexcept( std::is_nothrow_copy_constructible<T>::value )
|
||||
{
|
||||
v_.emplace<0>( t );
|
||||
}
|
||||
|
||||
void set_value( T&& t ) noexcept( std::is_nothrow_move_constructible<T>::value )
|
||||
{
|
||||
v_.emplace<0>( std::move( t ) );
|
||||
}
|
||||
|
||||
void set_error( std::error_code const & e ) noexcept
|
||||
{
|
||||
v_.emplace<1>( e );
|
||||
}
|
||||
|
||||
// swap
|
||||
|
||||
void swap( result& r ) noexcept( noexcept( v_.swap( r.v_ ) ) )
|
||||
{
|
||||
v_.swap( r.v_ );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace variant2
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_VARIANT2_RESULT_HPP_INCLUDED
|
@ -1,7 +1,7 @@
|
||||
#ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_VARIANT_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017, 2018 Peter Dimov.
|
||||
// Copyright 2017-2019 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
//
|
||||
@ -29,6 +29,13 @@
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
|
||||
BOOST_NORETURN void throw_exception( std::exception const & e ); // user defined
|
||||
|
||||
#endif
|
||||
|
||||
namespace variant2
|
||||
{
|
||||
|
||||
@ -48,6 +55,24 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_NORETURN inline void throw_bad_variant_access()
|
||||
{
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
|
||||
boost::throw_exception( bad_variant_access() );
|
||||
|
||||
#else
|
||||
|
||||
throw bad_variant_access();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// monostate
|
||||
|
||||
struct monostate
|
||||
@ -266,6 +291,10 @@ template<std::size_t I, class... T> struct variant_alternative<I, variant<T...>>
|
||||
|
||||
#endif
|
||||
|
||||
// variant_npos
|
||||
|
||||
constexpr std::size_t variant_npos = ~static_cast<std::size_t>( 0 );
|
||||
|
||||
// holds_alternative
|
||||
|
||||
template<class U, class... T> constexpr bool holds_alternative( variant<T...> const& v ) noexcept
|
||||
@ -279,20 +308,20 @@ template<class U, class... T> constexpr bool holds_alternative( variant<T...> co
|
||||
template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T...>>& get(variant<T...>& v)
|
||||
{
|
||||
static_assert( I < sizeof...(T), "Index out of bounds" );
|
||||
return (void)( v.index() != I? throw bad_variant_access(): 0 ), v._get_impl( mp11::mp_size_t<I>() );
|
||||
return ( v.index() != I? detail::throw_bad_variant_access(): (void)0 ), v._get_impl( mp11::mp_size_t<I>() );
|
||||
}
|
||||
|
||||
template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T...>>&& get(variant<T...>&& v)
|
||||
{
|
||||
static_assert( I < sizeof...(T), "Index out of bounds" );
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1920)
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1930)
|
||||
|
||||
return (void)( v.index() != I? throw bad_variant_access(): 0 ), std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
return ( v.index() != I? detail::throw_bad_variant_access(): (void)0 ), std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
|
||||
#else
|
||||
|
||||
if( v.index() != I ) throw bad_variant_access();
|
||||
if( v.index() != I ) detail::throw_bad_variant_access();
|
||||
return std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
|
||||
#endif
|
||||
@ -301,20 +330,20 @@ template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T
|
||||
template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T...>> const& get(variant<T...> const& v)
|
||||
{
|
||||
static_assert( I < sizeof...(T), "Index out of bounds" );
|
||||
return (void)( v.index() != I? throw bad_variant_access(): 0 ), v._get_impl( mp11::mp_size_t<I>() );
|
||||
return ( v.index() != I? detail::throw_bad_variant_access(): (void)0 ), v._get_impl( mp11::mp_size_t<I>() );
|
||||
}
|
||||
|
||||
template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T...>> const&& get(variant<T...> const&& v)
|
||||
{
|
||||
static_assert( I < sizeof...(T), "Index out of bounds" );
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1920)
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1930)
|
||||
|
||||
return (void)( v.index() != I? throw bad_variant_access(): 0 ), std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
return ( v.index() != I? detail::throw_bad_variant_access(): (void)0 ), std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
|
||||
#else
|
||||
|
||||
if( v.index() != I ) throw bad_variant_access();
|
||||
if( v.index() != I ) detail::throw_bad_variant_access();
|
||||
return std::move( v._get_impl( mp11::mp_size_t<I>() ) );
|
||||
|
||||
#endif
|
||||
@ -359,7 +388,7 @@ template<class U, class... T> constexpr U& get(variant<T...>& v)
|
||||
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
|
||||
return (void)( v.index() != I::value? throw bad_variant_access(): 0 ), v._get_impl( I() );
|
||||
return ( v.index() != I::value? detail::throw_bad_variant_access(): (void)0 ), v._get_impl( I() );
|
||||
}
|
||||
|
||||
template<class U, class... T> constexpr U&& get(variant<T...>&& v)
|
||||
@ -368,13 +397,13 @@ template<class U, class... T> constexpr U&& get(variant<T...>&& v)
|
||||
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1920)
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1930)
|
||||
|
||||
return (void)( v.index() != I::value? throw bad_variant_access(): 0 ), std::move( v._get_impl( I() ) );
|
||||
return ( v.index() != I::value? detail::throw_bad_variant_access(): (void)0 ), std::move( v._get_impl( I() ) );
|
||||
|
||||
#else
|
||||
|
||||
if( v.index() != I::value ) throw bad_variant_access();
|
||||
if( v.index() != I::value ) detail::throw_bad_variant_access();
|
||||
return std::move( v._get_impl( I() ) );
|
||||
|
||||
#endif
|
||||
@ -386,7 +415,7 @@ template<class U, class... T> constexpr U const& get(variant<T...> const& v)
|
||||
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
|
||||
return (void)( v.index() != I::value? throw bad_variant_access(): 0 ), v._get_impl( I() );
|
||||
return ( v.index() != I::value? detail::throw_bad_variant_access(): (void)0 ), v._get_impl( I() );
|
||||
}
|
||||
|
||||
template<class U, class... T> constexpr U const&& get(variant<T...> const&& v)
|
||||
@ -395,13 +424,13 @@ template<class U, class... T> constexpr U const&& get(variant<T...> const&& v)
|
||||
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1920)
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1930)
|
||||
|
||||
return (void)( v.index() != I::value? throw bad_variant_access(): 0 ), std::move( v._get_impl( I() ) );
|
||||
return ( v.index() != I::value? detail::throw_bad_variant_access(): (void)0 ), std::move( v._get_impl( I() ) );
|
||||
|
||||
#else
|
||||
|
||||
if( v.index() != I::value ) throw bad_variant_access();
|
||||
if( v.index() != I::value ) detail::throw_bad_variant_access();
|
||||
return std::move( v._get_impl( I() ) );
|
||||
|
||||
#endif
|
||||
@ -574,7 +603,7 @@ template<class T1, class... T> struct overload<T1, T...>: overload<T...>
|
||||
mp11::mp_identity<T1> operator()(T1) const;
|
||||
};
|
||||
|
||||
#if BOOST_WORKAROUND( BOOST_MSVC, < 1920 )
|
||||
#if BOOST_WORKAROUND( BOOST_MSVC, < 1930 )
|
||||
|
||||
template<class U, class... T> using resolve_overload_type_ = decltype( overload<T...>()(std::declval<U>()) );
|
||||
|
||||
@ -594,11 +623,8 @@ template<class U, class... T> using resolve_overload_index = mp11::mp_find<mp11:
|
||||
|
||||
// variant_base
|
||||
|
||||
template<class... T> using can_be_valueless = mp11::mp_any<std::is_same<T, monostate>..., std::is_nothrow_default_constructible<T>...>;
|
||||
template<class... T> using valueless_index = mp11::mp_if<mp11::mp_contains<mp11::mp_list<T...>, monostate>, mp11::mp_find<mp11::mp_list<T...>, monostate>, mp11::mp_find_if<mp11::mp_list<T...>, std::is_nothrow_default_constructible>>;
|
||||
|
||||
template<bool is_trivially_destructible, bool is_single_buffered, class... T> struct variant_base_impl; // trivially destructible, single buffered
|
||||
template<class... T> using variant_base = variant_base_impl<mp11::mp_all<std::is_trivially_destructible<T>...>::value, mp11::mp_any<mp11::mp_all<std::is_nothrow_move_constructible<T>...>, can_be_valueless<T...>>::value, T...>;
|
||||
template<bool is_trivially_destructible, bool is_single_buffered, class... T> struct variant_base_impl;
|
||||
template<class... T> using variant_base = variant_base_impl<mp11::mp_all<std::is_trivially_destructible<T>...>::value, mp11::mp_all<std::is_nothrow_move_constructible<T>...>::value, T...>;
|
||||
|
||||
struct none {};
|
||||
|
||||
@ -645,58 +671,30 @@ template<class... T> struct variant_base_impl<true, true, T...>
|
||||
return st1_.get( mp11::mp_size_t<I+1>() );
|
||||
}
|
||||
|
||||
template<std::size_t J, class U, bool B, class... A> BOOST_CXX14_CONSTEXPR void emplace_impl( mp11::mp_true, mp11::mp_bool<B>, A&&... a )
|
||||
template<std::size_t J, class U, class... A> BOOST_CXX14_CONSTEXPR void emplace_impl( mp11::mp_true, A&&... a )
|
||||
{
|
||||
static_assert( std::is_nothrow_constructible<U, A&&...>::value, "Logic error: U must be nothrow constructible from A&&..." );
|
||||
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::forward<A>(a)... );
|
||||
ix_ = J;
|
||||
}
|
||||
|
||||
template<std::size_t J, class U, class... A> BOOST_CXX14_CONSTEXPR void emplace_impl( mp11::mp_false, mp11::mp_true, A&&... a )
|
||||
template<std::size_t J, class U, class... A> BOOST_CXX14_CONSTEXPR void emplace_impl( mp11::mp_false, A&&... a )
|
||||
{
|
||||
static_assert( std::is_nothrow_move_constructible<U>::value, "Logic error: U must be nothrow move constructible" );
|
||||
|
||||
U tmp( std::forward<A>(a)... );
|
||||
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::move(tmp) );
|
||||
ix_ = J;
|
||||
}
|
||||
|
||||
template<std::size_t J, class U, class... A> void emplace_impl( mp11::mp_false, mp11::mp_false, A&&... a )
|
||||
{
|
||||
if( can_be_valueless<T...>::value )
|
||||
{
|
||||
std::size_t const K = valueless_index<T...>::value;
|
||||
|
||||
assert( K < sizeof...(T) );
|
||||
|
||||
try
|
||||
{
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::forward<A>(a)... );
|
||||
ix_ = J;
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
st1_.emplace( mp11::mp_size_t<K+1>() );
|
||||
ix_ = K+1;
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( std::is_nothrow_move_constructible<U>::value );
|
||||
|
||||
U tmp( std::forward<A>(a)... );
|
||||
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::move(tmp) );
|
||||
ix_ = J;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t I, class... A> BOOST_CXX14_CONSTEXPR void emplace( A&&... a )
|
||||
{
|
||||
std::size_t const J = I+1;
|
||||
using U = mp11::mp_at_c<variant<T...>, I>;
|
||||
|
||||
this->emplace_impl<J, U>( std::is_nothrow_constructible<U, A&&...>(), mp11::mp_all<detail::is_trivially_move_constructible<U>, detail::is_trivially_move_assignable<T>...>(), std::forward<A>(a)... );
|
||||
this->emplace_impl<J, U>( std::is_nothrow_constructible<U, A&&...>(), std::forward<A>(a)... );
|
||||
}
|
||||
};
|
||||
|
||||
@ -841,45 +839,14 @@ template<class... T> struct variant_base_impl<false, true, T...>
|
||||
|
||||
using U = mp11::mp_at_c<variant<T...>, I>;
|
||||
|
||||
if( std::is_nothrow_constructible<U, A...>::value )
|
||||
{
|
||||
_destroy();
|
||||
static_assert( std::is_nothrow_move_constructible<U>::value, "Logic error: U must be nothrow move constructible" );
|
||||
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::forward<A>(a)... );
|
||||
ix_ = J;
|
||||
}
|
||||
else if( can_be_valueless<T...>::value )
|
||||
{
|
||||
std::size_t const K = valueless_index<T...>::value;
|
||||
U tmp( std::forward<A>(a)... );
|
||||
|
||||
assert( K < sizeof...(T) );
|
||||
_destroy();
|
||||
|
||||
_destroy();
|
||||
|
||||
try
|
||||
{
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::forward<A>(a)... );
|
||||
ix_ = J;
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
st1_.emplace( mp11::mp_size_t<K+1>() );
|
||||
ix_ = K+1;
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( std::is_nothrow_move_constructible<U>::value );
|
||||
|
||||
U tmp( std::forward<A>(a)... );
|
||||
|
||||
_destroy();
|
||||
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::move(tmp) );
|
||||
ix_ = J;
|
||||
}
|
||||
st1_.emplace( mp11::mp_size_t<J>(), std::move(tmp) );
|
||||
ix_ = J;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1179,30 +1146,30 @@ public:
|
||||
class Ud = typename std::decay<U>::type,
|
||||
class E1 = typename std::enable_if< !std::is_same<Ud, variant>::value && !detail::is_in_place_index<Ud>::value && !detail::is_in_place_type<Ud>::value >::type,
|
||||
class V = detail::resolve_overload_type<U&&, T...>,
|
||||
class E2 = typename std::enable_if<std::is_constructible<V, U>::value>::type
|
||||
class E2 = typename std::enable_if<std::is_constructible<V, U&&>::value>::type
|
||||
>
|
||||
constexpr variant( U&& u )
|
||||
noexcept( std::is_nothrow_constructible<V, U>::value )
|
||||
noexcept( std::is_nothrow_constructible<V, U&&>::value )
|
||||
: variant_base( detail::resolve_overload_index<U&&, T...>(), std::forward<U>(u) )
|
||||
{
|
||||
}
|
||||
|
||||
template<class U, class... A, class I = mp11::mp_find<variant<T...>, U>, class E = typename std::enable_if<std::is_constructible<U, A...>::value>::type>
|
||||
template<class U, class... A, class I = mp11::mp_find<variant<T...>, U>, class E = typename std::enable_if<std::is_constructible<U, A&&...>::value>::type>
|
||||
constexpr explicit variant( in_place_type_t<U>, A&&... a ): variant_base( I(), std::forward<A>(a)... )
|
||||
{
|
||||
}
|
||||
|
||||
template<class U, class V, class... A, class I = mp11::mp_find<variant<T...>, U>, class E = typename std::enable_if<std::is_constructible<U, std::initializer_list<V>&, A...>::value>::type>
|
||||
template<class U, class V, class... A, class I = mp11::mp_find<variant<T...>, U>, class E = typename std::enable_if<std::is_constructible<U, std::initializer_list<V>&, A&&...>::value>::type>
|
||||
constexpr explicit variant( in_place_type_t<U>, std::initializer_list<V> il, A&&... a ): variant_base( I(), il, std::forward<A>(a)... )
|
||||
{
|
||||
}
|
||||
|
||||
template<std::size_t I, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, A...>::value>::type>
|
||||
template<std::size_t I, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, A&&...>::value>::type>
|
||||
constexpr explicit variant( in_place_index_t<I>, A&&... a ): variant_base( mp11::mp_size_t<I>(), std::forward<A>(a)... )
|
||||
{
|
||||
}
|
||||
|
||||
template<std::size_t I, class V, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, std::initializer_list<V>&, A...>::value>::type>
|
||||
template<std::size_t I, class V, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, std::initializer_list<V>&, A&&...>::value>::type>
|
||||
constexpr explicit variant( in_place_index_t<I>, std::initializer_list<V> il, A&&... a ): variant_base( mp11::mp_size_t<I>(), il, std::forward<A>(a)... )
|
||||
{
|
||||
}
|
||||
@ -1226,14 +1193,7 @@ private:
|
||||
|
||||
template<class I> void operator()( I i ) const
|
||||
{
|
||||
if( this_->index() == i )
|
||||
{
|
||||
this_->_get_impl( i ) = r._get_impl( i );
|
||||
}
|
||||
else
|
||||
{
|
||||
this_->variant_base::template emplace<I::value>( r._get_impl( i ) );
|
||||
}
|
||||
this_->variant_base::template emplace<I::value>( r._get_impl( i ) );
|
||||
}
|
||||
};
|
||||
|
||||
@ -1244,7 +1204,7 @@ public:
|
||||
class E3 = mp11::mp_if<mp11::mp_all<std::is_copy_constructible<T>..., std::is_copy_assignable<T>...>, E1>
|
||||
>
|
||||
BOOST_CXX14_CONSTEXPR variant& operator=( variant const & r )
|
||||
noexcept( mp11::mp_all<std::is_nothrow_copy_constructible<T>..., std::is_nothrow_copy_assignable<T>...>::value )
|
||||
noexcept( mp11::mp_all<std::is_nothrow_copy_constructible<T>...>::value )
|
||||
{
|
||||
mp11::mp_with_index<sizeof...(T)>( r.index(), L3{ this, r } );
|
||||
return *this;
|
||||
@ -1268,14 +1228,7 @@ private:
|
||||
|
||||
template<class I> void operator()( I i ) const
|
||||
{
|
||||
if( this_->index() == i )
|
||||
{
|
||||
this_->_get_impl( i ) = std::move( r._get_impl( i ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
this_->variant_base::template emplace<I::value>( std::move( r._get_impl( i ) ) );
|
||||
}
|
||||
this_->variant_base::template emplace<I::value>( std::move( r._get_impl( i ) ) );
|
||||
}
|
||||
};
|
||||
|
||||
@ -1286,7 +1239,7 @@ public:
|
||||
class E3 = mp11::mp_if<mp11::mp_all<std::is_move_constructible<T>..., std::is_move_assignable<T>...>, E1>
|
||||
>
|
||||
variant& operator=( variant && r )
|
||||
noexcept( mp11::mp_all<std::is_nothrow_move_constructible<T>..., std::is_nothrow_move_assignable<T>...>::value )
|
||||
noexcept( mp11::mp_all<std::is_nothrow_move_constructible<T>...>::value )
|
||||
{
|
||||
mp11::mp_with_index<sizeof...(T)>( r.index(), L4{ this, r } );
|
||||
return *this;
|
||||
@ -1295,29 +1248,20 @@ public:
|
||||
template<class U,
|
||||
class E1 = typename std::enable_if<!std::is_same<typename std::decay<U>::type, variant>::value>::type,
|
||||
class V = detail::resolve_overload_type<U, T...>,
|
||||
class E2 = typename std::enable_if<std::is_assignable<V&, U>::value && std::is_constructible<V, U>::value>::type
|
||||
class E2 = typename std::enable_if<std::is_assignable<V&, U&&>::value && std::is_constructible<V, U&&>::value>::type
|
||||
>
|
||||
BOOST_CXX14_CONSTEXPR variant& operator=( U&& u )
|
||||
noexcept( std::is_nothrow_assignable<V&, U>::value && std::is_nothrow_constructible<V, U>::value )
|
||||
noexcept( std::is_nothrow_constructible<V, U&&>::value )
|
||||
{
|
||||
std::size_t const I = detail::resolve_overload_index<U, T...>::value;
|
||||
|
||||
if( index() == I )
|
||||
{
|
||||
_get_impl( mp11::mp_size_t<I>() ) = std::forward<U>(u);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->template emplace<I>( std::forward<U>(u) );
|
||||
}
|
||||
|
||||
this->template emplace<I>( std::forward<U>(u) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
|
||||
template<class U, class... A,
|
||||
class E = typename std::enable_if< mp11::mp_count<variant<T...>, U>::value == 1 && std::is_constructible<U, A...>::value >::type>
|
||||
class E = typename std::enable_if< mp11::mp_count<variant<T...>, U>::value == 1 && std::is_constructible<U, A&&...>::value >::type>
|
||||
BOOST_CXX14_CONSTEXPR U& emplace( A&&... a )
|
||||
{
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
@ -1326,7 +1270,7 @@ public:
|
||||
}
|
||||
|
||||
template<class U, class V, class... A,
|
||||
class E = typename std::enable_if< mp11::mp_count<variant<T...>, U>::value == 1 && std::is_constructible<U, std::initializer_list<V>&, A...>::value >::type>
|
||||
class E = typename std::enable_if< mp11::mp_count<variant<T...>, U>::value == 1 && std::is_constructible<U, std::initializer_list<V>&, A&&...>::value >::type>
|
||||
BOOST_CXX14_CONSTEXPR U& emplace( std::initializer_list<V> il, A&&... a )
|
||||
{
|
||||
using I = mp11::mp_find<variant<T...>, U>;
|
||||
@ -1334,14 +1278,14 @@ public:
|
||||
return _get_impl( I() );
|
||||
}
|
||||
|
||||
template<std::size_t I, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, A...>::value>::type>
|
||||
template<std::size_t I, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, A&&...>::value>::type>
|
||||
BOOST_CXX14_CONSTEXPR variant_alternative_t<I, variant<T...>>& emplace( A&&... a )
|
||||
{
|
||||
variant_base::template emplace<I>( std::forward<A>(a)... );
|
||||
return _get_impl( mp11::mp_size_t<I>() );
|
||||
}
|
||||
|
||||
template<std::size_t I, class V, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, std::initializer_list<V>&, A...>::value>::type>
|
||||
template<std::size_t I, class V, class... A, class E = typename std::enable_if<std::is_constructible<mp11::mp_at_c<variant<T...>, I>, std::initializer_list<V>&, A&&...>::value>::type>
|
||||
BOOST_CXX14_CONSTEXPR variant_alternative_t<I, variant<T...>>& emplace( std::initializer_list<V> il, A&&... a )
|
||||
{
|
||||
variant_base::template emplace<I>( il, std::forward<A>(a)... );
|
||||
@ -1350,6 +1294,11 @@ public:
|
||||
|
||||
// value status
|
||||
|
||||
constexpr bool valueless_by_exception() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using variant_base::index;
|
||||
|
||||
// swap
|
||||
@ -1386,11 +1335,6 @@ public:
|
||||
|
||||
// private accessors
|
||||
|
||||
constexpr int _real_index() const noexcept
|
||||
{
|
||||
return this->ix_;
|
||||
}
|
||||
|
||||
using variant_base::_get_impl;
|
||||
|
||||
// converting constructors (extension)
|
||||
@ -1454,7 +1398,7 @@ private:
|
||||
|
||||
template<class... U, class V> static variant<U...> _subset_impl( mp11::mp_size_t<sizeof...(U)>, V && /*v*/ )
|
||||
{
|
||||
throw bad_variant_access();
|
||||
detail::throw_bad_variant_access();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -4,7 +4,7 @@
|
||||
</head>
|
||||
<body>
|
||||
Automatic redirection failed, please go to
|
||||
<a href="doc/html/mp11.html">doc/html/variant2.html</a>.
|
||||
<a href="doc/html/variant2.html">doc/html/variant2.html</a>.
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
|
@ -7,7 +7,7 @@
|
||||
"maintainers": [
|
||||
"Peter Dimov <pdimov -at- pdimov.com>"
|
||||
],
|
||||
"description": "A never-valueless implementation of std::variant.",
|
||||
"description": "A never-valueless, strong guarantee implementation of std::variant.",
|
||||
"category": [
|
||||
"Containers", "Data"
|
||||
]
|
||||
|
11
test/CMakeLists.txt
Normal file
11
test/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright 2018, 2019 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
|
||||
|
||||
if(HAVE_BOOST_TEST)
|
||||
|
||||
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::variant2 Boost::core)
|
||||
|
||||
endif()
|
32
test/Jamfile
32
test/Jamfile
@ -1,6 +1,6 @@
|
||||
# Boost.Variant2 Library Test Jamfile
|
||||
#
|
||||
# Copyright 2015-2017 Peter Dimov
|
||||
# Copyright 2015-2019 Peter Dimov
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# See accompanying file LICENSE_1_0.txt or copy at
|
||||
@ -9,7 +9,21 @@
|
||||
import testing ;
|
||||
import ../../config/checks/config : requires ;
|
||||
|
||||
project : requirements [ requires cxx11_variadic_templates cxx11_template_aliases cxx11_decltype cxx11_constexpr ] ;
|
||||
project
|
||||
: default-build
|
||||
|
||||
<warnings>all
|
||||
|
||||
: requirements
|
||||
|
||||
[ requires cxx11_variadic_templates cxx11_template_aliases cxx11_decltype cxx11_constexpr ]
|
||||
|
||||
<toolset>msvc:<warnings-as-errors>on
|
||||
<toolset>gcc:<warnings-as-errors>on
|
||||
<toolset>clang:<warnings-as-errors>on
|
||||
;
|
||||
|
||||
run quick.cpp ;
|
||||
|
||||
run variant_size.cpp ;
|
||||
run variant_alternative.cpp ;
|
||||
@ -77,3 +91,17 @@ run variant_convert_construct_throw.cpp ;
|
||||
|
||||
run variant_copy_assign_throw.cpp ;
|
||||
run variant_move_assign_throw.cpp ;
|
||||
|
||||
local NX =
|
||||
<exception-handling>off
|
||||
<toolset>msvc:<cxxflags>/wd4530
|
||||
<toolset>msvc:<cxxflags>/wd4577
|
||||
;
|
||||
|
||||
run variant_get_by_index.cpp throw_exception.cpp : : : $(NX) : variant_get_by_index_nx ;
|
||||
compile variant_get_by_index_cx.cpp : $(NX) : variant_get_by_index_cx_nx ;
|
||||
|
||||
run variant_get_by_type.cpp throw_exception.cpp : : : $(NX) : variant_get_by_type_nx ;
|
||||
compile variant_get_by_type_cx.cpp : $(NX) : variant_get_by_type_cx_nx ;
|
||||
|
||||
run variant_subset.cpp throw_exception.cpp : : : $(NX) : variant_subset_nx ;
|
||||
|
17
test/cmake_install_test/CMakeLists.txt
Normal file
17
test/cmake_install_test/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2018, 2019 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
|
||||
project(cmake_install_test LANGUAGES CXX)
|
||||
|
||||
find_package(boost_variant2 REQUIRED)
|
||||
|
||||
add_executable(quick ../quick.cpp)
|
||||
target_link_libraries(quick Boost::variant2)
|
||||
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
19
test/cmake_subdir_test/CMakeLists.txt
Normal file
19
test/cmake_subdir_test/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright 2018, 2019 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
|
||||
project(cmake_subdir_test LANGUAGES CXX)
|
||||
|
||||
add_subdirectory(../.. boostorg/variant2)
|
||||
add_subdirectory(../../../config boostorg/config)
|
||||
add_subdirectory(../../../mp11 boostorg/mp11)
|
||||
|
||||
add_executable(quick ../quick.cpp)
|
||||
target_link_libraries(quick Boost::variant2)
|
||||
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
15
test/quick.cpp
Normal file
15
test/quick.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
// Copyright 2019 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
int main()
|
||||
{
|
||||
variant<float, int> v( 2 );
|
||||
return get<1>( v ) == 2? 0: 1;
|
||||
}
|
20
test/throw_exception.cpp
Normal file
20
test/throw_exception.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
// Copyright 2019 Peter Dimov.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <exception>
|
||||
#include <cstdio>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
void throw_exception( std::exception const & e )
|
||||
{
|
||||
std::fprintf( stderr, "Exception: %s\n", e.what() );
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
} // namespace boost
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <stdexcept>
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <stdexcept>
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <stdexcept>
|
||||
|
@ -57,6 +57,21 @@ STATIC_ASSERT( !std::is_nothrow_move_constructible<X2>::value );
|
||||
STATIC_ASSERT( !std::is_nothrow_copy_assignable<X2>::value );
|
||||
STATIC_ASSERT( !std::is_nothrow_move_assignable<X2>::value );
|
||||
|
||||
struct Y1
|
||||
{
|
||||
};
|
||||
|
||||
struct Y2
|
||||
{
|
||||
~Y2() {}
|
||||
};
|
||||
|
||||
struct Guard
|
||||
{
|
||||
explicit Guard(int) {}
|
||||
Guard(Guard&&) = delete;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
@ -165,5 +180,15 @@ int main()
|
||||
BOOST_TEST_EQ( get<0>(v).v, 4 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<Y1, Guard> v;
|
||||
v.emplace<Guard>( 1 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<Y2, Guard> v;
|
||||
v.emplace<Guard>( 1 );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <stdexcept>
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <stdexcept>
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
@ -6,6 +6,10 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4702 ) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <type_traits>
|
||||
@ -111,8 +115,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; X1 is nothrow default-constructible
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,8 +132,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; X1 is nothrow default-constructible
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,8 +149,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; monostate
|
||||
BOOST_TEST_EQ( v.index(), 2 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,8 +166,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; monostate
|
||||
BOOST_TEST_EQ( v.index(), 2 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,8 +183,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; X1 is nothrow default-constructible
|
||||
BOOST_TEST_EQ( v.index(), 2 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,8 +200,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// basic guarantee; monostate
|
||||
BOOST_TEST_EQ( v.index(), 3 );
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,18 +234,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// X3 is not v2d::trivially_move_assignable on libstdc++ 4.x
|
||||
|
||||
if( v2d::is_trivially_move_assignable<X3>::value )
|
||||
{
|
||||
// all trivially destructible and move-assignable, no change
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// basic guarantee; X1 is nothrow default-constructible
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
}
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,18 +251,8 @@ int main()
|
||||
}
|
||||
catch( std::exception const& )
|
||||
{
|
||||
// X3 is not v2d::trivially_move_assignable on libstdc++ 4.x
|
||||
|
||||
if( v2d::is_trivially_move_assignable<X3>::value )
|
||||
{
|
||||
// all trivially destructible and move-assignable, no change
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// basic guarantee; monostate
|
||||
BOOST_TEST_EQ( v.index(), 2 );
|
||||
}
|
||||
// strong guarantee
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user