Compare commits

...

37 Commits

Author SHA1 Message Date
58b4a21deb Add CMake tests 2020-01-09 17:43:54 +02:00
5192a345ab Fix .travis.yml 2019-12-28 22:13:37 +02:00
74e6a04a60 Update .travis.yml 2019-12-28 21:35:20 +02:00
2af66dd815 Add CMake install support 2019-12-28 18:52:32 +02:00
a15364bd81 Update README 2019-09-21 19:12:34 +03:00
12758c076d Fix Travis and Appveyor links 2019-09-21 18:38:55 +03:00
a60f432b64 Add dist: trusty to .travis.yml 2019-06-17 15:07:45 +03:00
bc73c4b822 Fix return code of quick.cpp 2019-06-03 18:48:13 +03:00
566bde5fcc Add CMakeLists.txt 2019-06-03 17:50:13 +03:00
2e831fbdb2 Add gcc9, clang8 to Travis 2019-06-03 16:07:29 +03:00
2738f081cd Include boost/config.hpp for ::gets workaround on clang 3.8, 3.9 2019-06-03 15:43:43 +03:00
f5d9662946 Support -fno-exceptions 2019-06-03 02:46:59 +03:00
adeb970be6 Introduce detail::throw_bad_variant_access 2019-06-03 01:05:15 +03:00
6b617ae3a8 Update README 2019-05-17 17:39:42 +03:00
04f655e76a Fix link 2019-05-17 16:12:59 +03:00
c4f7f2d63a Delete doc/html 2019-05-17 16:11:43 +03:00
504abbe8eb Update description 2019-05-17 16:10:06 +03:00
8cb1789167 Update README.md 2019-05-17 03:30:40 +03:00
69b25cb42a Add design rationale 2019-05-13 19:15:53 +03:00
6b3a2b2b4d Merge branch 'develop' into feature/documentation 2019-05-12 20:15:31 +03:00
2d990f774a Remove _real_index 2019-05-12 19:34:21 +03:00
a083667a96 Apply corrections from #10. Closes #10. 2019-05-12 19:19:55 +03:00
e686dab3ea Update README 2019-05-12 19:02:59 +03:00
3041fff85c Remove expected 2019-05-12 18:56:54 +03:00
cacd99d69d Update documentation 2019-05-12 18:43:56 +03:00
64ea067c79 Merge branch 'develop' into feature/documentation 2019-05-12 02:21:12 +03:00
6390b5ed20 Update html 2019-05-12 02:02:58 +03:00
8691721a9c Update reference 2019-05-12 02:02:49 +03:00
7f7c74522b Strong guarantee on assignment 2019-05-12 01:46:55 +03:00
c3f9beaadc Strong guarantee on emplace 2019-05-12 00:38:21 +03:00
475ad691d6 Add changelog, design, implementation sections 2019-05-12 00:15:17 +03:00
5011dd8e1c Update html 2019-05-12 00:13:38 +03:00
f2980f97fc Add headings to overview 2019-05-12 00:08:27 +03:00
c8ebfd0481 Update html 2019-05-11 21:06:44 +03:00
6f190133be Update overview 2019-05-11 21:06:31 +03:00
c138488551 Update footer for Asciidoctor 2 2019-05-11 21:05:24 +03:00
6b69a1deca Asciidoctor 2 fixes 2019-05-11 21:04:45 +03:00
24 changed files with 875 additions and 3252 deletions

View File

@ -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
View 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()

View File

@ -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
View File

@ -1 +1,2 @@
/pdf/
/html/

View File

@ -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

View File

@ -1,5 +1,6 @@
<style>
*:not(pre)>code { background: none; color: #600000; }
:not(pre):not([class^=L])>code { background: none; color: #600000; }
</style>

View File

@ -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[]

View 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
View 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.

View 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].

View File

@ -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`.

View File

@ -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...`.
@ -504,16 +496,10 @@ Ensures: :: `index() == I`.
Returns: :: A reference to the new contained value.
Throws: ::
Nothing unless the initialization of the new contained value throws.
Exception Safety: :: On exception:
- If the list of alternatives contains `monostate`, the contained value
is either unchanged, or `monostate{}`;
- Otherwise, if the list of alternatives contains types for which
`is_nothrow_default_constructible_v` is `true`, the contained value
is either unchanged, or `Tj{}`, where `Tj` is the first such alternative;
- Otherwise, the contained value is unchanged.
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>
@ -531,16 +517,10 @@ Ensures: :: `index() == I`.
Returns: :: A reference to the new contained value.
Throws: ::
Nothing unless the initialization of the new contained value throws.
Exception Safety: :: On exception:
- If the list of alternatives contains `monostate`, the contained value
is either unchanged, or `monostate{}`;
- Otherwise, if the list of alternatives contains types for which
`is_nothrow_default_constructible_v` is `true`, the contained value
is either unchanged, or `Tj{}`, where `Tj` is the first such alternative;
- Otherwise, the contained value is unchanged.
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
@ -552,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
@ -727,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>
@ -767,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>
@ -798,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()`.
```
@ -817,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()`.
```
@ -836,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()`.
```
@ -846,7 +832,8 @@ template<class... T>
[none]
* {blank}
+
Returns: :: `w \<= v`.
Returns: ::
`w \<= v`.
### visit
@ -869,7 +856,8 @@ template<class... T>
[none]
* {blank}
+
Effects: :: Equivalent to `v.swap(w)`.
Effects: ::
Equivalent to `v.swap(w)`.
### bad_variant_access

View File

@ -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

View File

@ -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
@ -283,7 +308,7 @@ 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)
@ -292,11 +317,11 @@ template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T
#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
@ -305,7 +330,7 @@ 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)
@ -314,11 +339,11 @@ template<std::size_t I, class... T> constexpr variant_alternative_t<I, variant<T
#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
@ -363,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)
@ -374,11 +399,11 @@ template<class U, class... T> constexpr U&& get(variant<T...>&& v)
#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
@ -390,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)
@ -401,11 +426,11 @@ template<class U, class... T> constexpr U const&& get(variant<T...> const&& v)
#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
@ -598,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 {};
@ -649,7 +671,7 @@ 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&&..." );
@ -657,7 +679,7 @@ template<class... T> struct variant_base_impl<true, true, T...>
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" );
@ -667,37 +689,12 @@ template<class... T> struct variant_base_impl<true, true, T...>
ix_ = J;
}
template<std::size_t J, class U, class... A> void emplace_impl( mp11::mp_false, mp11::mp_false, A&&... a )
{
static_assert( can_be_valueless<T...>::value, "Logic error: T... must have a fallback type" );
std::size_t const K = valueless_index<T...>::value;
static_assert( K < sizeof...(T), "Logic error: T... must have a fallback index" );
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;
}
}
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>;
constexpr bool B1 = can_be_valueless<T...>::value;
constexpr bool B2 = mp11::mp_all<detail::is_trivially_move_constructible<U>, detail::is_trivially_move_assignable<T>...>::value;
this->emplace_impl<J, U>( std::is_nothrow_constructible<U, A&&...>(), mp11::mp_bool<B2 || !B1>(), std::forward<A>(a)... );
this->emplace_impl<J, U>( std::is_nothrow_constructible<U, A&&...>(), std::forward<A>(a)... );
}
};
@ -836,42 +833,12 @@ template<class... T> struct variant_base_impl<false, true, T...>
return st1_.get( mp11::mp_size_t<I+1>() );
}
template<std::size_t J, class U, class... A> void emplace_impl( mp11::mp_int<0>, A&&... a )
template<std::size_t I, class... A> void emplace( A&&... a )
{
static_assert( std::is_nothrow_constructible<U, A&&...>::value, "Logic error: U must be nothrow constructible from A&&..." );
size_t const J = I+1;
_destroy();
using U = mp11::mp_at_c<variant<T...>, I>;
st1_.emplace( mp11::mp_size_t<J>(), std::forward<A>(a)... );
ix_ = J;
}
template<std::size_t J, class U, class... A> void emplace_impl( mp11::mp_int<1>, A&&... a )
{
static_assert( can_be_valueless<T...>::value, "Logic error: T... must have a fallback type" );
std::size_t const K = valueless_index<T...>::value;
static_assert( K < sizeof...(T), "Logic error: T... must have a fallback index" );
_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;
}
}
template<std::size_t J, class U, class... A> void emplace_impl( mp11::mp_int<2>, 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)... );
@ -881,17 +848,6 @@ template<class... T> struct variant_base_impl<false, true, T...>
st1_.emplace( mp11::mp_size_t<J>(), std::move(tmp) );
ix_ = J;
}
template<std::size_t I, class... A> void emplace( A&&... a )
{
size_t const J = I+1;
using U = mp11::mp_at_c<variant<T...>, I>;
int const D = std::is_nothrow_constructible<U, A&&...>::value? 0: ( can_be_valueless<T...>::value? 1: 2 );
this->emplace_impl<J, U>( mp11::mp_int<D>(), std::forward<A>(a)... );
}
};
// not trivially destructible, double buffered
@ -1237,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 ) );
}
};
@ -1255,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;
@ -1279,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 ) ) );
}
};
@ -1297,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;
@ -1309,19 +1251,10 @@ public:
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;
}
@ -1402,11 +1335,6 @@ public:
// private accessors
constexpr int _real_index() const noexcept
{
return this->ix_;
}
using variant_base::_get_impl;
// converting constructors (extension)
@ -1470,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:

View File

@ -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>
<!--

View File

@ -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
View 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()

View File

@ -23,6 +23,8 @@ project
<toolset>clang:<warnings-as-errors>on
;
run quick.cpp ;
run variant_size.cpp ;
run variant_alternative.cpp ;
@ -89,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 ;

View 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>)

View 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
View 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
View 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

View File

@ -115,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 );
}
}
@ -132,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 );
}
}
@ -149,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 );
}
}
@ -166,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 );
}
}
@ -183,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 );
}
}
@ -200,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 );
}
}
@ -234,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 );
}
}
@ -261,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 );
}
}