forked from boostorg/variant2
Compare commits
164 Commits
pre-review
...
esp-idf-co
Author | SHA1 | Date | |
---|---|---|---|
5c0274a76e | |||
d34658f4c8 | |||
5e2bce1baa | |||
573ef77382 | |||
f6cad272d4 | |||
3fc7540733 | |||
e4b4b3f02b | |||
44d38c4111 | |||
1ebfb3b689 | |||
8af1fbde8e | |||
26ce33597d | |||
c89713b978 | |||
10c441c8d7 | |||
09ec260780 | |||
2da13befd7 | |||
e668c099ce | |||
aebcb9792d | |||
8fd9f830a9 | |||
ed4bebff3d | |||
ad06c9b923 | |||
de4eb8b6d2 | |||
4d69db1441 | |||
095d9770c4 | |||
cca476fbbc | |||
c76af4d3cf | |||
1af7b165cf | |||
49bff72be0 | |||
59780ba08e | |||
edd70cfd66 | |||
86c2782dff | |||
dce8174550 | |||
9d40bc80f1 | |||
be6ddf9fdc | |||
4153a535a0 | |||
f374ad68a3 | |||
3bc9a57c9b | |||
3ca95a65df | |||
ae1f72671e | |||
a2dab8c7d3 | |||
f39a71ed2f | |||
06dde96400 | |||
8be0a4a5fe | |||
90cda5339f | |||
afb0aafd64 | |||
f586dc8848 | |||
20b9175932 | |||
fb3ce863ff | |||
8ec0bf448a | |||
6d19e6be68 | |||
d8552b23ae | |||
f44aba09a2 | |||
99cc1db385 | |||
6d848c5af6 | |||
3c9f4e56bf | |||
3015e56bcb | |||
7c37053950 | |||
60995edb41 | |||
84a2c175d0 | |||
ea3268feb4 | |||
6de876954a | |||
efc1d5acca | |||
453b00dec8 | |||
c6186e0a95 | |||
24ccee2104 | |||
93b8618e94 | |||
1ebc29aa02 | |||
d3db874762 | |||
5586ebaa64 | |||
bede3777a8 | |||
b302dd5912 | |||
2ad6fed07a | |||
84ea994325 | |||
465e5bac3d | |||
41829b0fb1 | |||
b57d75ff80 | |||
03019860df | |||
8ec4720d2d | |||
75f574fc48 | |||
1d79adfca0 | |||
fa92e40b35 | |||
a403f82691 | |||
a7d0da59ad | |||
fa872cb835 | |||
93204676f5 | |||
772ef0d312 | |||
f3b3b533aa | |||
4b60dee4b6 | |||
76c67c9b21 | |||
308bd731a5 | |||
719c43316f | |||
d6680df4e5 | |||
8cf72527a6 | |||
b649a39776 | |||
37f6efab5d | |||
5259bdd5fc | |||
9964e77dd2 | |||
57ab36bd95 | |||
fac5992e45 | |||
972280e59f | |||
ef2ef292b9 | |||
3b2fb1a48a | |||
4f81882bfd | |||
17bc06b090 | |||
c4cad3e96a | |||
83ff667813 | |||
5e76451843 | |||
c2b5d101eb | |||
a92185a86d | |||
a795f9cf01 | |||
f6e2e78d7d | |||
1f05cdfb17 | |||
1203aa97c0 | |||
b3229d48b7 | |||
be11d94adc | |||
fece11142c | |||
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 |
358
.github/workflows/ci.yml
vendored
Normal file
358
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,358 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "03,11"
|
||||
os: ubuntu-18.04
|
||||
install: g++-4.8
|
||||
- toolset: gcc-5
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-5
|
||||
- toolset: gcc-6
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-6
|
||||
- toolset: gcc-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-8
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
install: g++-8
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-10
|
||||
- toolset: gcc-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-11
|
||||
- toolset: clang
|
||||
compiler: clang++-3.9
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-3.9
|
||||
- toolset: clang
|
||||
compiler: clang++-4.0
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-4.0
|
||||
- toolset: clang
|
||||
compiler: clang++-5.0
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: clang-5.0
|
||||
- toolset: clang
|
||||
compiler: clang++-6.0
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-6.0
|
||||
- toolset: clang
|
||||
compiler: clang++-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-7
|
||||
- toolset: clang
|
||||
compiler: clang++-8
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-8
|
||||
- toolset: clang
|
||||
compiler: clang++-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-9
|
||||
- toolset: clang
|
||||
compiler: clang++-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-10
|
||||
- toolset: clang
|
||||
compiler: clang++-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-11
|
||||
- toolset: clang
|
||||
compiler: clang++-12
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: clang-12
|
||||
- toolset: clang
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: macos-10.15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
- name: Create user-config.jam
|
||||
if: matrix.compiler
|
||||
run: |
|
||||
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root
|
||||
./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} variant=debug,release
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: msvc-14.0
|
||||
cxxstd: "14"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.1
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2016
|
||||
- toolset: msvc-14.2
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.3
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: gcc
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
addrmd: 64
|
||||
os: windows-2019
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Boost
|
||||
shell: cmd
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
|
||||
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
|
||||
echo LIBRARY: %LIBRARY%
|
||||
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
|
||||
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
|
||||
echo GITHUB_REF: %GITHUB_REF%
|
||||
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
|
||||
set BOOST_BRANCH=develop
|
||||
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
|
||||
echo BOOST_BRANCH: %BOOST_BRANCH%
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
|
||||
- name: Run tests
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release
|
||||
|
||||
posix-cmake-subdir:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: macos-10.15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Use library with add_subdirectory
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test
|
||||
mkdir __build__ && cd __build__
|
||||
cmake ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
posix-cmake-install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: macos-10.15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target install
|
||||
|
||||
- name: Use the installed library
|
||||
run: |
|
||||
cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__
|
||||
cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
|
||||
cmake --build .
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
posix-cmake-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: macos-10.15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON ..
|
||||
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target tests
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error
|
148
.travis.yml
148
.travis.yml
@ -1,12 +1,10 @@
|
||||
# Copyright 2016-2019 Peter Dimov
|
||||
# Copyright 2016-2021 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
language: cpp
|
||||
|
||||
sudo: false
|
||||
|
||||
python: "2.7"
|
||||
dist: xenial
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -25,8 +23,14 @@ matrix:
|
||||
|
||||
include:
|
||||
- os: linux
|
||||
compiler: g++
|
||||
env: TOOLSET=gcc COMPILER=g++ CXXSTD=11
|
||||
compiler: g++-4.8
|
||||
env: TOOLSET=gcc COMPILER=g++-4.8 CXXSTD=11
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.8
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- os: linux
|
||||
compiler: g++-4.9
|
||||
@ -79,12 +83,23 @@ matrix:
|
||||
- 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
|
||||
compiler: g++-9
|
||||
env: TOOLSET=gcc COMPILER=g++-9 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-8
|
||||
- g++-9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: g++-10
|
||||
env: UBSAN=1 TOOLSET=gcc COMPILER=g++-10 CXXSTD=11,14,17,2a UBSAN_OPTIONS=print_stacktrace=1 LINKFLAGS=-fuse-ld=gold
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-10
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
@ -98,7 +113,6 @@ matrix:
|
||||
- libstdc++-4.9-dev
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.5
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.6
|
||||
@ -109,7 +123,6 @@ matrix:
|
||||
- clang-3.6
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.6
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.7
|
||||
@ -120,7 +133,6 @@ matrix:
|
||||
- clang-3.7
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.7
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.8
|
||||
@ -131,7 +143,6 @@ matrix:
|
||||
- clang-3.8
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.8
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-3.9
|
||||
@ -142,7 +153,6 @@ matrix:
|
||||
- clang-3.9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.9
|
||||
|
||||
- os: linux
|
||||
compiler: clang++-4.0
|
||||
@ -153,9 +163,9 @@ matrix:
|
||||
- clang-4.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-5.0
|
||||
env: TOOLSET=clang COMPILER=clang++-5.0 CXXSTD=11,14,1z
|
||||
addons:
|
||||
@ -164,9 +174,9 @@ matrix:
|
||||
- clang-5.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-5.0
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-6.0
|
||||
env: TOOLSET=clang COMPILER=clang++-6.0 CXXSTD=11,14,17
|
||||
addons:
|
||||
@ -175,9 +185,9 @@ matrix:
|
||||
- clang-6.0
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-6.0
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-7
|
||||
env: TOOLSET=clang COMPILER=clang++-7 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
@ -186,20 +196,74 @@ matrix:
|
||||
- clang-7
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-7
|
||||
- llvm-toolchain-bionic-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
|
||||
dist: bionic
|
||||
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-bionic-8
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
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/bionic/ llvm-toolchain-bionic-9 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-10
|
||||
env: TOOLSET=clang COMPILER=clang++-10 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-10
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-11
|
||||
env: TOOLSET=clang COMPILER=clang++-11 CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-11
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-12
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++-12 CXXSTD=11,14,17,2a UBSAN_OPTIONS=print_stacktrace=1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-12
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: clang++-libc++
|
||||
env: TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,1z
|
||||
addons:
|
||||
@ -208,8 +272,9 @@ matrix:
|
||||
- libc++-dev
|
||||
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang++-libc++
|
||||
env: UBSAN=1 TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,1z UBSAN_OPTIONS=print_stacktrace=1
|
||||
env: TOOLSET=clang COMPILER=clang++-libc++ CXXSTD=11,14,17,2a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@ -217,15 +282,46 @@ matrix:
|
||||
|
||||
- os: osx
|
||||
compiler: clang++
|
||||
env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z
|
||||
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 -DBUILD_TESTING=ON -DBoost_VERBOSE=1 -DBOOST_INCLUDE_LIBRARIES=variant2 ..
|
||||
- ctest --output-on-failure -R boost_variant2
|
||||
|
||||
- os: linux
|
||||
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:
|
||||
- pip install --user cmake
|
||||
- mkdir __build__ && cd __build__
|
||||
- cmake -DBOOST_ENABLE_CMAKE=1 -DBoost_VERBOSE=1 -DBOOST_INCLUDE_LIBRARIES=variant2 -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
|
||||
|
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
||||
# Generated by `boostdep --cmake variant2`
|
||||
# Copyright 2020, 2021 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
if(NOT DEFINED IDF_TARGET)
|
||||
|
||||
cmake_minimum_required(VERSION 3.8...3.20)
|
||||
|
||||
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::assert
|
||||
Boost::config
|
||||
Boost::mp11
|
||||
)
|
||||
|
||||
target_compile_features(boost_variant2 INTERFACE cxx_std_11)
|
||||
|
||||
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
endif()
|
||||
|
||||
else()
|
||||
|
||||
FILE(GLOB_RECURSE headers include/*.h include/*.hpp)
|
||||
|
||||
idf_component_register(
|
||||
SRCS
|
||||
${headers}
|
||||
INCLUDE_DIRS
|
||||
include
|
||||
REQUIRES
|
||||
boost_assert
|
||||
boost_config
|
||||
boost_mp11
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
|
68
README.md
68
README.md
@ -1,64 +1,18 @@
|
||||
# variant2
|
||||
# Boost.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. It depends on
|
||||
Boost.Mp11, Boost.Config, and Boost.Assert.
|
||||
|
||||
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
|
||||
* clang++ 3.9 or later with `-std=c++11` or above
|
||||
* 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 [Github Actions](https://github.com/boostorg/variant2/actions) and
|
||||
[Appveyor](https://ci.appveyor.com/project/pdimov/variant2-fkab9).
|
||||
|
20
appveyor.yml
20
appveyor.yml
@ -1,4 +1,4 @@
|
||||
# Copyright 2016-2018 Peter Dimov
|
||||
# Copyright 2016-2021 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -20,7 +20,19 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: msvc-14.1
|
||||
ADDRMD: 32,64
|
||||
CXXSTD: 14,17
|
||||
CXXSTD: 14,17,latest
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: clang-win
|
||||
ADDRMD: 64
|
||||
CXXSTD: 14,17,latest
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: msvc-14.2
|
||||
ADDRMD: 32,64
|
||||
CXXSTD: 14,17,latest
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
ADDRMD: 64
|
||||
CXXSTD: 14,17,latest
|
||||
|
||||
install:
|
||||
- set BOOST_BRANCH=develop
|
||||
@ -28,8 +40,6 @@ install:
|
||||
- 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
|
||||
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\variant2\
|
||||
- python tools/boostdep/depinst/depinst.py variant2
|
||||
@ -41,4 +51,4 @@ build: off
|
||||
test_script:
|
||||
- if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
- if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
- b2 libs/variant2/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release
|
||||
- b2 -j3 libs/variant2/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
|
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>
|
||||
|
@ -13,10 +13,15 @@ Peter Dimov
|
||||
:toclevels: 4
|
||||
:idprefix:
|
||||
:docinfo: private-footer
|
||||
:source-highlighter: rouge
|
||||
:source-language: c++
|
||||
|
||||
: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[]
|
||||
|
||||
|
43
doc/variant2/changelog.adoc
Normal file
43
doc/variant2/changelog.adoc
Normal file
@ -0,0 +1,43 @@
|
||||
////
|
||||
Copyright 2019-2021 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#changelog]
|
||||
# Revision History
|
||||
:idprefix: changelog_
|
||||
|
||||
## Changes in 1.79.0
|
||||
|
||||
* Added `operator<<` for `monostate`.
|
||||
|
||||
## Changes in 1.78.0
|
||||
|
||||
* Added `<boost/variant2.hpp>`.
|
||||
* Added `unsafe_get<I>`.
|
||||
* Added `visit_by_index`.
|
||||
* Added `operator<<`.
|
||||
|
||||
## Changes in 1.76.0
|
||||
|
||||
* Improved generated code for the double buffered case.
|
||||
|
||||
## Changes in 1.74.0
|
||||
|
||||
* Added support for derived types in `visit`
|
||||
* Improved compilation performance for many (hundreds of) alternatives.
|
||||
* Added support for `visit<R>`
|
||||
|
||||
## Changes in 1.73.0
|
||||
|
||||
* Added support for `std::hash`, `boost::hash`.
|
||||
* `variant<T...>` is now trivial when all types in `T...` are trivial.
|
||||
This improves performance by enabling it to be passed to, and returned
|
||||
from, functions in registers.
|
||||
|
||||
## 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.
|
189
doc/variant2/design.adoc
Normal file
189
doc/variant2/design.adoc
Normal file
@ -0,0 +1,189 @@
|
||||
////
|
||||
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.)
|
||||
* `unsafe_get`, an unchecked alternative to `get` and `get_if`, is provided
|
||||
as an extension.
|
||||
* `visit_by_index`, a visitation function that takes a single variant and a
|
||||
number of function objects, one per alternative, is provided as an extension.
|
||||
* 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.
|
22
doc/variant2/implementation.adoc
Normal file
22
doc/variant2/implementation.adoc
Normal file
@ -0,0 +1,22 @@
|
||||
////
|
||||
Copyright 2019 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#implementation]
|
||||
# Implementation
|
||||
:idprefix: implementation_
|
||||
|
||||
## Dependencies
|
||||
|
||||
This implementation only depends on Boost.Config, Boost.Assert, and Boost.Mp11.
|
||||
|
||||
## Supported Compilers
|
||||
|
||||
* GCC 4.8 or later with `-std=c++11` or above
|
||||
* Clang 3.9 or later with `-std=c++11` or above
|
||||
* Visual Studio 2015, 2017, 2019
|
||||
|
||||
Tested on https://github.com/boostorg/variant2/actions[Github Actions] 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`.
|
||||
|
@ -1,10 +1,7 @@
|
||||
////
|
||||
Copyright 2018, 2019 Peter Dimov
|
||||
|
||||
Copyright 2018-2021 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
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#reference]
|
||||
@ -117,6 +114,21 @@ template<class U, class... T>
|
||||
constexpr add_pointer_t<const U>
|
||||
get_if(const variant<T...>* v) noexcept;
|
||||
|
||||
// unsafe_get (extension)
|
||||
|
||||
template<size_t I, class... T>
|
||||
constexpr variant_alternative_t<I, variant<T...>>&
|
||||
unsafe_get(variant<T...>& v);
|
||||
template<size_t I, class... T>
|
||||
constexpr variant_alternative_t<I, variant<T...>>&&
|
||||
unsafe_get(variant<T...>&& v);
|
||||
template<size_t I, class... T>
|
||||
constexpr const variant_alternative_t<I, variant<T...>>&
|
||||
unsafe_get(const variant<T...>& v);
|
||||
template<size_t I, class... T>
|
||||
constexpr const variant_alternative_t<I, variant<T...>>&&
|
||||
unsafe_get(const variant<T...>&& v);
|
||||
|
||||
// relational operators
|
||||
|
||||
template<class... T>
|
||||
@ -132,11 +144,21 @@ template<class... T>
|
||||
template<class... T>
|
||||
constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
|
||||
|
||||
// swap
|
||||
|
||||
template<class... T>
|
||||
void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
|
||||
|
||||
// visit
|
||||
|
||||
template<class F, class... V>
|
||||
template<class R = /*unspecified*/, class F, class... V>
|
||||
constexpr /*see below*/ visit(F&& f, V&&... v);
|
||||
|
||||
// visit_by_index (extension)
|
||||
|
||||
template<class R = /*unspecified*/, class V, class... F>
|
||||
constexpr /*see below*/ visit_by_index(V&& v, F&&.. f);
|
||||
|
||||
// monostate
|
||||
|
||||
struct monostate {};
|
||||
@ -148,10 +170,15 @@ constexpr bool operator>(monostate, monostate) noexcept { return false; }
|
||||
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
|
||||
constexpr bool operator>=(monostate, monostate) noexcept { return true; }
|
||||
|
||||
// swap
|
||||
// stream insertion (extension)
|
||||
|
||||
template<class... T>
|
||||
void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
|
||||
template<class Ch, class Tr, class... T>
|
||||
std::basic_ostream<Ch, Tr>&
|
||||
operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );
|
||||
|
||||
template<class Ch, class Tr>
|
||||
std::basic_ostream<Ch, Tr>&
|
||||
operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );
|
||||
|
||||
// bad_variant_access
|
||||
|
||||
@ -247,6 +274,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 +413,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 +436,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 +462,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 +488,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 +503,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...`.
|
||||
|
||||
|
||||
@ -498,13 +517,16 @@ template<size_t I, class... A>
|
||||
+
|
||||
Requires: :: `I < sizeof...(T)`.
|
||||
Effects: ::
|
||||
Destroys the currently contained value, then initializes a new contained
|
||||
value as if using the expression `Ti(std::forward<A>(a)...)`.
|
||||
Initializes a new contained value as if using the expression
|
||||
`Ti(std::forward<A>(a)...)`, then destroys the currently contained value.
|
||||
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>
|
||||
@ -516,13 +538,16 @@ template<size_t I, class V, class... A>
|
||||
+
|
||||
Requires: :: `I < sizeof...(T)`.
|
||||
Effects: ::
|
||||
Destroys the currently contained value, then initializes a new contained
|
||||
value as if using the expression `Ti(il, std::forward<A>(a)...)`.
|
||||
Initializes a new contained value as if using the expression
|
||||
`Ti(il, std::forward<A>(a)...)`, then destroys the currently contained value.
|
||||
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 +559,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 +737,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 +779,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>
|
||||
@ -771,6 +802,34 @@ Requires: :: The type `U` occurs exactly once in `T...`. Otherwise, the
|
||||
Effects: :: Equivalent to: `return get_if<I>(v);` with `I` being
|
||||
the zero-based index of `U` in `T...`.
|
||||
|
||||
### unsafe_get (extension)
|
||||
|
||||
```
|
||||
template<size_t I, class... T>
|
||||
constexpr variant_alternative_t<I, variant<T...>>&
|
||||
unsafe_get(variant<T...>& v);
|
||||
```
|
||||
```
|
||||
template<size_t I, class... T>
|
||||
constexpr variant_alternative_t<I, variant<T...>>&&
|
||||
unsafe_get(variant<T...>&& v);
|
||||
```
|
||||
```
|
||||
template<size_t I, class... T>
|
||||
constexpr const variant_alternative_t<I, variant<T...>>&
|
||||
unsafe_get(const variant<T...>& v);
|
||||
```
|
||||
```
|
||||
template<size_t I, class... T>
|
||||
constexpr const variant_alternative_t<I, variant<T...>>&&
|
||||
unsafe_get(const variant<T...>&& v);
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Requires: :: `v.index() == I`.
|
||||
Returns: :: a reference to the object stored in the variant.
|
||||
|
||||
### Relational Operators
|
||||
|
||||
```
|
||||
@ -780,7 +839,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 +858,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 +877,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,19 +887,8 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `w \<= v`.
|
||||
|
||||
### visit
|
||||
|
||||
```
|
||||
template<class F, class... V>
|
||||
constexpr /*see below*/ visit(F&& f, V&&... v);
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `std::forward<F>(f)(get<I>(std::forward<V>(v))...)`, where
|
||||
`I...` is `v.index()...`.
|
||||
Returns: ::
|
||||
`w \<= v`.
|
||||
|
||||
### swap
|
||||
|
||||
@ -851,7 +899,69 @@ template<class... T>
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Effects: :: Equivalent to `v.swap(w)`.
|
||||
Effects: ::
|
||||
Equivalent to `v.swap(w)`.
|
||||
|
||||
### visit
|
||||
|
||||
```
|
||||
template<class R = /*unspecified*/, class F, class... V>
|
||||
constexpr /*see below*/ visit(F&& f, V&&... v);
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Returns: :: `std::forward<F>(f)(get<I>(std::forward<V>(v))...)`, where
|
||||
`I...` is `v.index()...`.
|
||||
Remarks: :: If `R` is given explicitly, as in `visit<int>`, the return
|
||||
type is `R`. Otherwise, it's deduced from `F`. All possible applications
|
||||
of `F` to the variant alternatives must have the same return type for
|
||||
this deduction to succeed.
|
||||
|
||||
### visit_by_index (extension)
|
||||
|
||||
```
|
||||
template<class R = /*unspecified*/, class V, class... F>
|
||||
constexpr /*see below*/ visit_by_index(V&& v, F&&.. f);
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Requires: :: `variant_size<V>::value == sizeof...(F)`, or the program is ill-formed.
|
||||
Returns: :: `std::forward<Fi>(fi)(get<i>(std::forward<V>(v)))`, where
|
||||
`i` is `v.index()` and `Fi` and `fi` are the `i`-th element of `F...` and `f...`
|
||||
accordingly.
|
||||
Remarks: :: If `R` is given explicitly, as in `visit_by_index<int>`, the return
|
||||
type is `R`. Otherwise, it's deduced from `F...` and `V`. All the applications
|
||||
of `Fi` to the corresponding variant alternatives must have the same return type
|
||||
for this deduction to succeed.
|
||||
|
||||
### Stream Insertion (extension)
|
||||
```
|
||||
template<class Ch, class Tr, class... T>
|
||||
std::basic_ostream<Ch, Tr>&
|
||||
operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Requires: ::
|
||||
`sizeof...(T) != 0`.
|
||||
Returns: ::
|
||||
`os << get<I>(v)`, where `I` is `v.index()`.
|
||||
|
||||
```
|
||||
template<class Ch, class Tr>
|
||||
std::basic_ostream<Ch, Tr>&
|
||||
operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );
|
||||
```
|
||||
[none]
|
||||
* {blank}
|
||||
+
|
||||
Effects: ::
|
||||
`os << "monostate"`.
|
||||
Returns: ::
|
||||
`os`.
|
||||
|
||||
### bad_variant_access
|
||||
|
||||
@ -868,3 +978,7 @@ public:
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## <boost/variant2.hpp>
|
||||
|
||||
This convenience header includes `<boost/variant2/variant.hpp>`.
|
||||
|
10
include/boost/variant2.hpp
Normal file
10
include/boost/variant2.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef BOOST_VARIANT2_HPP_INCLUDED
|
||||
#define BOOST_VARIANT2_HPP_INCLUDED
|
||||
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
|
||||
#endif // #ifndef BOOST_VARIANT2_HPP_INCLUDED
|
@ -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
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||
<!--
|
||||
|
@ -5,10 +5,11 @@
|
||||
"Peter Dimov"
|
||||
],
|
||||
"maintainers": [
|
||||
"Peter Dimov <pdimov -at- pdimov.com>"
|
||||
"Peter Dimov <pdimov -at- gmail.com>"
|
||||
],
|
||||
"description": "A never-valueless implementation of std::variant.",
|
||||
"description": "A never-valueless, strong guarantee implementation of std::variant.",
|
||||
"category": [
|
||||
"Containers", "Data"
|
||||
]
|
||||
],
|
||||
"cxxstd": "11"
|
||||
}
|
||||
|
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 Boost::container_hash)
|
||||
|
||||
endif()
|
67
test/Jamfile
67
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,22 +9,36 @@
|
||||
import testing ;
|
||||
import ../../config/checks/config : requires ;
|
||||
|
||||
project : requirements [ requires cxx11_variadic_templates cxx11_template_aliases cxx11_decltype cxx11_constexpr ] ;
|
||||
project
|
||||
: default-build
|
||||
|
||||
<warnings>extra
|
||||
|
||||
: 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 ;
|
||||
|
||||
run variant_holds_alternative.cpp ;
|
||||
compile variant_holds_alternative_cx.cpp ;
|
||||
compile variant_holds_alternative_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_get_by_index.cpp ;
|
||||
compile variant_get_by_index_cx.cpp ;
|
||||
compile variant_get_by_index_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_get_by_type.cpp ;
|
||||
compile variant_get_by_type_cx.cpp ;
|
||||
compile variant_get_by_type_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_default_construct.cpp ;
|
||||
compile variant_default_construct_cx.cpp ;
|
||||
compile variant_default_construct_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_copy_construct.cpp ;
|
||||
compile variant_copy_construct_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
@ -33,13 +47,13 @@ run variant_move_construct.cpp ;
|
||||
compile variant_move_construct_cx.cpp : [ requires cxx14_constexpr ] ;
|
||||
|
||||
run variant_value_construct.cpp ;
|
||||
compile variant_value_construct_cx.cpp ;
|
||||
compile variant_value_construct_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_in_place_index_construct.cpp ;
|
||||
compile variant_in_place_index_construct_cx.cpp ;
|
||||
compile variant_in_place_index_construct_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_in_place_type_construct.cpp ;
|
||||
compile variant_in_place_type_construct_cx.cpp ;
|
||||
compile variant_in_place_type_construct_cx.cpp : <toolset>msvc-14.0:<build>no ;
|
||||
|
||||
run variant_copy_assign.cpp ;
|
||||
compile variant_copy_assign_cx.cpp : [ requires cxx14_constexpr ] ;
|
||||
@ -77,3 +91,38 @@ 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) <toolset>msvc-14.0:<build>no : 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) <toolset>msvc-14.0:<build>no : variant_get_by_type_cx_nx ;
|
||||
|
||||
run variant_subset.cpp throw_exception.cpp : : : $(NX) : variant_subset_nx ;
|
||||
|
||||
run variant_hash.cpp ;
|
||||
|
||||
run variant_trivial.cpp ;
|
||||
run variant_special.cpp ;
|
||||
|
||||
run variant_visit_derived.cpp ;
|
||||
|
||||
run variant_many_types.cpp ;
|
||||
|
||||
run variant_visit_r.cpp : : :
|
||||
<toolset>gcc,<target-os>windows:<variant>release
|
||||
<toolset>gcc,<target-os>cygwin:<variant>release
|
||||
;
|
||||
|
||||
compile variant_derived_construct.cpp ;
|
||||
|
||||
run variant_visit_by_index.cpp ;
|
||||
|
||||
run variant_ostream_insert.cpp ;
|
||||
run is_output_streamable.cpp ;
|
||||
|
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>)
|
21
test/cmake_subdir_test/CMakeLists.txt
Normal file
21
test/cmake_subdir_test/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# 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(../../../assert boostorg/assert)
|
||||
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>)
|
41
test/is_output_streamable.cpp
Normal file
41
test/is_output_streamable.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2021 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
struct X
|
||||
{
|
||||
};
|
||||
|
||||
struct Y
|
||||
{
|
||||
};
|
||||
|
||||
std::ostream& operator<<( std::ostream& os, Y const& /*y*/ )
|
||||
{
|
||||
os << "Y()";
|
||||
return os;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, float>));
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, std::string>));
|
||||
BOOST_TEST_TRAIT_FALSE((boost::variant2::detail::is_output_streamable<std::ostream, void>));
|
||||
BOOST_TEST_TRAIT_FALSE((boost::variant2::detail::is_output_streamable<std::ostream, X>));
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, Y>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, boost::variant2::monostate>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, boost::variant2::variant<int, float, std::string>>));
|
||||
BOOST_TEST_TRAIT_FALSE((boost::variant2::detail::is_output_streamable<std::ostream, boost::variant2::variant<int, float, X>>));
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, boost::variant2::variant<int, float, Y>>));
|
||||
BOOST_TEST_TRAIT_TRUE((boost::variant2::detail::is_output_streamable<std::ostream, boost::variant2::variant<boost::variant2::monostate, int, float>>));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
13
test/quick.cpp
Normal file
13
test/quick.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2019 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2.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>
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
@ -48,12 +50,21 @@ struct Y
|
||||
Y( Y const& ) = delete;
|
||||
};
|
||||
|
||||
struct D
|
||||
{
|
||||
~D() {}
|
||||
};
|
||||
|
||||
inline bool operator==( D, D ) { return true; }
|
||||
|
||||
template<class V> static void test( V const & v )
|
||||
{
|
||||
V v2( v );
|
||||
|
||||
BOOST_TEST_EQ( v.index(), v2.index() );
|
||||
BOOST_TEST( v == v2 );
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_copy_constructible<V>));
|
||||
}
|
||||
|
||||
int main()
|
||||
@ -115,6 +126,12 @@ int main()
|
||||
test( v );
|
||||
}
|
||||
|
||||
#if !BOOST_WORKAROUND( __GNUC__, < 5 )
|
||||
|
||||
test( variant<D>() );
|
||||
|
||||
#endif
|
||||
|
||||
{
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_nothrow_copy_constructible<variant<int>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_nothrow_copy_constructible<variant<int const>>));
|
||||
|
@ -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>
|
||||
|
33
test/variant_derived_construct.cpp
Normal file
33
test/variant_derived_construct.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
template<class... T> class X: variant<T...>
|
||||
{
|
||||
using base = variant<T...>;
|
||||
using base::base;
|
||||
};
|
||||
|
||||
struct Y
|
||||
{
|
||||
Y( Y const& rhs ) = default;
|
||||
|
||||
template<class T> Y( T const& t )
|
||||
{
|
||||
t.bar();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
using W = X<int, double, Y>;
|
||||
|
||||
W a( 1 );
|
||||
W b( a );
|
||||
|
||||
(void)b;
|
||||
}
|
@ -57,6 +57,60 @@ 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
|
||||
{
|
||||
int v = 1;
|
||||
|
||||
Y1() = default;
|
||||
Y1(Y1 const&) = delete;
|
||||
Y1(Y1&&) = delete;
|
||||
};
|
||||
|
||||
STATIC_ASSERT( !std::is_copy_constructible<Y1>::value );
|
||||
STATIC_ASSERT( !std::is_move_constructible<Y1>::value );
|
||||
|
||||
struct Y2
|
||||
{
|
||||
int v = 2;
|
||||
|
||||
Y2() = default;
|
||||
Y2(Y2 const&) = delete;
|
||||
Y2(Y2&&) = delete;
|
||||
};
|
||||
|
||||
STATIC_ASSERT( !std::is_copy_constructible<Y2>::value );
|
||||
STATIC_ASSERT( !std::is_move_constructible<Y2>::value );
|
||||
|
||||
struct Z1
|
||||
{
|
||||
static int instances;
|
||||
|
||||
int v = 1;
|
||||
|
||||
Z1() { ++instances; }
|
||||
~Z1() { --instances; }
|
||||
|
||||
Z1(Z1 const&) = delete;
|
||||
Z1(Z1&&) = delete;
|
||||
};
|
||||
|
||||
int Z1::instances = 0;
|
||||
|
||||
struct Z2
|
||||
{
|
||||
static int instances;
|
||||
|
||||
int v = 2;
|
||||
|
||||
Z2() { ++instances; }
|
||||
~Z2() { --instances; }
|
||||
|
||||
Z2(Z2 const&) = delete;
|
||||
Z2(Z2&&) = delete;
|
||||
};
|
||||
|
||||
int Z2::instances = 0;
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
@ -177,5 +231,62 @@ int main()
|
||||
BOOST_TEST_EQ( get<0>(v).v, 4 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<Y1, Y2> v;
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
|
||||
v.emplace<0>();
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
|
||||
v.emplace<1>();
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
BOOST_TEST_EQ( get<1>(v).v, 2 );
|
||||
|
||||
v.emplace<1>();
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
BOOST_TEST_EQ( get<1>(v).v, 2 );
|
||||
|
||||
v.emplace<0>();
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<Z1, Z2> v;
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
BOOST_TEST_EQ( Z1::instances, 1 );
|
||||
BOOST_TEST_EQ( Z2::instances, 0 );
|
||||
|
||||
v.emplace<0>();
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
BOOST_TEST_EQ( Z1::instances, 1 );
|
||||
BOOST_TEST_EQ( Z2::instances, 0 );
|
||||
|
||||
v.emplace<1>();
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
BOOST_TEST_EQ( get<1>(v).v, 2 );
|
||||
BOOST_TEST_EQ( Z1::instances, 0 );
|
||||
BOOST_TEST_EQ( Z2::instances, 1 );
|
||||
|
||||
v.emplace<1>();
|
||||
BOOST_TEST_EQ( v.index(), 1 );
|
||||
BOOST_TEST_EQ( get<1>(v).v, 2 );
|
||||
BOOST_TEST_EQ( Z1::instances, 0 );
|
||||
BOOST_TEST_EQ( Z2::instances, 1 );
|
||||
|
||||
v.emplace<0>();
|
||||
BOOST_TEST_EQ( v.index(), 0 );
|
||||
BOOST_TEST_EQ( get<0>(v).v, 1 );
|
||||
BOOST_TEST_EQ( Z1::instances, 1 );
|
||||
BOOST_TEST_EQ( Z2::instances, 0 );
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ( Z1::instances, 0 );
|
||||
BOOST_TEST_EQ( Z2::instances, 0 );
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -88,5 +88,12 @@ int main()
|
||||
BOOST_TEST_NOT( v1 != v2 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<monostate> v1, v2;
|
||||
|
||||
BOOST_TEST( v1 == v2 );
|
||||
BOOST_TEST_NOT( v1 != v2 );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -93,6 +93,13 @@ int main()
|
||||
STATIC_ASSERT( !(v1 == v2) );
|
||||
STATIC_ASSERT( !(v1 != v2) );
|
||||
}
|
||||
|
||||
{
|
||||
constexpr variant<monostate> v1, v2;
|
||||
|
||||
STATIC_ASSERT( v1 == v2 );
|
||||
STATIC_ASSERT( !(v1 != v2) );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,12 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined( __clang__ ) && defined( __has_warning )
|
||||
# if __has_warning( "-Wdeprecated-volatile" )
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-volatile"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
@ -6,6 +6,12 @@
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined( __clang__ ) && defined( __has_warning )
|
||||
# if __has_warning( "-Wdeprecated-volatile" )
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-volatile"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
86
test/variant_hash.cpp
Normal file
86
test/variant_hash.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
// Copyright 2020 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4244 ) // conversion from int to float, possible loss of data
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
template<template<class...> class Hash, class T1, class T2, class T3> void test()
|
||||
{
|
||||
variant<T1, T2, T3> v1( in_place_index_t<0>{} );
|
||||
std::size_t h1 = Hash<decltype(v1)>()( v1 );
|
||||
|
||||
variant<T1, T2, T3> v2( in_place_index_t<1>{} );
|
||||
std::size_t h2 = Hash<decltype(v2)>()( v2 );
|
||||
|
||||
variant<T1, T2, T3> v3( in_place_index_t<2>{} );
|
||||
std::size_t h3 = Hash<decltype(v3)>()( v3 );
|
||||
|
||||
BOOST_TEST_NE( h1, h2 );
|
||||
BOOST_TEST_NE( h1, h3 );
|
||||
BOOST_TEST_NE( h2, h3 );
|
||||
}
|
||||
|
||||
template<template<class...> class Hash, class T> void test2()
|
||||
{
|
||||
variant<T> v1( 0 );
|
||||
std::size_t h1 = Hash<decltype(v1)>()( v1 );
|
||||
|
||||
variant<T> v2( 1 );
|
||||
std::size_t h2 = Hash<decltype(v2)>()( v2 );
|
||||
|
||||
variant<T> v3( 2 );
|
||||
std::size_t h3 = Hash<decltype(v3)>()( v3 );
|
||||
|
||||
BOOST_TEST_NE( h1, h2 );
|
||||
BOOST_TEST_NE( h1, h3 );
|
||||
BOOST_TEST_NE( h2, h3 );
|
||||
}
|
||||
|
||||
struct X
|
||||
{
|
||||
int m = 0;
|
||||
};
|
||||
|
||||
std::size_t hash_value( X const& x )
|
||||
{
|
||||
return boost::hash<int>()( x.m );
|
||||
}
|
||||
|
||||
struct Y {}; // no hash support
|
||||
|
||||
int main()
|
||||
{
|
||||
test<std::hash, monostate, monostate, monostate>();
|
||||
test<std::hash, int, int, float>();
|
||||
|
||||
test<boost::hash, monostate, monostate, monostate>();
|
||||
test<boost::hash, int, int, float>();
|
||||
|
||||
test<boost::hash, monostate, X, std::vector<X>>();
|
||||
|
||||
test2<std::hash, int>();
|
||||
test2<std::hash, float>();
|
||||
|
||||
test2<boost::hash, int>();
|
||||
test2<boost::hash, float>();
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1910) && ( !defined(_LIBCPP_STD_VER) || _LIBCPP_STD_VER > 11 )
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE(( detail::is_hash_enabled<Y> ));
|
||||
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -7,11 +7,8 @@
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
@ -108,9 +105,17 @@ int main()
|
||||
STATIC_ASSERT( v.index() == 4 );
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_GCC, >= 100000 && BOOST_GCC < 120000)
|
||||
|
||||
// no idea why this fails on g++ 10/11
|
||||
|
||||
#else
|
||||
|
||||
{
|
||||
constexpr variant<int, int, float, float, X, X> v( in_place_index_t<5>{}, 0, 0 );
|
||||
|
||||
STATIC_ASSERT( v.index() == 5 );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
@ -100,6 +102,12 @@ int main()
|
||||
STATIC_ASSERT( holds_alternative<X>(v) );
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_GCC, >= 100000 && BOOST_GCC < 120000)
|
||||
|
||||
// no idea why this fails on g++ 10/11
|
||||
|
||||
#else
|
||||
|
||||
{
|
||||
constexpr variant<int, int, float, float, X> v( in_place_type_t<X>{}, 0, 0 );
|
||||
|
||||
@ -107,4 +115,6 @@ int main()
|
||||
|
||||
STATIC_ASSERT( holds_alternative<X>(v) );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -81,5 +81,14 @@ int main()
|
||||
BOOST_TEST_NOT( v1 >= v2 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<monostate> v1, v2;
|
||||
|
||||
BOOST_TEST_NOT( v1 < v2 );
|
||||
BOOST_TEST_NOT( v1 > v2 );
|
||||
BOOST_TEST( v1 <= v2 );
|
||||
BOOST_TEST( v1 >= v2 );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -86,6 +86,15 @@ int main()
|
||||
STATIC_ASSERT( !(v1 <= v2) );
|
||||
STATIC_ASSERT( !(v1 >= v2) );
|
||||
}
|
||||
|
||||
{
|
||||
constexpr variant<monostate> v1, v2;
|
||||
|
||||
STATIC_ASSERT( !(v1 < v2) );
|
||||
STATIC_ASSERT( !(v1 > v2) );
|
||||
STATIC_ASSERT( v1 <= v2 );
|
||||
STATIC_ASSERT( v1 >= v2 );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
109
test/variant_many_types.cpp
Normal file
109
test/variant_many_types.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
|
||||
// Copyright 2020 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4503 ) // decorated name length exceeded
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
using namespace boost::mp11;
|
||||
|
||||
template<class I> struct X
|
||||
{
|
||||
static int const value = I::value;
|
||||
int v_;
|
||||
};
|
||||
|
||||
template<class I> int const X<I>::value;
|
||||
|
||||
template<class I> struct Y
|
||||
{
|
||||
static int const value = I::value;
|
||||
int v_;
|
||||
|
||||
Y() = default;
|
||||
Y( Y const& ) = default;
|
||||
|
||||
explicit Y( int v ): v_( v ) {}
|
||||
|
||||
Y& operator=( Y const& ) noexcept = default;
|
||||
|
||||
Y& operator=( Y&& r ) noexcept
|
||||
{
|
||||
v_ = r.v_;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<class I> int const Y<I>::value;
|
||||
|
||||
template<class I> struct Z
|
||||
{
|
||||
static int const value = I::value;
|
||||
int v_;
|
||||
|
||||
~Z() {}
|
||||
};
|
||||
|
||||
template<class I> int const Z<I>::value;
|
||||
|
||||
template<class V> struct F1
|
||||
{
|
||||
template<class T> void operator()( T ) const
|
||||
{
|
||||
int const i = T::value;
|
||||
|
||||
T t{ i * 2 };
|
||||
|
||||
using boost::variant2::get;
|
||||
|
||||
{
|
||||
V v( t );
|
||||
|
||||
BOOST_TEST_EQ( v.index(), i );
|
||||
BOOST_TEST_EQ( get<i>( v ).v_, t.v_ );
|
||||
BOOST_TEST_EQ( get<T>( v ).v_, t.v_ );
|
||||
}
|
||||
|
||||
{
|
||||
V const v( t );
|
||||
|
||||
BOOST_TEST_EQ( v.index(), i );
|
||||
BOOST_TEST_EQ( get<i>( v ).v_, t.v_ );
|
||||
BOOST_TEST_EQ( get<T>( v ).v_, t.v_ );
|
||||
}
|
||||
|
||||
{
|
||||
V v;
|
||||
|
||||
v = t;
|
||||
|
||||
BOOST_TEST_EQ( v.index(), i );
|
||||
BOOST_TEST_EQ( get<i>( v ).v_, t.v_ );
|
||||
BOOST_TEST_EQ( get<T>( v ).v_, t.v_ );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class V> void test()
|
||||
{
|
||||
mp_for_each<V>( F1<V>() );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int const N = 32;
|
||||
|
||||
using V = mp_rename<mp_iota_c<N>, boost::variant2::variant>;
|
||||
|
||||
test< mp_transform<X, V> >();
|
||||
test< mp_transform<Y, V> >();
|
||||
test< mp_transform<Z, V> >();
|
||||
|
||||
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>
|
||||
|
43
test/variant_ostream_insert.cpp
Normal file
43
test/variant_ostream_insert.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2021 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
template<class T> std::string to_string( T const& t )
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << t;
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
BOOST_TEST_EQ( to_string( monostate() ), "monostate" );
|
||||
}
|
||||
|
||||
{
|
||||
variant<monostate, int, float, std::string> v;
|
||||
|
||||
BOOST_TEST_EQ( to_string( v ), to_string( monostate() ) );
|
||||
|
||||
v = 1;
|
||||
BOOST_TEST_EQ( to_string( v ), to_string( 1 ) );
|
||||
|
||||
v = 3.14f;
|
||||
BOOST_TEST_EQ( to_string( v ), to_string( 3.14f ) );
|
||||
|
||||
v = "test";
|
||||
BOOST_TEST_EQ( to_string( v ), to_string( "test" ) );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
103
test/variant_special.cpp
Normal file
103
test/variant_special.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2020 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
# pragma warning(disable: 4503) // decorated name length exceeded
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#include <boost/mp11.hpp>
|
||||
using namespace boost::mp11;
|
||||
|
||||
//
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
struct D
|
||||
{
|
||||
~D() noexcept {}
|
||||
};
|
||||
|
||||
struct CC1
|
||||
{
|
||||
CC1( CC1 const& ) {}
|
||||
};
|
||||
|
||||
struct CC2
|
||||
{
|
||||
CC2( CC2 const& ) = delete;
|
||||
};
|
||||
|
||||
struct MC1
|
||||
{
|
||||
MC1( MC1 && ) {}
|
||||
};
|
||||
|
||||
struct MC2
|
||||
{
|
||||
MC2( MC2 && ) = delete;
|
||||
};
|
||||
|
||||
struct CA1
|
||||
{
|
||||
CA1& operator=( CA1 const& ) { return *this; }
|
||||
};
|
||||
|
||||
struct CA2
|
||||
{
|
||||
CA2& operator=( CA2 const& ) = delete;
|
||||
};
|
||||
|
||||
struct MA1
|
||||
{
|
||||
MA1& operator=( MA1 && ) { return *this; }
|
||||
};
|
||||
|
||||
struct MA2
|
||||
{
|
||||
MA2& operator=( MA2 && ) = delete;
|
||||
};
|
||||
|
||||
struct test
|
||||
{
|
||||
template<class... T> void operator()( mp_list<T...> ) const noexcept
|
||||
{
|
||||
using U = mp_inherit<T...>;
|
||||
|
||||
#if !BOOST_WORKAROUND( __GNUC__, < 5 )
|
||||
|
||||
BOOST_TEST_EQ( std::is_copy_constructible<variant<U>>::value, std::is_copy_constructible<U>::value );
|
||||
BOOST_TEST_EQ( std::is_nothrow_copy_constructible<variant<U>>::value, std::is_nothrow_copy_constructible<U>::value );
|
||||
|
||||
#endif
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
|
||||
|
||||
BOOST_TEST_EQ( std::is_move_constructible<variant<U>>::value, std::is_move_constructible<U>::value );
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_GE( std::is_move_constructible<variant<U>>::value, std::is_move_constructible<U>::value );
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_TEST_EQ( std::is_nothrow_move_constructible<variant<U>>::value, std::is_nothrow_move_constructible<U>::value );
|
||||
|
||||
BOOST_TEST_EQ( std::is_copy_assignable<variant<U>>::value, std::is_copy_constructible<U>::value && std::is_copy_assignable<U>::value );
|
||||
BOOST_TEST_EQ( std::is_nothrow_copy_assignable<variant<U>>::value, std::is_nothrow_copy_constructible<U>::value && std::is_copy_assignable<U>::value );
|
||||
|
||||
BOOST_TEST_EQ( std::is_move_assignable<variant<U>>::value, std::is_move_constructible<U>::value && std::is_move_assignable<U>::value );
|
||||
BOOST_TEST_EQ( std::is_nothrow_move_assignable<variant<U>>::value, std::is_nothrow_move_constructible<U>::value && std::is_move_assignable<U>::value );
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
mp_for_each< mp_power_set< mp_list<D, CC1, CC2, MC1, MC2, CA1, CA2, MA1, MA2> > >( test() );
|
||||
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 <boost/core/lightweight_test_trait.hpp>
|
||||
|
95
test/variant_trivial.cpp
Normal file
95
test/variant_trivial.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2020 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
# pragma warning(disable: 4503) // decorated name length exceeded
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#include <boost/mp11.hpp>
|
||||
using namespace boost::mp11;
|
||||
|
||||
//
|
||||
|
||||
struct D
|
||||
{
|
||||
~D() noexcept {}
|
||||
};
|
||||
|
||||
struct CC1
|
||||
{
|
||||
CC1( CC1 const& ) noexcept {}
|
||||
};
|
||||
|
||||
struct CC2
|
||||
{
|
||||
CC2( CC2 const& ) = delete;
|
||||
};
|
||||
|
||||
struct MC1
|
||||
{
|
||||
MC1( MC1 && ) noexcept {}
|
||||
};
|
||||
|
||||
struct MC2
|
||||
{
|
||||
MC2( MC2 && ) = delete;
|
||||
};
|
||||
|
||||
struct CA1
|
||||
{
|
||||
CA1& operator=( CA1 const& ) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct CA2
|
||||
{
|
||||
CA2& operator=( CA2 const& ) = delete;
|
||||
};
|
||||
|
||||
struct MA1
|
||||
{
|
||||
MA1& operator=( MA1 && ) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct MA2
|
||||
{
|
||||
MA2& operator=( MA2 && ) = delete;
|
||||
};
|
||||
|
||||
using namespace boost::variant2;
|
||||
namespace v2d = boost::variant2::detail;
|
||||
|
||||
struct test
|
||||
{
|
||||
template<class... T> void operator()( mp_list<T...> ) const noexcept
|
||||
{
|
||||
using U = mp_inherit<T...>;
|
||||
|
||||
#if !BOOST_WORKAROUND( __GNUC__, < 5 )
|
||||
|
||||
BOOST_TEST_EQ( v2d::is_trivially_copy_constructible<variant<U>>::value, v2d::is_trivially_copy_constructible<U>::value );
|
||||
BOOST_TEST_EQ( v2d::is_trivially_copy_assignable<variant<U>>::value, std::is_trivially_destructible<U>::value && v2d::is_trivially_copy_constructible<U>::value && v2d::is_trivially_copy_assignable<U>::value );
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_TEST_EQ( std::is_trivially_destructible<variant<U>>::value, std::is_trivially_destructible<U>::value );
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
|
||||
|
||||
BOOST_TEST_EQ( v2d::is_trivially_move_constructible<variant<U>>::value, v2d::is_trivially_move_constructible<U>::value );
|
||||
BOOST_TEST_EQ( v2d::is_trivially_move_assignable<variant<U>>::value, std::is_trivially_destructible<U>::value && v2d::is_trivially_move_constructible<U>::value && v2d::is_trivially_move_assignable<U>::value );
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
mp_for_each< mp_power_set< mp_list<D, CC1, CC2, MC1, MC2, CA1, CA2, MA1, MA2> > >( test() );
|
||||
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 <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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,10 @@ struct F
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
BOOST_TEST_EQ( (visit( []{ return 5; } )), 5 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int> v( 1 );
|
||||
|
||||
|
126
test/variant_visit_by_index.cpp
Normal file
126
test/variant_visit_by_index.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2017, 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
using boost::mp11::mp_int;
|
||||
|
||||
struct X
|
||||
{
|
||||
};
|
||||
|
||||
struct F1
|
||||
{
|
||||
int operator()( X& ) const { return 1; }
|
||||
int operator()( X const& ) const { return 2; }
|
||||
int operator()( X&& ) const { return 3; }
|
||||
int operator()( X const&& ) const { return 4; }
|
||||
};
|
||||
|
||||
struct F2
|
||||
{
|
||||
mp_int<1> operator()( X& ) const { return {}; }
|
||||
mp_int<2> operator()( X const& ) const { return {}; }
|
||||
mp_int<3> operator()( X&& ) const { return {}; }
|
||||
mp_int<4> operator()( X const&& ) const { return {}; }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
variant<int, int, float> v;
|
||||
|
||||
visit_by_index( v,
|
||||
[]( int& x ){ BOOST_TEST_EQ( x, 0 ); },
|
||||
[]( int& ){ BOOST_ERROR( "incorrect alternative" ); },
|
||||
[]( float& ){ BOOST_ERROR( "incorrect alternative" ); } );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int const, int, float const> v( in_place_index_t<0>(), 1 );
|
||||
|
||||
visit_by_index( v,
|
||||
[]( int const& x ){ BOOST_TEST_EQ( x, 1 ); },
|
||||
[]( int& ){ BOOST_ERROR( "incorrect alternative" ); },
|
||||
[]( float const& ){ BOOST_ERROR( "incorrect alternative" ); } );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, int, float> const v( in_place_index_t<1>(), 2 );
|
||||
|
||||
visit_by_index( v,
|
||||
[]( int const& ){ BOOST_ERROR( "incorrect alternative" ); },
|
||||
[]( int const& x ){ BOOST_TEST_EQ( x, 2 ); },
|
||||
[]( float const& ){ BOOST_ERROR( "incorrect alternative" ); } );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int const, int, float const> const v( 3.14f );
|
||||
|
||||
visit_by_index( v,
|
||||
[]( int const& ){ BOOST_ERROR( "incorrect alternative" ); },
|
||||
[]( int const& ){ BOOST_ERROR( "incorrect alternative" ); },
|
||||
[]( float const& x ){ BOOST_TEST_EQ( x, 3.14f ); } );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float> const v( 7 );
|
||||
|
||||
auto r = visit_by_index( v,
|
||||
[]( int const& x ) -> double { return x; },
|
||||
[]( float const& x ) -> double { return x; } );
|
||||
|
||||
BOOST_TEST_TRAIT_SAME( decltype(r), double );
|
||||
BOOST_TEST_EQ( r, 7.0 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float> const v( 2.0f );
|
||||
|
||||
auto r = visit_by_index( v,
|
||||
[]( int const& x ) { return x + 0.0; },
|
||||
[]( float const& x ) { return x + 0.0; } );
|
||||
|
||||
BOOST_TEST_TRAIT_SAME( decltype(r), double );
|
||||
BOOST_TEST_EQ( r, 2.0 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float, double> const v( 3.0 );
|
||||
|
||||
auto r = visit_by_index<double>( v,
|
||||
[]( int const& x ) { return x; },
|
||||
[]( float const& x ) { return x; },
|
||||
[]( double const& x ) { return x; } );
|
||||
|
||||
BOOST_TEST_TRAIT_SAME( decltype(r), double );
|
||||
BOOST_TEST_EQ( r, 3.0 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<X> v;
|
||||
variant<X> const cv;
|
||||
|
||||
F1 f1;
|
||||
|
||||
BOOST_TEST_EQ( visit_by_index( v, f1 ), 1 );
|
||||
BOOST_TEST_EQ( visit_by_index( cv, f1 ), 2 );
|
||||
BOOST_TEST_EQ( visit_by_index( std::move( v ), f1 ), 3 );
|
||||
BOOST_TEST_EQ( visit_by_index( std::move( cv ), f1 ), 4 );
|
||||
|
||||
F2 f2;
|
||||
|
||||
BOOST_TEST_EQ( visit_by_index<int>( v, f2 ), 1 );
|
||||
BOOST_TEST_EQ( visit_by_index<int>( cv, f2 ), 2 );
|
||||
BOOST_TEST_EQ( visit_by_index<int>( std::move( v ), f2 ), 3 );
|
||||
BOOST_TEST_EQ( visit_by_index<int>( std::move( cv ), f2 ), 4 );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
56
test/variant_visit_derived.cpp
Normal file
56
test/variant_visit_derived.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2017, 2020 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4244 ) // conversion from float to int, possible loss of data
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <utility>
|
||||
|
||||
struct X: boost::variant2::variant<int, float>
|
||||
{
|
||||
#if BOOST_WORKAROUND( BOOST_MSVC, < 1940 )
|
||||
|
||||
template<class T> explicit X( T&& t ): variant( std::forward<T>( t ) ) {};
|
||||
|
||||
#else
|
||||
|
||||
using variant::variant;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class... T> struct Y: boost::variant2::variant<T...>
|
||||
{
|
||||
using boost::variant2::variant<T...>::variant;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
X v1( 1 );
|
||||
X const v2( 3.14f );
|
||||
|
||||
BOOST_TEST_EQ( (visit( []( int x1, float x2 ){ return (int)(x1 * 1000) + (int)(x2 * 100); }, v1, v2 )), 1314 );
|
||||
|
||||
visit( []( int x1, float x2 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); }, v1, v2 );
|
||||
visit( []( int x1, float x2 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); }, std::move(v1), std::move(v2) );
|
||||
}
|
||||
|
||||
{
|
||||
Y<int, float> v1( 1 );
|
||||
Y<int, float> const v2( 3.14f );
|
||||
|
||||
BOOST_TEST_EQ( (visit( []( int x1, float x2 ){ return (int)(x1 * 1000) + (int)(x2 * 100); }, v1, v2 )), 1314 );
|
||||
|
||||
visit( []( int x1, float x2 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); }, v1, v2 );
|
||||
visit( []( int x1, float x2 ){ BOOST_TEST_EQ( x1, 1 ); BOOST_TEST_EQ( x2, 3.14f ); }, std::move(v1), std::move(v2) );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
98
test/variant_visit_r.cpp
Normal file
98
test/variant_visit_r.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
// 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
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning( disable: 4244 ) // conversion from float to int, possible loss of data
|
||||
#endif
|
||||
|
||||
#include <boost/variant2/variant.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
using namespace boost::variant2;
|
||||
|
||||
struct F1
|
||||
{
|
||||
template<class T1> T1 operator()( T1 t1 ) const
|
||||
{
|
||||
return t1;
|
||||
}
|
||||
};
|
||||
|
||||
struct F2
|
||||
{
|
||||
template<class T1, class T2> auto operator()( T1 t1, T2 t2 ) const -> decltype( t1 + t2 )
|
||||
{
|
||||
return t1 + t2;
|
||||
}
|
||||
};
|
||||
|
||||
struct F3
|
||||
{
|
||||
template<class T1, class T2, class T3> auto operator()( T1 t1, T2 t2, T3 t3 ) const -> decltype( t1 + t2 + t3 )
|
||||
{
|
||||
return t1 + t2 + t3;
|
||||
}
|
||||
};
|
||||
|
||||
struct F4
|
||||
{
|
||||
template<class T1, class T2, class T3, class T4> auto operator()( T1 t1, T2 t2, T3 t3, T4 t4 ) const -> decltype( t1 + t2 + t3 + t4 )
|
||||
{
|
||||
return t1 + t2 + t3 + t4;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
BOOST_TEST_EQ( (visit<int>( []{ return 3.14f; } )), 3 );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float> v( 1 );
|
||||
|
||||
BOOST_TEST_EQ( visit<int>( F1(), v ), 1 );
|
||||
BOOST_TEST_EQ( visit<float>( F1(), v ), 1.0f );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float> const v( 3.14f );
|
||||
|
||||
BOOST_TEST_EQ( visit<int>( F1(), v ), 3 );
|
||||
BOOST_TEST_EQ( visit<float>( F1(), v ), 3.14f );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float> v1( 1 );
|
||||
variant<int, float> const v2( 3.14f );
|
||||
|
||||
BOOST_TEST_EQ( visit<int>( F2(), v1, v2 ), 4 );
|
||||
BOOST_TEST_EQ( visit<float>( F2(), v1, v2 ), 1 + 3.14f );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float, double> v1( 1 );
|
||||
variant<int, float, double> const v2( 3.14f );
|
||||
variant<int, float, double> v3( 6.28 );
|
||||
|
||||
BOOST_TEST_EQ( visit<int>( F3(), v1, v2, v3 ), 10 );
|
||||
BOOST_TEST_EQ( visit<float>( F3(), v1, v2, v3 ), static_cast<float>( 1 + 3.14f + 6.28 ) );
|
||||
}
|
||||
|
||||
{
|
||||
variant<int, float, double, char> v1( 1 );
|
||||
variant<int, float, double, char> const v2( 3.14f );
|
||||
variant<int, float, double, char> v3( 6.28 );
|
||||
variant<int, float, double, char> const v4( 'A' );
|
||||
|
||||
BOOST_TEST_EQ( visit<int>( F4(), v1, v2, v3, v4 ), 10 + 'A' );
|
||||
BOOST_TEST_EQ( visit<float>( F4(), v1, v2, v3, v4 ), static_cast<float>( 1 + 3.14f + 6.28 + 'A' ) );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
Reference in New Issue
Block a user