Squashed 'third_party/variant/' content from commit b585021

git-subtree-dir: third_party/variant
git-subtree-split: b5850212f16efeb409a112edb1e719d5f5edb604
This commit is contained in:
Patrick Niklaus 2016-03-24 21:32:27 +01:00
commit 62e8601919
51 changed files with 15816 additions and 0 deletions

89
.clang-format Normal file
View File

@ -0,0 +1,89 @@
---
# Mapbox.Variant C/C+ style
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
out
profiling
build
deps
*.gcda
*.gcno
.ycm_extra_conf.pyc

127
.travis.yml Normal file
View File

@ -0,0 +1,127 @@
language: c
sudo: false
# Save common build configurations as shortcuts, so we can reference them later.
addons_shortcuts:
addons_clang35: &clang35
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5', 'boost-latest' ]
packages: [ 'clang-3.5', 'libboost1.55-all-dev' ]
addons_clang36: &clang36
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6', 'boost-latest' ]
packages: [ 'clang-3.6', 'libboost1.55-all-dev' ]
addons_clang37: &clang37
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7', 'boost-latest' ]
packages: [ 'clang-3.7', 'libboost1.55-all-dev' ]
addons_clang38: &clang38
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise', 'boost-latest' ]
packages: [ 'clang-3.8', 'libboost1.55-all-dev']
addons_gcc47: &gcc47
apt:
sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ]
packages: [ 'g++-4.7', 'libboost1.55-all-dev' ]
addons_gcc48: &gcc48
apt:
sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ]
packages: [ 'g++-4.8', 'libboost1.55-all-dev' ]
addons_gcc49: &gcc49
apt:
sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ]
packages: [ 'g++-4.9', 'libboost1.55-all-dev' ]
addons_gcc5: &gcc5
apt:
sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ]
packages: [ 'g++-5', 'libboost1.55-all-dev' ]
matrix:
include:
- os: osx
osx_image: xcode6
compiler: clang
- os: osx
osx_image: xcode7
env: TEST_GYP_BUILD=True
compiler: clang
- os: linux
compiler: "clang35"
env: CXX=clang++-3.5
addons: *clang35
- os: linux
compiler: "clang36"
env: CXX=clang++-3.6
addons: *clang36
- os: linux
compiler: "clang37"
env: CXX=clang++-3.7 COVERAGE=True
addons: *clang37
- os: linux
compiler: "clang38"
env: CXX=clang++-3.8
addons: *clang38
- os: linux
compiler: "clang38"
env: CXX=clang++-3.8 CXX_STD=c++14
addons: *clang38
- os: linux
compiler: "gcc47"
env: CXX=g++-4.7
addons: *gcc47
- os: linux
compiler: "gcc48"
env: CXX=g++-4.8
addons: *gcc48
- os: linux
compiler: "gcc49"
env: CXX=g++-4.9
addons: *gcc49
- os: linux
compiler: "gcc49"
env: CXX=g++-4.9 CXX_STD=c++14
addons: *gcc49
- os: linux
compiler: "gcc5"
env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0"
addons: *gcc5
- os: linux
compiler: "gcc5"
env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1"
addons: *gcc5
before_install:
- echo ${CXX}
- if [[ $(uname -s) == 'Linux' ]]; then
export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages;
else
brew install boost;
export PYTHONPATH=$(pwd)/.local/lib/python/site-packages;
fi
- if [[ ${COVERAGE:-0} == 'True' ]]; then
PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls;
fi
install:
- make test
- make bench
- if [[ $(uname -s) == 'Linux' ]]; then
make sizes /usr/include/boost/variant.hpp;
else
make sizes `brew --prefix`/include/boost/variant.hpp;
fi
- scripts/run_compilation_failure_tests.sh
- if [[ ${TEST_GYP_BUILD:-0} == 'True' ]]; then
make clean;
make gyp;
fi
script:
- if [[ ${COVERAGE:-0} == 'True' ]]; then
make clean;
make coverage;
./out/cov-test;
cp unit*gc* test/;
./.local/bin/cpp-coveralls -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp --gcov-options '\-lp';
fi

40
.ycm_extra_conf.py Normal file
View File

@ -0,0 +1,40 @@
#-----------------------------------------------------------------------------
#
# Configuration for YouCompleteMe Vim plugin
#
# http://valloric.github.io/YouCompleteMe/
#
#-----------------------------------------------------------------------------
from os.path import realpath, dirname
basedir = dirname(realpath(__file__))
# some default flags
# for more information install clang-3.2-doc package and
# check UsersManual.html
flags = [
'-Werror',
'-Wall',
'-Wextra',
'-pedantic',
'-std=c++11',
# '-x' and 'c++' also required
# use 'c' for C projects
'-x',
'c++',
# include third party libraries
'-I.',
]
# youcompleteme is calling this function to get flags
# You can also set database for flags. Check: JSONCompilationDatabase.html in
# clang-3.2-doc package
def FlagsForFile( filename ):
return {
'flags': flags,
'do_cache': True
}

73
Jamroot Normal file
View File

@ -0,0 +1,73 @@
# Inofficial and incomplete build file using Boost build system.
# You should use make unless you know what you are doing.
local BOOST_DIR = "/usr/local" ;
#using clang : : ;
lib system : : <name>boost_system <search>$(BOOST_DIR)/lib ;
lib timer : chrono : <name>boost_timer <search>$(BOOST_DIR)/lib ;
lib chrono : system : <name>boost_chrono <search>$(BOOST_DIR)/lib ;
exe variant-test
:
test/bench_variant.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./
#<define>SINGLE_THREADED
<variant>release:<cxxflags>-march=native
;
exe binary-visitor-test
:
test/binary_visitor_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./
<variant>release:<cxxflags>-march=native
;
exe recursive-wrapper-test
:
test/recursive_wrapper_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./
<variant>release:<cxxflags>-march=native
;
exe unique-ptr-test
:
test/unique_ptr_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./
<variant>release:<cxxflags>-march=native
;
exe reference_wrapper_test
:
test/reference_wrapper_test.cpp
.//system
.//timer
.//chrono
:
<include>$(BOOST_DIR)/include
<include>./
<variant>release:<cxxflags>-march=native
;

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) MapBox
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name "MapBox" nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

23
LICENSE_1_0.txt Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

110
Makefile Normal file
View File

@ -0,0 +1,110 @@
CXX := $(CXX)
CXX_STD ?= c++11
BOOST_LIBS = -lboost_timer -lboost_system -lboost_chrono
RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden
DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions
COMMON_FLAGS = -Wall -pedantic -Wextra -Wsign-compare -Wsign-conversion -Wshadow -Wunused-parameter -std=$(CXX_STD)
CXXFLAGS := $(CXXFLAGS)
LDFLAGS := $(LDFLAGS)
OS:=$(shell uname -s)
ifeq ($(OS),Darwin)
CXXFLAGS += -stdlib=libc++
LDFLAGS += -stdlib=libc++ -F/ -framework CoreFoundation
else
BOOST_LIBS += -lrt
endif
ifeq (sizes,$(firstword $(MAKECMDGOALS)))
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(RUN_ARGS):;@:)
ifndef RUN_ARGS
$(error sizes target requires you pass full path to boost variant.hpp)
endif
.PHONY: $(RUN_ARGS)
endif
all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test
./deps/gyp:
git clone --depth 1 https://chromium.googlesource.com/external/gyp.git ./deps/gyp
gyp: ./deps/gyp
deps/gyp/gyp --depth=. -Goutput_dir=./ --generator-output=./out -f make
make V=1 -C ./out tests
./out/Release/tests
out/bench-variant-debug: Makefile test/bench_variant.cpp variant.hpp recursive_wrapper.hpp
mkdir -p ./out
$(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./ -pthreads $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS)
out/bench-variant: Makefile test/bench_variant.cpp variant.hpp recursive_wrapper.hpp
mkdir -p ./out
$(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS)
out/unique_ptr_test: Makefile test/unique_ptr_test.cpp variant.hpp recursive_wrapper.hpp
mkdir -p ./out
$(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS)
out/recursive_wrapper_test: Makefile test/recursive_wrapper_test.cpp variant.hpp recursive_wrapper.hpp
mkdir -p ./out
$(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS)
out/binary_visitor_test: Makefile test/binary_visitor_test.cpp variant.hpp variant_io.hpp recursive_wrapper.hpp
mkdir -p ./out
$(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS)
bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test
./out/bench-variant 100000
./out/unique_ptr_test 100000
./out/recursive_wrapper_test 100000
./out/binary_visitor_test 100000
out/unit.o: Makefile test/unit.cpp
mkdir -p ./out
$(CXX) -c -o $@ test/unit.cpp -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS)
out/%.o: test/t/%.cpp Makefile optional.hpp recursive_wrapper.hpp variant.hpp variant_io.hpp
mkdir -p ./out
$(CXX) -c -o $@ $< -I. -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS)
out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o
mkdir -p ./out
$(CXX) -o $@ $^ $(LDFLAGS)
test: out/unit
./out/unit
coverage:
mkdir -p ./out
$(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./ -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS)
sizes: Makefile variant.hpp recursive_wrapper.hpp
mkdir -p ./out
@$(CXX) -o ./out/our_variant_hello_world.out variant.hpp $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world.out
@$(CXX) -o ./out/boost_variant_hello_world.out $(RUN_ARGS) $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/boost_variant_hello_world.out
@$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world
@$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/boost_variant_hello_world
profile: out/bench-variant-debug
mkdir -p profiling/
rm -rf profiling/*
iprofiler -timeprofiler -d profiling/ ./out/bench-variant-debug 500000
clean:
rm -rf ./out
rm -rf *.dSYM
rm -f unit.gc*
rm -f *gcov
rm -f test/unit.gc*
rm -f test/*gcov
rm -f *.gcda *.gcno
pgo: out Makefile variant.hpp recursive_wrapper.hpp
$(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -pg -fprofile-generate
./test-variant 500000 >/dev/null 2>/dev/null
$(CXX) -o out/bench-variant test/bench_variant.cpp -I./ $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_LIBS) -fprofile-use
.PHONY: sizes test

111
README.md Normal file
View File

@ -0,0 +1,111 @@
# Mapbox Variant
An alternative to `boost::variant` for C++11.
[![Build Status](https://secure.travis-ci.org/mapbox/variant.svg)](https://travis-ci.org/mapbox/variant)
[![Build status](https://ci.appveyor.com/api/projects/status/v9tatx21j1k0fcgy)](https://ci.appveyor.com/project/Mapbox/variant)
[![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master&service=github)](https://coveralls.io/r/mapbox/variant?branch=master)
## Why use Mapbox Variant?
Mapbox variant has the same speedy performance of `boost::variant` but is
faster to compile, results in smaller binaries, and has no dependencies.
For example on OS X 10.9 with clang++ and libc++:
Test | Mapbox Variant | Boost Variant
---- | -------------- | -------------
Size of pre-compiled header (release / debug) | 2.8/2.8 MB | 12/15 MB
Size of simple program linking variant (release / debug) | 8/24 K | 12/40 K
Time to compile header | 185 ms | 675 ms
(Numbers from an older version of Mapbox variant.)
## Goals
Mapbox `variant` has been a very valuable, lightweight alternative for apps
that can use c++11 or c++14 but that do not want a boost dependency.
Mapbox `variant` has also been useful in apps that do depend on boost, like
mapnik, to help (slightly) with compile times and to majorly lessen dependence
on boost in core headers. The original goal and near term goal is to maintain
external API compatibility with `boost::variant` such that Mapbox `variant`
can be a "drop in". At the same time the goal is to stay minimal: Only
implement the features that are actually needed in existing software. So being
an "incomplete" implementation is just fine.
Currently Mapbox variant doesn't try to be API compatible with the upcoming
variant standard, because the standard is not finished and it would be too much
work. But we'll revisit this decision in the future if needed.
If Mapbox variant is not for you, have a look at [these other
implementations](doc/other_implementations.md).
Want to know more about the upcoming standard? Have a look at our
[overview](doc/standards_effort.md).
## Depends
- Compiler supporting `-std=c++11` or `-std=c++14`
Tested with:
- g++-4.7
- g++-4.8
- g++-4.9
- g++-5
- clang++-3.5
- clang++-3.6
- clang++-3.7
- clang++-3.8
- Visual Studio 2015
## Usage
There is nothing to build, just include `variant.hpp` and
`recursive_wrapper.hpp` in your project. Include `variant_io.hpp` if you need
the `operator<<` overload for variant.
## Unit Tests
On Unix systems compile and run the unit tests with `make test`.
On Windows run `scripts/build-local.bat`.
## Limitations
* The `variant` can not hold references (something like `variant<int&>` is
not possible). You might want to try `std::reference_wrapper` instead.
## Deprecations
* The included implementation of `optional` is deprecated and will be removed
in a future version. See https://github.com/mapbox/variant/issues/64.
* Old versions of the code needed visitors to derive from `static_visitor`.
This is not needed any more and marked as deprecated. The `static_visitor`
class will be removed in future versions.
## Benchmarks
The benchmarks depend on:
- Boost headers (for benchmarking against `boost::variant`)
- Boost built with `--with-timer` (used for benchmark timing)
On Unix systems set your boost includes and libs locations and run `make test`:
export LDFLAGS='-L/opt/boost/lib'
export CXXFLAGS='-I/opt/boost/include'
make bench
## Check object sizes
make sizes /path/to/boost/variant.hpp

17
appveyor.yml Normal file
View File

@ -0,0 +1,17 @@
platform:
- x64
- x86
configuration:
- Debug
- Release
os: Visual Studio 2015
install:
- CALL scripts\build-appveyor.bat
build: off
test: off
deploy: off

143
common.gypi Normal file
View File

@ -0,0 +1,143 @@
{
"conditions": [
["OS=='win'", {
"target_defaults": {
"default_configuration": "Release_x64",
"msbuild_toolset":"v140",
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1, # /EHsc
"RuntimeTypeInfo": "true" # /GR
}
},
"configurations": {
"Debug_Win32": {
"msvs_configuration_platform": "Win32",
"defines": [ "DEBUG","_DEBUG"],
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeLibrary": "1", # static debug /MTd
"Optimization": 0, # /Od, no optimization
"MinimalRebuild": "false",
"OmitFramePointers": "false",
"BasicRuntimeChecks": 3 # /RTC1
}
}
},
"Debug_x64": {
"msvs_configuration_platform": "x64",
"defines": [ "DEBUG","_DEBUG"],
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeLibrary": "1", # static debug /MTd
"Optimization": 0, # /Od, no optimization
"MinimalRebuild": "false",
"OmitFramePointers": "false",
"BasicRuntimeChecks": 3 # /RTC1
}
}
},
"Release_Win32": {
"msvs_configuration_platform": "Win32",
"defines": [ "NDEBUG"],
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeLibrary": 0, # static release
"Optimization": 3, # /Ox, full optimization
"FavorSizeOrSpeed": 1, # /Ot, favour speed over size
"InlineFunctionExpansion": 2, # /Ob2, inline anything eligible
"WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG
"OmitFramePointers": "true",
"EnableFunctionLevelLinking": "true",
"EnableIntrinsicFunctions": "true",
"AdditionalOptions": [
"/MP", # compile across multiple CPUs
],
"DebugInformationFormat": "0"
},
"VCLibrarianTool": {
"AdditionalOptions": [
"/LTCG" # link time code generation
],
},
"VCLinkerTool": {
"LinkTimeCodeGeneration": 1, # link-time code generation
"OptimizeReferences": 2, # /OPT:REF
"EnableCOMDATFolding": 2, # /OPT:ICF
"LinkIncremental": 1, # disable incremental linking
"GenerateDebugInformation": "false"
}
}
},
"Release_x64": {
"msvs_configuration_platform": "x64",
"defines": [ "NDEBUG"],
"msvs_settings": {
"VCCLCompilerTool": {
"RuntimeLibrary": 0, # static release
"Optimization": 3, # /Ox, full optimization
"FavorSizeOrSpeed": 1, # /Ot, favour speed over size
"InlineFunctionExpansion": 2, # /Ob2, inline anything eligible
"WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG
"OmitFramePointers": "true",
"EnableFunctionLevelLinking": "true",
"EnableIntrinsicFunctions": "true",
"AdditionalOptions": [
"/MP", # compile across multiple CPUs
],
"DebugInformationFormat": "0"
},
"VCLibrarianTool": {
"AdditionalOptions": [
"/LTCG" # link time code generation
],
},
"VCLinkerTool": {
"LinkTimeCodeGeneration": 1, # link-time code generation
"OptimizeReferences": 2, # /OPT:REF
"EnableCOMDATFolding": 2, # /OPT:ICF
"LinkIncremental": 1, # disable incremental linking
"GenerateDebugInformation": "false"
}
}
}
}
}
}, {
"target_defaults": {
"default_configuration": "Release",
"xcode_settings": {
"CLANG_CXX_LIBRARY": "libc++",
"CLANG_CXX_LANGUAGE_STANDARD":"c++11",
"GCC_VERSION": "com.apple.compilers.llvm.clang.1_0",
},
"cflags_cc": ["-std=c++11"],
"configurations": {
"Debug": {
"defines": [
"DEBUG"
],
"xcode_settings": {
"GCC_OPTIMIZATION_LEVEL": "0",
"GCC_GENERATE_DEBUGGING_SYMBOLS": "YES",
"OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-g", "-O0" ]
}
},
"Release": {
"defines": [
"NDEBUG"
],
"xcode_settings": {
"GCC_OPTIMIZATION_LEVEL": "3",
"GCC_GENERATE_DEBUGGING_SYMBOLS": "NO",
"DEAD_CODE_STRIPPING": "YES",
"GCC_INLINES_ARE_PRIVATE_EXTERN": "YES",
"OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-O3" ]
}
}
}
}
}]
]
}

View File

@ -0,0 +1,15 @@
# Other implementations of variant and optional
These are some other implementations of `variant` and/or `optional` types.
They are not necessarily compatible with this implementation. This is an
incomplete list.
* [Boost Variant](http://www.boost.org/doc/libs/1_59_0/doc/html/variant.html) and [Boost Optional](http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/index.html)
* [Eggs Variant](http://eggs-cpp.github.io/variant/) by [Agustín Bergé](http://talesofcpp.fusionfenix.com/)
* [anthonyw/variant](https://bitbucket.org/anthonyw/variant) (implementation of P0110R0)
* [JasonL9000/cppcon14](https://github.com/JasonL9000/cppcon14)
* [tomilov/variant](https://github.com/tomilov/variant)
* [akrzemi1/Optional](https://github.com/akrzemi1/Optional)
* [mpark/variant](https://github.com/mpark/variant)

28
doc/standards_effort.md Normal file
View File

@ -0,0 +1,28 @@
# Standards efforts
A `variant` type is on planned for inclusion in the C++ Standard, probably in
C++17. Current working papers are (list extracted from [2015 working group
papers](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/)):
* 2015-09-28: Variant design review. [P0086R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0086r0.pdf)
* 2015-09-28: Variant: a type-safe union without undefined behavior (v2) [P0087R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0087r0.pdf)
* 2015-09-27: Variant: a type-safe union that is rarely invalid (v5) [P0088R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0088r0.pdf)
* 2015-09-24: Simply a Strong Variant [P0093R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0093r0.html)
* 2015-09-24: Simply a Basic Variant [P0094R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0094r0.html)
* 2015-09-24: The Case for a Language Based Variant [P0096R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0095r0.html)
* 2015-09-25: Implementing the strong guarantee for variant<> assignment [P0110R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0110r0.html)
* 2015-09-24: Homogeneous interface for variant, any and optional (Revision 1) [P0032R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0032r1.pdf)
Last state can be seen from
[The Variant Saga: A happy ending?](https://isocpp.org/blog/2015/11/the-variant-saga-a-happy-ending).
The `optional` type is also on the way into the standard. The papers are:
* 2013-10-03: A proposal to add a utility class to represent optional objects (Revision 5) [N3793](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html)
* 2014-01-18: Working Draft, Technical Specification on C++ Extensions for Library Fundamentals [N3848](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html)
## Older Papers
* Older working drafts are: N4218 (rev 1), N4516 (rev 2), N4450 (rev 3), and [N4542](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4542.pdf) (rev 4). They have been split into P0086 (general design discussions) and P0087 and P0088 (containing two competing? specs).
* 2015-07-28: Variant: Discriminated Union with Value Semantics [P0080R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0080r0.pdf) An alternative proposal to N4542.

74
optional.hpp Normal file
View File

@ -0,0 +1,74 @@
#ifndef MAPBOX_UTIL_OPTIONAL_HPP
#define MAPBOX_UTIL_OPTIONAL_HPP
#pragma message("This implementation of optional is deprecated. See https://github.com/mapbox/variant/issues/64.")
#include <type_traits>
#include <utility>
#include "variant.hpp"
namespace mapbox {
namespace util {
template <typename T>
class optional
{
static_assert(!std::is_reference<T>::value, "optional doesn't support references");
struct none_type
{
};
variant<none_type, T> variant_;
public:
optional() = default;
optional(optional const& rhs)
{
if (this != &rhs)
{ // protect against invalid self-assignment
variant_ = rhs.variant_;
}
}
optional(T const& v) { variant_ = v; }
explicit operator bool() const noexcept { return variant_.template is<T>(); }
T const& get() const { return variant_.template get<T>(); }
T& get() { return variant_.template get<T>(); }
T const& operator*() const { return this->get(); }
T operator*() { return this->get(); }
optional& operator=(T const& v)
{
variant_ = v;
return *this;
}
optional& operator=(optional const& rhs)
{
if (this != &rhs)
{
variant_ = rhs.variant_;
}
return *this;
}
template <typename... Args>
void emplace(Args&&... args)
{
variant_ = T{std::forward<Args>(args)...};
}
void reset() { variant_ = none_type{}; }
}; // class optional
} // namespace util
} // namespace mapbox
#endif // MAPBOX_UTIL_OPTIONAL_HPP

122
recursive_wrapper.hpp Normal file
View File

@ -0,0 +1,122 @@
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
// Based on variant/recursive_wrapper.hpp from boost.
//
// Original license:
//
// Copyright (c) 2002-2003
// Eric Friedman, Itay Maman
//
// 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 <cassert>
#include <utility>
namespace mapbox {
namespace util {
template <typename T>
class recursive_wrapper
{
T* p_;
void assign(T const& rhs)
{
this->get() = rhs;
}
public:
using type = T;
/**
* Default constructor default initializes the internally stored value.
* For POD types this means nothing is done and the storage is
* uninitialized.
*
* @throws std::bad_alloc if there is insufficient memory for an object
* of type T.
* @throws any exception thrown by the default constructur of T.
*/
recursive_wrapper()
: p_(new T){};
~recursive_wrapper() noexcept { delete p_; };
recursive_wrapper(recursive_wrapper const& operand)
: p_(new T(operand.get())) {}
recursive_wrapper(T const& operand)
: p_(new T(operand)) {}
recursive_wrapper(recursive_wrapper&& operand)
: p_(new T(std::move(operand.get()))) {}
recursive_wrapper(T&& operand)
: p_(new T(std::move(operand))) {}
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
{
assign(rhs.get());
return *this;
}
inline recursive_wrapper& operator=(T const& rhs)
{
assign(rhs);
return *this;
}
inline void swap(recursive_wrapper& operand) noexcept
{
T* temp = operand.p_;
operand.p_ = p_;
p_ = temp;
}
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
{
swap(rhs);
return *this;
}
recursive_wrapper& operator=(T&& rhs)
{
get() = std::move(rhs);
return *this;
}
T& get()
{
assert(p_);
return *get_pointer();
}
T const& get() const
{
assert(p_);
return *get_pointer();
}
T* get_pointer() { return p_; }
const T* get_pointer() const { return p_; }
operator T const&() const { return this->get(); }
operator T&() { return this->get(); }
}; // class recursive_wrapper
template <typename T>
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
{
lhs.swap(rhs);
}
} // namespace util
} // namespace mapbox
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP

View File

@ -0,0 +1,32 @@
@ECHO OFF
SETLOCAL
SET PATH=c:\python27;%PATH%
ECHO activating VS command prompt
IF /I "%PLATFORM"=="x64" (
CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
) ELSE (
CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
)
IF NOT EXIST deps\gyp git clone --quiet --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp
CALL deps\gyp\gyp.bat variant.gyp --depth=. ^
-f msvs ^
-G msvs_version=2015 ^
--generator-output=build
SET MSBUILD_PLATFORM=%platform%
IF /I "%MSBUILD_PLATFORM%" == "x86" SET MSBUILD_PLATFORM=Win32
msbuild ^
build\variant.sln ^
/nologo ^
/toolsversion:14.0 ^
/p:PlatformToolset=v140 ^
/p:Configuration=%configuration% ^
/p:Platform=%MSBUILD_PLATFORM%
build\"%configuration%"\tests.exe

7
scripts/build-local.bat Normal file
View File

@ -0,0 +1,7 @@
@ECHO OFF
SETLOCAL
SET platform=x64
SET configuration=Release
CALL scripts\build-appveyor.bat

View File

@ -0,0 +1,50 @@
#!/bin/sh
#
# Try to compile all programs in the test/compilation_failure directory.
# Compilation must fail and the error message must match the pattern in the
# corresponding .pattern file.
#
DIR="test/compilation_failure"
CXX=${CXX:-clang++}
if [ `uname -s` = "Darwin" ]; then
CXXFLAGS="$CXXFLAGS -stdlib=libc++"
fi
error_msg() {
if [ ! -z "$1" ]; then
printf 'output was:\n=======\n%s\n=======\n' "$1"
fi
}
exit_code=0
for test_code in $DIR/*.cpp; do
name=`basename $test_code .cpp`
result=`${CXX} -std=c++11 -c -o /dev/null -I. ${CXXFLAGS} ${test_code} 2>&1`
status=$?
if [ $status = 1 ]; then
expected=`sed -n -e '/@EXPECTED/s/.*: \+//p' ${test_code}`
if echo $result | grep -q "$expected"; then
echo "$name [OK]"
else
echo "$name [FAILED - wrong error message]"
echo "Expected error message: $expected"
error_msg "$result"
exit_code=1
fi
elif [ $status = 0 ]; then
echo "$name [FAILED - compile was successful]"
error_msg "$result"
exit_code=1
else
echo "$name [FAILED - unknown error in compile]"
error_msg "$result"
exit_code=1
fi
done
exit ${exit_code}

185
test/bench_variant.cpp Normal file
View File

@ -0,0 +1,185 @@
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <boost/timer/timer.hpp>
#include <boost/variant.hpp>
#include "variant.hpp"
#define TEXT_SHORT "Test"
#define TEXT_LONG "Testing various variant implementations with a longish string ........................................."
#define NUM_SAMPLES 3
//#define BOOST_VARIANT_MINIMIZE_SIZE
using namespace mapbox;
namespace test {
template <typename V>
struct Holder
{
typedef V value_type;
std::vector<value_type> data;
template <typename T>
void append_move(T&& obj)
{
data.emplace_back(std::forward<T>(obj));
}
template <typename T>
void append(T const& obj)
{
data.push_back(obj);
}
};
} // namespace test
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
template <typename V>
struct dummy : boost::static_visitor<>
{
dummy(V& v)
: v_(v) {}
template <typename T>
void operator()(T&& val) const
{
v_ = std::move(val);
}
V& v_;
};
template <typename V>
struct dummy2
{
dummy2(V& v)
: v_(v) {}
template <typename T>
void operator()(T&& val) const
{
v_ = std::move(val);
}
V& v_;
};
void run_boost_test(std::size_t runs)
{
test::Holder<boost::variant<int, double, std::string>> h;
h.data.reserve(runs);
for (std::size_t i = 0; i < runs; ++i)
{
h.append_move(std::string(TEXT_SHORT));
h.append_move(std::string(TEXT_LONG));
h.append_move(123);
h.append_move(3.14159);
}
boost::variant<int, double, std::string> v;
for (auto const& v2 : h.data)
{
dummy<boost::variant<int, double, std::string>> d(v);
boost::apply_visitor(d, v2);
}
}
void run_variant_test(std::size_t runs)
{
test::Holder<util::variant<int, double, std::string>> h;
h.data.reserve(runs);
for (std::size_t i = 0; i < runs; ++i)
{
h.append_move(std::string(TEXT_SHORT));
h.append_move(std::string(TEXT_LONG));
h.append_move(123);
h.append_move(3.14159);
}
util::variant<int, double, std::string> v;
for (auto const& v2 : h.data)
{
dummy2<util::variant<int, double, std::string>> d(v);
util::apply_visitor(d, v2);
}
}
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage:" << argv[0] << " <num-runs>" << std::endl;
return 1;
}
#ifndef SINGLE_THREADED
const std::size_t THREADS = 4;
#endif
const std::size_t NUM_RUNS = static_cast<std::size_t>(std::stol(argv[1]));
#ifdef SINGLE_THREADED
for (std::size_t j = 0; j < NUM_SAMPLES; ++j)
{
{
std::cerr << "custom variant: ";
boost::timer::auto_cpu_timer t;
run_variant_test(NUM_RUNS);
}
{
std::cerr << "boost variant: ";
boost::timer::auto_cpu_timer t;
run_boost_test(NUM_RUNS);
}
}
#else
for (std::size_t j = 0; j < NUM_SAMPLES; ++j)
{
{
typedef std::vector<std::unique_ptr<std::thread>> thread_group;
typedef thread_group::value_type value_type;
thread_group tg;
std::cerr << "custom variant: ";
boost::timer::auto_cpu_timer timer;
for (std::size_t i = 0; i < THREADS; ++i)
{
tg.emplace_back(new std::thread(run_variant_test, NUM_RUNS));
}
std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); });
}
{
typedef std::vector<std::unique_ptr<std::thread>> thread_group;
typedef thread_group::value_type value_type;
thread_group tg;
std::cerr << "boost variant: ";
boost::timer::auto_cpu_timer timer;
for (std::size_t i = 0; i < THREADS; ++i)
{
tg.emplace_back(new std::thread(run_boost_test, NUM_RUNS));
}
std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); });
}
}
#endif
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,138 @@
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "variant.hpp"
#include "variant_io.hpp"
using namespace mapbox;
namespace test {
template <typename T>
struct string_to_number
{
};
template <>
struct string_to_number<double>
{
double operator()(std::string const& str) const
{
return std::stod(str);
}
};
template <>
struct string_to_number<std::int64_t>
{
std::int64_t operator()(std::string const& str) const
{
return std::stoll(str);
}
};
template <>
struct string_to_number<std::uint64_t>
{
std::uint64_t operator()(std::string const& str) const
{
return std::stoull(str);
}
};
template <>
struct string_to_number<bool>
{
bool operator()(std::string const& str) const
{
bool result;
std::istringstream(str) >> std::boolalpha >> result;
return result;
}
};
struct javascript_equal_visitor
{
template <typename T>
bool operator()(T lhs, T rhs) const
{
return lhs == rhs;
}
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
bool operator()(T lhs, std::string const& rhs) const
{
return lhs == string_to_number<T>()(rhs);
}
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
bool operator()(std::string const& lhs, T rhs) const
{
return string_to_number<T>()(lhs) == rhs;
}
template <typename T0, typename T1>
bool operator()(T0 lhs, T1 rhs) const
{
return lhs == static_cast<T0>(rhs);
}
};
template <typename T>
struct javascript_equal
{
javascript_equal(T const& lhs)
: lhs_(lhs) {}
bool operator()(T const& rhs) const
{
return util::apply_visitor(test::javascript_equal_visitor(), lhs_, rhs);
}
T const& lhs_;
};
} // namespace test
int main()
{
typedef util::variant<bool, std::int64_t, std::uint64_t, double, std::string> variant_type;
variant_type v0(3.14159);
variant_type v1(std::string("3.14159"));
variant_type v2(std::uint64_t(1));
std::cerr << v0 << " == " << v1 << " -> "
<< std::boolalpha << util::apply_visitor(test::javascript_equal_visitor(), v0, v1) << std::endl;
std::vector<variant_type> vec;
vec.emplace_back(std::string("1"));
vec.push_back(variant_type(std::uint64_t(2)));
vec.push_back(variant_type(std::uint64_t(3)));
vec.push_back(std::string("3.14159"));
vec.emplace_back(3.14159);
//auto itr = std::find_if(vec.begin(), vec.end(), [&v0](variant_type const& val) {
// return util::apply_visitor(test::javascript_equal_visitor(), v0, val);
// });
auto itr = std::find_if(vec.begin(), vec.end(), test::javascript_equal<variant_type>(v2));
if (itr != std::end(vec))
{
std::cout << "found " << *itr << std::endl;
}
else
{
std::cout << "can't find " << v2 << '\n';
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,20 @@
#include <boost/variant.hpp>
#include <stdexcept>
struct check : boost::static_visitor<>
{
template <typename T>
void operator()(T const& val) const
{
if (val != 0) throw std::runtime_error("invalid");
}
};
int main()
{
typedef boost::variant<bool, int, double> variant_type;
variant_type v(0);
boost::apply_visitor(check(), v);
return 0;
}

View File

@ -0,0 +1,22 @@
// @EXPECTED: First type in variant must be default constructible to allow default construction of variant
#include <variant.hpp>
// Checks that the first type in a variant must be default constructible to
// make the variant default constructible.
struct no_def_constructor
{
int value;
no_def_constructor() = delete;
no_def_constructor(int v) : value(v) {}
};
int main()
{
mapbox::util::variant<no_def_constructor> x;
}

View File

@ -0,0 +1,11 @@
// @EXPECTED: Template parameter type list of variant can not be empty
#include <variant.hpp>
// Empty type list should not work.
int main()
{
mapbox::util::variant<> x;
}

View File

@ -0,0 +1,11 @@
// @EXPECTED:
#include <variant.hpp>
int main()
{
mapbox::util::variant<int> x;
mapbox::util::variant<double> y;
x == y;
}

View File

@ -0,0 +1,10 @@
// @EXPECTED: enable_if
#include <variant.hpp>
int main()
{
mapbox::util::variant<int, double> x;
x.get<std::string>();
}

View File

@ -0,0 +1,10 @@
// @EXPECTED: invalid type in T in `is<T>()` for this variant
#include <variant.hpp>
int main()
{
mapbox::util::variant<int, double> x;
x.is<std::string>();
}

View File

@ -0,0 +1,24 @@
// @EXPECTED: const int
#include <variant.hpp>
struct mutating_visitor
{
mutating_visitor(int val)
: val_(val) {}
void operator()(int& val) const
{
val = val_;
}
int val_;
};
int main()
{
const mapbox::util::variant<int> var(123);
const mutating_visitor visitor(456);
mapbox::util::apply_visitor(visitor, var);
}

View File

@ -0,0 +1,9 @@
// @EXPECTED: Variant can not hold reference types
#include <variant.hpp>
int main()
{
mapbox::util::variant<double, int&, long> x{mapbox::util::no_init()};
}

11613
test/include/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
#include "variant.hpp"
#include <stdexcept>
struct check
{
template <typename T>
void operator()(T const& val) const
{
if (val != 0) throw std::runtime_error("invalid");
}
};
int main()
{
typedef mapbox::util::variant<bool, int, double> variant_type;
variant_type v(0);
mapbox::util::apply_visitor(check(), v);
return 0;
}

View File

@ -0,0 +1,125 @@
#include <cstdlib>
#include <iostream>
#include <string>
#include <typeinfo>
#include <utility>
#include <boost/timer/timer.hpp>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct add;
struct sub;
template <typename OpTag>
struct binary_op;
typedef util::variant<int,
util::recursive_wrapper<binary_op<add>>,
util::recursive_wrapper<binary_op<sub>>>
expression;
template <typename Op>
struct binary_op
{
expression left; // variant instantiated here...
expression right;
binary_op(expression&& lhs, expression&& rhs)
: left(std::move(lhs)), right(std::move(rhs))
{
}
};
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
struct test
{
template <typename T>
std::string operator()(T const& obj) const
{
return std::string("TYPE_ID=") + typeid(obj).name();
}
};
struct calculator
{
public:
int operator()(int value) const
{
return value;
}
int operator()(binary_op<add> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) + util::apply_visitor(calculator(), binary.right);
}
int operator()(binary_op<sub> const& binary) const
{
return util::apply_visitor(calculator(), binary.left) - util::apply_visitor(calculator(), binary.right);
}
};
struct to_string
{
public:
std::string operator()(int value) const
{
return std::to_string(value);
}
std::string operator()(binary_op<add> const& binary) const
{
return util::apply_visitor(to_string(), binary.left) + std::string("+") + util::apply_visitor(to_string(), binary.right);
}
std::string operator()(binary_op<sub> const& binary) const
{
return util::apply_visitor(to_string(), binary.left) + std::string("-") + util::apply_visitor(to_string(), binary.right);
}
};
} // namespace test
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage" << argv[0] << " <num-iter>" << std::endl;
return EXIT_FAILURE;
}
const std::size_t NUM_ITER = static_cast<std::size_t>(std::stol(argv[1]));
test::expression sum(test::binary_op<test::add>(2, 3));
test::expression result(test::binary_op<test::sub>(std::move(sum), 4));
std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl;
int total = 0;
{
boost::timer::auto_cpu_timer t;
for (std::size_t i = 0; i < NUM_ITER; ++i)
{
total += util::apply_visitor(test::calculator(), result);
}
}
std::cerr << "total=" << total << std::endl;
std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,76 @@
#include <cstdlib>
#include <functional>
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct point
{
public:
point(double x_, double y_)
: x(x_), y(y_) {}
double x;
double y;
};
struct line_string : std::vector<point>
{
};
struct polygon : std::vector<line_string>
{
};
using variant = util::variant<std::reference_wrapper<const point>,
std::reference_wrapper<const line_string>,
std::reference_wrapper<const polygon>>;
struct print
{
using result_type = void;
void operator()(point const& pt) const
{
std::cerr << "Point(" << pt.x << "," << pt.y << ")" << std::endl;
}
void operator()(line_string const& line) const
{
std::cerr << "Line(";
for (auto const& pt : line)
{
std::cerr << pt.x << " " << pt.y << ",";
}
std::cerr << ")" << std::endl;
}
template <typename T>
void operator()(T const& val) const
{
std::cerr << typeid(T).name() << std::endl;
}
};
}
int main()
{
std::cerr << sizeof(test::polygon) << std::endl;
std::cerr << sizeof(test::variant) << std::endl;
test::point pt(123, 456);
test::variant var = std::cref(pt);
util::apply_visitor(test::print(), var);
test::line_string line;
line.push_back(pt);
line.push_back(pt);
line.push_back(test::point(999, 333));
var = std::cref(line);
util::apply_visitor(test::print(), var);
std::cerr << "Is line (cref) ? " << var.is<std::reference_wrapper<test::line_string const>>() << std::endl;
auto const& line2 = var.get<test::line_string>(); // accessing underlying type of std::reference_wrapper<T>
test::print printer;
printer(line2);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " i-d"
using variant_type = mapbox::util::variant<int, double>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-d"
using variant_type = mapbox::util::variant<bool, int, double>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " i-d-b"
using variant_type = mapbox::util::variant<int, double, bool>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-d-c"
using variant_type = mapbox::util::variant<bool, int, double, char>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-c-d-i"
using variant_type = mapbox::util::variant<bool, int, char, double, int>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,7 @@
#include "variant.hpp"
#define NAME_EXT " b-i-i-d-c-u"
using variant_type = mapbox::util::variant<bool, int, int, double, char, short int>;
#include "binary_visitor_impl.hpp"

View File

@ -0,0 +1,204 @@
#include <type_traits>
#include "catch.hpp"
#include "variant_io.hpp"
struct add_visitor
{
add_visitor() {}
template <typename A, typename B>
double operator()(A a, B b) const
{
return a + b;
}
};
TEST_CASE("const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a{7};
const variant_type b = 3;
const variant_type c{7.1};
const variant_type d = 2.9;
const add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("non-const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 7;
const variant_type b = 3;
const variant_type c = 7.1;
const variant_type d = 2.9;
add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 7;
variant_type b = 3;
variant_type c = 7.1;
variant_type d = 2.9;
const add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("non-const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 7;
variant_type b = 3;
variant_type c = 7.1;
variant_type d = 2.9;
add_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9));
}
TEST_CASE("rvalue binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 7;
const variant_type b = 3;
const variant_type c = 7.1;
const variant_type d = 2.9;
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, b) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, d) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, c) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, d) == Approx(9.9));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, b, a) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, c) == Approx(10));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, a) == Approx(14.1));
REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, a) == Approx(9.9));
}
struct sum_mul_visitor
{
double sum;
sum_mul_visitor() : sum(0.0) {}
template <typename A, typename B>
double operator()(A a, B b)
{
double m = a * b;
sum += m;
return m;
}
};
TEST_CASE("mutable binary visitor works" NAME_EXT, "[visitor][binary visitor]")
{
const variant_type a = 2;
const variant_type b = 3;
const variant_type c = 0.1;
const variant_type d = 0.2;
sum_mul_visitor v;
REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(6));
REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(0.02));
REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(0.2));
REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(0.4));
REQUIRE(v.sum == Approx(6.62));
REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(6));
REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(0.02));
REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(0.2));
REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(0.4));
}
struct swap_visitor
{
swap_visitor(){};
template <typename A, typename B>
void operator()(A& a, B& b) const
{
using T = typename std::common_type<A, B>::type;
T tmp = a;
a = b;
b = tmp;
}
};
TEST_CASE("static mutating visitor on mutable variants works" NAME_EXT, "[visitor][binary visitor]")
{
variant_type a = 2;
variant_type b = 3;
variant_type c = 0.1;
variant_type d = 0.2;
const swap_visitor v;
SECTION("swap a and b")
{
mapbox::util::apply_visitor(v, a, b);
REQUIRE(a.get<int>() == 3);
REQUIRE(b.get<int>() == 2);
}
SECTION("swap c and d")
{
mapbox::util::apply_visitor(v, c, d);
REQUIRE(c.get<double>() == Approx(0.2));
REQUIRE(d.get<double>() == Approx(0.1));
}
SECTION("swap a and c")
{
mapbox::util::apply_visitor(v, a, c);
REQUIRE(a.get<int>() == 0);
REQUIRE(c.get<double>() == Approx(2.0));
}
SECTION("swap c and a")
{
mapbox::util::apply_visitor(v, c, a);
REQUIRE(a.get<int>() == 0);
REQUIRE(c.get<double>() == Approx(2.0));
}
}

48
test/t/issue21.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
// https://github.com/mapbox/variant/issues/21
static int count;
struct t1
{
int value;
t1(int v) : value(v)
{
++count;
}
~t1()
{
--count;
}
};
struct t2
{
int value;
t2(int v) : value(v)
{ // constructor fails
throw std::runtime_error("fail");
}
};
TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]")
{
using variant_type = mapbox::util::variant<t1, t2>;
count = 0;
{
variant_type v{42};
REQUIRE(v.is<t1>());
REQUIRE(v.get<t1>().value == 42);
REQUIRE_THROWS({
v.set<t2>(13);
});
}
REQUIRE(count == 0);
}

View File

@ -0,0 +1,36 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <string>
template <typename T>
struct mutating_visitor
{
mutating_visitor(T& val)
: val_(val) {}
void operator()(T& val) const
{
val = val_;
}
template <typename T1>
void operator()(T1&) const
{
} // no-op
T& val_;
};
TEST_CASE("variant visitation", "[visitor][unary visitor]")
{
mapbox::util::variant<int, double, std::string> var(123);
REQUIRE(var.get<int>() == 123);
int val = 456;
const mutating_visitor<int> visitor(val);
mapbox::util::apply_visitor(visitor, var);
REQUIRE(var.get<int>() == 456);
}

102
test/t/optional.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "catch.hpp"
#include "optional.hpp"
struct dummy
{
dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {}
int m_1;
int m_2;
};
TEST_CASE("optional can be instantiated with a POD type", "[optional]")
{
mapbox::util::optional<int> dbl_opt;
REQUIRE(!dbl_opt);
dbl_opt = 3;
REQUIRE(dbl_opt);
REQUIRE(dbl_opt.get() == 3);
REQUIRE(*dbl_opt == 3);
}
TEST_CASE("copy c'tor", "[optional]")
{
mapbox::util::optional<int> dbl_opt;
REQUIRE(!dbl_opt);
dbl_opt = 3;
REQUIRE(dbl_opt);
mapbox::util::optional<int> other = dbl_opt;
REQUIRE(other.get() == 3);
REQUIRE(*other == 3);
}
TEST_CASE("const operator*, const get()", "[optional]")
{
const mapbox::util::optional<int> dbl_opt = 3;
REQUIRE(dbl_opt);
auto pi1 = dbl_opt.get();
auto pi2 = *dbl_opt;
REQUIRE(pi1 == 3);
REQUIRE(pi2 == 3);
}
TEST_CASE("non-const operator*, non-const get()", "[optional]")
{
mapbox::util::optional<int> dbl_opt = 3;
REQUIRE(dbl_opt);
auto pi1 = dbl_opt.get();
auto pi2 = *dbl_opt;
REQUIRE(pi1 == 3);
REQUIRE(pi2 == 3);
}
TEST_CASE("emplace initialization, reset", "[optional]")
{
mapbox::util::optional<dummy> dummy_opt;
REQUIRE(!dummy_opt);
// rvalues, baby!
dummy_opt.emplace(1, 2);
REQUIRE(dummy_opt);
REQUIRE(dummy_opt.get().m_1 == 1);
REQUIRE((*dummy_opt).m_2 == 2);
dummy_opt.reset();
REQUIRE(!dummy_opt);
}
TEST_CASE("assignment", "[optional]")
{
mapbox::util::optional<int> a;
mapbox::util::optional<int> b;
a = 1;
b = 3;
REQUIRE(a.get() == 1);
REQUIRE(b.get() == 3);
b = a;
REQUIRE(a.get() == b.get());
REQUIRE(b.get() == 1);
}
TEST_CASE("self assignment", "[optional]")
{
mapbox::util::optional<int> a;
a = 1;
REQUIRE(a.get() == 1);
a = a;
REQUIRE(a.get() == 1);
}

View File

@ -0,0 +1,158 @@
#include "catch.hpp"
#include "recursive_wrapper.hpp"
#include <type_traits>
#include <utility>
using rwi = mapbox::util::recursive_wrapper<int>;
using rwp = mapbox::util::recursive_wrapper<std::pair<int, int>>;
static_assert(std::is_same<rwi::type, int>::value, "type check failed");
TEST_CASE("recursive wrapper of int")
{
SECTION("construct with value")
{
rwi a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
a = 8;
REQUIRE(a.get() == 8);
rwi b{a};
REQUIRE(b.get() == 8);
rwi c;
c = b;
REQUIRE(b.get() == 8);
REQUIRE(c.get() == 8);
c = 9;
REQUIRE(c.get() == 9);
int x = 10;
c = x;
REQUIRE(c.get() == 10);
b = std::move(c);
REQUIRE(b.get() == 10);
}
SECTION("construct with const reference")
{
int i = 7;
rwi a{i};
REQUIRE(a.get() == 7);
}
SECTION("implicit conversion to reference of underlying type")
{
SECTION("const")
{
rwi const a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
rwi::type const& underlying = a;
REQUIRE(underlying == 7);
}
SECTION("non const")
{
rwi a{7};
REQUIRE(a.get() == 7);
REQUIRE(*a.get_pointer() == 7);
rwi::type& underlying = a;
REQUIRE(underlying == 7);
a = 8;
REQUIRE(underlying == 8);
}
}
}
TEST_CASE("move of recursive wrapper")
{
rwi a{1};
SECTION("move constructor")
{
rwi b{std::move(a)};
REQUIRE(b.get() == 1);
}
SECTION("operator= on rvalue")
{
rwi b{2};
b = std::move(a);
REQUIRE(b.get() == 1);
}
}
TEST_CASE("swap")
{
rwi a{1};
rwi b{2};
REQUIRE(a.get() == 1);
REQUIRE(b.get() == 2);
using std::swap;
swap(a, b);
REQUIRE(a.get() == 2);
REQUIRE(b.get() == 1);
}
TEST_CASE("recursive wrapper of pair<int, int>")
{
SECTION("default constructed")
{
rwp a;
REQUIRE(a.get().first == 0);
REQUIRE(a.get().second == 0);
}
SECTION("construct with value")
{
rwp a{std::make_pair(1, 2)};
REQUIRE(a.get().first == 1);
REQUIRE(a.get().second == 2);
REQUIRE(a.get_pointer()->first == 1);
REQUIRE(a.get_pointer()->second == 2);
a = {3, 4};
REQUIRE(a.get().first == 3);
REQUIRE(a.get().second == 4);
rwp b{a};
REQUIRE(b.get().first == 3);
REQUIRE(b.get().second == 4);
rwp c;
c = b;
REQUIRE(b.get().first == 3);
REQUIRE(b.get().second == 4);
REQUIRE(c.get().first == 3);
REQUIRE(c.get().second == 4);
c = {5, 6};
REQUIRE(c.get().first == 5);
REQUIRE(c.get().second == 6);
b = std::move(c);
REQUIRE(b.get().first == 5);
REQUIRE(b.get().second == 6);
// REQUIRE(c.get_pointer() == nullptr);
}
}

52
test/t/sizeof.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
struct some_struct
{
int a;
bool b;
std::string c;
};
using variant_internal_index_type = size_t;
TEST_CASE("size of variants")
{
constexpr const auto min_overhead = sizeof(variant_internal_index_type);
using namespace std; // workaround for bug in GCC <= 4.8 where max_align_t is not in std
constexpr const auto max_overhead = alignof(max_align_t) + min_overhead;
using v1 = mapbox::util::variant<int>;
using v2 = mapbox::util::variant<int, bool, int64_t>;
using v3 = mapbox::util::variant<int, std::string>;
using v4 = mapbox::util::variant<std::string, std::string>;
using v5 = mapbox::util::variant<some_struct>;
constexpr const auto si = sizeof(int);
constexpr const auto sb = sizeof(bool);
constexpr const auto si64 = sizeof(int64_t);
constexpr const auto sd = sizeof(double);
constexpr const auto sstr = sizeof(std::string);
constexpr const auto spi = sizeof(std::pair<int, int>);
constexpr const auto ss = sizeof(some_struct);
REQUIRE(sizeof(v1) <= max_overhead + si);
REQUIRE(sizeof(v2) <= max_overhead + std::max({si, sb, si64}));
REQUIRE(sizeof(v3) <= max_overhead + std::max({si, sstr}));
REQUIRE(sizeof(v4) <= max_overhead + sstr);
REQUIRE(sizeof(v5) <= max_overhead + ss);
REQUIRE(sizeof(v1) >= min_overhead + si);
REQUIRE(sizeof(v2) >= min_overhead + std::max({si, sb, si64}));
REQUIRE(sizeof(v3) >= min_overhead + std::max({si, sstr}));
REQUIRE(sizeof(v4) >= min_overhead + sstr);
REQUIRE(sizeof(v5) >= min_overhead + ss);
}

127
test/t/unary_visitor.cpp Normal file
View File

@ -0,0 +1,127 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <string>
struct some_visitor
{
int var_;
some_visitor(int init)
: var_(init) {}
int operator()(int val) const
{
return var_ + val;
}
int operator()(double val) const
{
return var_ + int(val);
}
int operator()(const std::string&) const
{
return 0;
}
};
TEST_CASE("non-const visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
some_visitor visitor{1};
REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0);
}
TEST_CASE("const visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
const some_visitor visitor{1};
REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0);
}
TEST_CASE("rvalue visitor works on const variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
variant_type var1(123);
variant_type var2(3.2);
variant_type var3("foo");
REQUIRE(var1.get<int>() == 123);
REQUIRE(var2.get<double>() == Approx(3.2));
REQUIRE(var3.get<std::string>() == "foo");
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var1) == 124);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var2) == 4);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var3) == 0);
}
TEST_CASE("visitor works on rvalue variants", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, double, std::string>;
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{123}) == 124);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{3.2}) == 4);
REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{"foo"}) == 0);
}
struct total_sizeof
{
total_sizeof() : total_(0) {}
template <class Value>
int operator()(const Value&) const
{
total_ += int(sizeof(Value));
return total_;
}
int result() const
{
return total_;
}
mutable int total_;
}; // total_sizeof
TEST_CASE("changes in visitor should be visible", "[visitor][unary visitor]")
{
using variant_type = mapbox::util::variant<int, std::string, double>;
variant_type v;
total_sizeof ts;
v = 5.9;
REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(double));
REQUIRE(ts.result() == sizeof(double));
}
TEST_CASE("changes in const visitor (with mutable internals) should be visible", "[visitor][unary visitor]")
{
using variant_type = const mapbox::util::variant<int, std::string, double>;
variant_type v{"foo"};
const total_sizeof ts;
REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(std::string));
REQUIRE(ts.result() == sizeof(std::string));
}

570
test/t/variant.cpp Normal file
View File

@ -0,0 +1,570 @@
#include "catch.hpp"
#include "variant.hpp"
#include "variant_io.hpp"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
// Hack to make nullptr work with Catch
namespace std {
template <class C, class T>
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, std::nullptr_t)
{
return os << (void*)nullptr;
}
}
TEST_CASE("variant can be moved into vector", "[variant]")
{
using variant_type = mapbox::util::variant<bool, std::string>;
variant_type v(std::string("test"));
std::vector<variant_type> vec;
vec.emplace_back(std::move(v));
REQUIRE(v.get<std::string>() != std::string("test"));
REQUIRE(vec.at(0).get<std::string>() == std::string("test"));
}
TEST_CASE("variant should support built-in types", "[variant]")
{
SECTION("bool")
{
mapbox::util::variant<bool> v(true);
REQUIRE(v.valid());
REQUIRE(v.is<bool>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<bool>() == true);
v.set<bool>(false);
REQUIRE(v.get<bool>() == false);
v = true;
REQUIRE(v == mapbox::util::variant<bool>(true));
}
SECTION("nullptr")
{
using value_type = std::nullptr_t;
mapbox::util::variant<value_type> v(nullptr);
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == nullptr);
REQUIRE(v == mapbox::util::variant<value_type>(nullptr));
}
SECTION("unique_ptr")
{
using value_type = std::unique_ptr<std::string>;
mapbox::util::variant<value_type> v(value_type(new std::string("hello")));
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(*v.get<value_type>().get() == *value_type(new std::string("hello")).get());
REQUIRE(*v.get<value_type>() == "hello");
}
SECTION("string")
{
using value_type = std::string;
mapbox::util::variant<value_type> v(value_type("hello"));
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == value_type("hello"));
v.set<value_type>(value_type("there"));
REQUIRE(v.get<value_type>() == value_type("there"));
v = value_type("variant");
REQUIRE(v == mapbox::util::variant<value_type>(value_type("variant")));
}
SECTION("size_t")
{
using value_type = std::size_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max());
v.set<value_type>(value_type(0));
REQUIRE(v.get<value_type>() == value_type(0));
v = value_type(1);
REQUIRE(v == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int8_t")
{
using value_type = std::int8_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max());
v.set<value_type>(0);
REQUIRE(v.get<value_type>() == value_type(0));
v = value_type(1);
REQUIRE(v == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int16_t")
{
using value_type = std::int16_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max());
v.set<value_type>(0);
REQUIRE(v.get<value_type>() == value_type(0));
v = value_type(1);
REQUIRE(v == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int32_t")
{
using value_type = std::int32_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max());
v.set<value_type>(0);
REQUIRE(v.get<value_type>() == value_type(0));
v = value_type(1);
REQUIRE(v == mapbox::util::variant<value_type>(value_type(1)));
}
SECTION("int64_t")
{
using value_type = std::int64_t;
mapbox::util::variant<value_type> v(std::numeric_limits<value_type>::max());
REQUIRE(v.valid());
REQUIRE(v.is<value_type>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<value_type>() == std::numeric_limits<value_type>::max());
v.set<value_type>(0);
REQUIRE(v.get<value_type>() == value_type(0));
v = value_type(1);
REQUIRE(v == mapbox::util::variant<value_type>(value_type(1)));
}
}
struct MissionInteger
{
using value_type = uint64_t;
value_type val_;
public:
MissionInteger(uint64_t val) : val_(val) {}
bool operator==(MissionInteger const& rhs) const
{
return (val_ == rhs.get());
}
uint64_t get() const
{
return val_;
}
};
std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs)
{
os << rhs.get();
return os;
}
TEST_CASE("variant should support custom types", "[variant]")
{
// http://www.missionintegers.com/integer/34838300
mapbox::util::variant<MissionInteger> v(MissionInteger(34838300));
REQUIRE(v.valid());
REQUIRE(v.is<MissionInteger>());
REQUIRE(v.which() == 0);
REQUIRE(v.get<MissionInteger>() == MissionInteger(34838300));
REQUIRE(v.get<MissionInteger>().get() == MissionInteger::value_type(34838300));
// TODO: should both of the set usages below compile?
v.set<MissionInteger>(MissionInteger::value_type(0));
v.set<MissionInteger>(MissionInteger(0));
REQUIRE(v.get<MissionInteger>().get() == MissionInteger::value_type(0));
v = MissionInteger(1);
REQUIRE(v == mapbox::util::variant<MissionInteger>(MissionInteger(1)));
}
TEST_CASE("variant::which() returns zero based index of stored type", "[variant]")
{
using variant_type = mapbox::util::variant<bool, std::string, std::uint64_t, std::int64_t, double, float>;
// which() returns index in forward order
REQUIRE(0 == variant_type(true).which());
REQUIRE(1 == variant_type(std::string("test")).which());
REQUIRE(2 == variant_type(std::uint64_t(0)).which());
REQUIRE(3 == variant_type(std::int64_t(0)).which());
REQUIRE(4 == variant_type(double(0.0)).which());
REQUIRE(5 == variant_type(float(0.0)).which());
}
TEST_CASE("get with wrong type (here: double) should throw", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type var = 5;
REQUIRE(var.is<int>());
REQUIRE_FALSE(var.is<double>());
REQUIRE(var.get<int>() == 5);
REQUIRE_THROWS_AS({
var.get<double>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("get with wrong type (here: int) should throw", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type var = 5.0;
REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<int>());
REQUIRE(var.get<double>() == 5.0);
REQUIRE(mapbox::util::get<double>(var) == 5.0);
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(var);
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("get on const varint with wrong type (here: int) should throw", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
const variant_type var = 5.0;
REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<int>());
REQUIRE(var.get<double>() == 5.0);
REQUIRE(mapbox::util::get<double>(var) == 5.0);
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(var);
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("get with any type should throw if not initialized", "[variant]")
{
mapbox::util::variant<int, double> var{mapbox::util::no_init()};
REQUIRE_THROWS_AS({
var.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
var.get<double>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("no_init variant can be copied and moved from", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type v1{mapbox::util::no_init()};
variant_type v2{42};
variant_type v3{23};
REQUIRE(v2.get<int>() == 42);
v2 = v1;
REQUIRE_THROWS_AS({
v2.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE(v3.get<int>() == 23);
v3 = std::move(v1);
REQUIRE_THROWS_AS({
v3.get<int>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("no_init variant can be copied and moved to", "[variant]")
{
using variant_type = mapbox::util::variant<int, double>;
variant_type v1{42};
variant_type v2{mapbox::util::no_init()};
variant_type v3{mapbox::util::no_init()};
REQUIRE_THROWS_AS({
v2.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE(v1.get<int>() == 42);
v2 = v1;
REQUIRE(v2.get<int>() == 42);
REQUIRE(v1.get<int>() == 42);
REQUIRE_THROWS_AS({
v3.get<int>();
},
mapbox::util::bad_variant_access&);
v3 = std::move(v1);
REQUIRE(v3.get<int>() == 42);
}
TEST_CASE("implicit conversion", "[variant][implicit conversion]")
{
using variant_type = mapbox::util::variant<int>;
variant_type var(5.0); // converted to int
REQUIRE(var.get<int>() == 5);
var = 6.0; // works for operator=, too
REQUIRE(var.get<int>() == 6);
}
TEST_CASE("implicit conversion to first type in variant type list", "[variant][implicit conversion]")
{
using variant_type = mapbox::util::variant<long, char>;
variant_type var = 5.0; // converted to long
REQUIRE(var.get<long>() == 5);
REQUIRE_THROWS_AS({
var.get<char>();
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]")
{
using variant_type = mapbox::util::variant<unsigned char>;
variant_type var = 100.0;
CHECK(var.get<unsigned char>() == static_cast<unsigned char>(100.0));
CHECK(var.get<unsigned char>() == static_cast<unsigned char>(static_cast<unsigned int>(100.0)));
}
struct dummy
{
};
TEST_CASE("implicit conversion to a suitable type", "[variant][implicit conversion]")
{
using mapbox::util::variant;
CHECK_NOTHROW((variant<dummy, float, std::string>(123)).get<float>());
CHECK_NOTHROW((variant<dummy, float, std::string>("foo")).get<std::string>());
}
TEST_CASE("value_traits for non-convertible type", "[variant::detail]")
{
namespace detail = mapbox::util::detail;
using target_type = detail::value_traits<dummy, int>::target_type;
CHECK((std::is_same<target_type, void>::value) == true);
}
TEST_CASE("Type indexing should work with variants with duplicated types", "[variant::detail]")
{
// Index is in reverse order
REQUIRE((mapbox::util::detail::value_traits<bool, bool, int, double, std::string>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<bool, bool, int, double, int>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<int, bool, int, double, std::string>::index == 2));
REQUIRE((mapbox::util::detail::value_traits<int, bool, int, double, int>::index == 2));
REQUIRE((mapbox::util::detail::value_traits<double, bool, int, double, std::string>::index == 1));
REQUIRE((mapbox::util::detail::value_traits<bool, bool, int, bool, std::string>::index == 3));
REQUIRE((mapbox::util::detail::value_traits<std::string, bool, int, double, std::string>::index == 0));
REQUIRE((mapbox::util::detail::value_traits<dummy, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value));
REQUIRE((mapbox::util::detail::value_traits<std::vector<int>, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value));
}
TEST_CASE("variant default constructor", "[variant][default constructor]")
{
// By default variant is initialised with (default constructed) first type in template parameters pack
// As a result first type in Types... must be default constructable
// NOTE: index in reverse order -> index = N - 1
using variant_type = mapbox::util::variant<int, double, std::string>;
REQUIRE(variant_type{}.which() == 0);
REQUIRE(variant_type{}.valid());
REQUIRE_FALSE(variant_type{mapbox::util::no_init()}.valid());
}
TEST_CASE("variant printer", "[visitor][unary visitor][printer]")
{
using variant_type = mapbox::util::variant<int, double, std::string>;
std::vector<variant_type> var = {2.1, 123, "foo", 456};
std::stringstream out;
std::copy(var.begin(), var.end(), std::ostream_iterator<variant_type>(out, ","));
out << var[2];
REQUIRE(out.str() == "2.1,123,foo,456,foo");
}
TEST_CASE("swapping variants should do the right thing", "[variant]")
{
using variant_type = mapbox::util::variant<int, double, std::string>;
variant_type a = 7;
variant_type b = 3;
variant_type c = 3.141;
variant_type d = "foo";
variant_type e = "a long string that is longer than small string optimization";
using std::swap;
swap(a, b);
REQUIRE(a.get<int>() == 3);
REQUIRE(a.which() == 0);
REQUIRE(b.get<int>() == 7);
REQUIRE(b.which() == 0);
swap(b, c);
REQUIRE(b.get<double>() == Approx(3.141));
REQUIRE(b.which() == 1);
REQUIRE(c.get<int>() == 7);
REQUIRE(c.which() == 0);
swap(b, d);
REQUIRE(b.get<std::string>() == "foo");
REQUIRE(b.which() == 2);
REQUIRE(d.get<double>() == Approx(3.141));
REQUIRE(d.which() == 1);
swap(b, e);
REQUIRE(b.get<std::string>() == "a long string that is longer than small string optimization");
REQUIRE(b.which() == 2);
REQUIRE(e.get<std::string>() == "foo");
REQUIRE(e.which() == 2);
}
TEST_CASE("variant should work with equality operators")
{
using variant_type = mapbox::util::variant<int, std::string>;
variant_type a{1};
variant_type b{1};
variant_type c{2};
variant_type s{"foo"};
REQUIRE(a == a);
REQUIRE(a == b);
REQUIRE_FALSE(a == c);
REQUIRE_FALSE(a == s);
REQUIRE_FALSE(c == s);
REQUIRE_FALSE(a != a);
REQUIRE_FALSE(a != b);
REQUIRE(a != c);
REQUIRE(a != s);
REQUIRE(c != s);
}
TEST_CASE("variant should work with comparison operators")
{
using variant_type = mapbox::util::variant<int, std::string>;
variant_type a{1};
variant_type b{1};
variant_type c{2};
variant_type s{"foo"};
variant_type t{"bar"};
REQUIRE_FALSE(a < a);
REQUIRE_FALSE(a < b);
REQUIRE(a < c);
REQUIRE(a < s);
REQUIRE(c < s);
REQUIRE(t < s);
REQUIRE_FALSE(a > a);
REQUIRE_FALSE(a > b);
REQUIRE_FALSE(a > c);
REQUIRE_FALSE(a > s);
REQUIRE_FALSE(c > s);
REQUIRE_FALSE(t > s);
REQUIRE(a <= a);
REQUIRE(a <= b);
REQUIRE(a <= c);
REQUIRE(a <= s);
REQUIRE(c <= s);
REQUIRE(t <= s);
REQUIRE(a >= a);
REQUIRE(a >= b);
REQUIRE_FALSE(a >= c);
REQUIRE_FALSE(a >= s);
REQUIRE_FALSE(c >= s);
REQUIRE_FALSE(t >= s);
}
TEST_CASE("storing reference wrappers works")
{
using variant_type = mapbox::util::variant<std::reference_wrapper<int>, std::reference_wrapper<double>>;
int a = 1;
variant_type v{std::ref(a)};
REQUIRE(v.get<int>() == 1);
REQUIRE(mapbox::util::get<int>(v) == 1);
REQUIRE_THROWS_AS({
v.get<double>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double>(v);
},
mapbox::util::bad_variant_access&);
a = 2;
REQUIRE(v.get<int>() == 2);
v.get<int>() = 3;
REQUIRE(a == 3);
double b = 3.141;
v = std::ref(b);
REQUIRE(v.get<double>() == Approx(3.141));
REQUIRE(mapbox::util::get<double>(v) == Approx(3.141));
REQUIRE_THROWS_AS({
v.get<int>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int>(v);
},
mapbox::util::bad_variant_access&);
b = 2.718;
REQUIRE(v.get<double>() == Approx(2.718));
a = 3;
REQUIRE(v.get<double>() == Approx(2.718));
v.get<double>() = 4.1;
REQUIRE(b == Approx(4.1));
REQUIRE_THROWS_AS({
v.get<int>() = 4;
},
mapbox::util::bad_variant_access&);
}
TEST_CASE("storing reference wrappers to consts works")
{
using variant_type = mapbox::util::variant<std::reference_wrapper<int const>, std::reference_wrapper<double const>>;
int a = 1;
variant_type v{std::cref(a)};
REQUIRE(v.get<int const>() == 1);
REQUIRE(v.get<int>() == 1); // this works (see #82)
REQUIRE(mapbox::util::get<int const>(v) == 1);
// REQUIRE(mapbox::util::get<int>(v) == 1); // this doesn't work (see #82)
REQUIRE_THROWS_AS({
v.get<double const>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<double const>(v);
},
mapbox::util::bad_variant_access&);
double b = 3.141;
v = std::cref(b);
REQUIRE(v.get<double const>() == Approx(3.141));
REQUIRE(mapbox::util::get<double const>(v) == Approx(3.141));
REQUIRE_THROWS_AS({
v.get<int const>();
},
mapbox::util::bad_variant_access&);
REQUIRE_THROWS_AS({
mapbox::util::get<int const>(v);
},
mapbox::util::bad_variant_access&);
}

126
test/unique_ptr_test.cpp Normal file
View File

@ -0,0 +1,126 @@
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>
#include <utility>
#include <boost/timer/timer.hpp>
#include "variant.hpp"
using namespace mapbox;
namespace test {
struct add;
struct sub;
template <typename OpTag>
struct binary_op;
typedef util::variant<int,
std::unique_ptr<binary_op<add>>,
std::unique_ptr<binary_op<sub>>>
expression;
template <typename Op>
struct binary_op
{
expression left; // variant instantiated here...
expression right;
binary_op(expression&& lhs, expression&& rhs)
: left(std::move(lhs)), right(std::move(rhs))
{
}
};
struct print
{
template <typename T>
void operator()(T const& val) const
{
std::cerr << val << ":" << typeid(T).name() << std::endl;
}
};
struct test
{
template <typename T>
std::string operator()(T const& obj) const
{
return std::string("TYPE_ID=") + typeid(obj).name();
}
};
struct calculator
{
public:
int operator()(int value) const
{
return value;
}
int operator()(std::unique_ptr<binary_op<add>> const& binary) const
{
return util::apply_visitor(calculator(), binary->left) + util::apply_visitor(calculator(), binary->right);
}
int operator()(std::unique_ptr<binary_op<sub>> const& binary) const
{
return util::apply_visitor(calculator(), binary->left) - util::apply_visitor(calculator(), binary->right);
}
};
struct to_string
{
public:
std::string operator()(int value) const
{
return std::to_string(value);
}
std::string operator()(std::unique_ptr<binary_op<add>> const& binary) const
{
return util::apply_visitor(to_string(), binary->left) + std::string("+") + util::apply_visitor(to_string(), binary->right);
}
std::string operator()(std::unique_ptr<binary_op<sub>> const& binary) const
{
return util::apply_visitor(to_string(), binary->left) + std::string("-") + util::apply_visitor(to_string(), binary->right);
}
};
} // namespace test
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage" << argv[0] << " <num-iter>" << std::endl;
return EXIT_FAILURE;
}
const std::size_t NUM_ITER = static_cast<std::size_t>(std::stol(argv[1]));
test::expression sum(std::unique_ptr<test::binary_op<test::add>>(new test::binary_op<test::add>(2, 3)));
test::expression result(std::unique_ptr<test::binary_op<test::sub>>(new test::binary_op<test::sub>(std::move(sum), 4)));
std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl;
int total = 0;
{
boost::timer::auto_cpu_timer t;
for (std::size_t i = 0; i < NUM_ITER; ++i)
{
total += util::apply_visitor(test::calculator(), result);
}
}
std::cerr << "total=" << total << std::endl;
std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl;
return EXIT_SUCCESS;
}

2
test/unit.cpp Normal file
View File

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

35
variant.gyp Normal file
View File

@ -0,0 +1,35 @@
{
"includes": [
"common.gypi"
],
"targets": [
{
"target_name": "tests",
"type": "executable",
"sources": [
"test/unit.cpp",
"test/t/binary_visitor_1.cpp",
"test/t/binary_visitor_2.cpp",
"test/t/binary_visitor_3.cpp",
"test/t/binary_visitor_4.cpp",
"test/t/binary_visitor_5.cpp",
"test/t/binary_visitor_6.cpp",
"test/t/issue21.cpp",
"test/t/mutating_visitor.cpp",
"test/t/optional.cpp",
"test/t/recursive_wrapper.cpp",
"test/t/sizeof.cpp",
"test/t/unary_visitor.cpp",
"test/t/variant.cpp"
],
"xcode_settings": {
"SDKROOT": "macosx",
"SUPPORTED_PLATFORMS":["macosx"]
},
"include_dirs": [
"./",
"test/include"
]
}
]
}

901
variant.hpp Normal file
View File

@ -0,0 +1,901 @@
#ifndef MAPBOX_UTIL_VARIANT_HPP
#define MAPBOX_UTIL_VARIANT_HPP
#include <cassert>
#include <cstddef> // size_t
#include <new> // operator new
#include <stdexcept> // runtime_error
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include "recursive_wrapper.hpp"
// clang-format off
// [[deprecated]] is only available in C++14, use this for the time being
#if __cplusplus <= 201103L
# ifdef __GNUC__
# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated)
# else
# define MAPBOX_VARIANT_DEPRECATED
# endif
#else
# define MAPBOX_VARIANT_DEPRECATED [[deprecated]]
#endif
#ifdef _MSC_VER
// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx
#ifdef NDEBUG
#define VARIANT_INLINE __forceinline
#else
#define VARIANT_INLINE __declspec(noinline)
#endif
#else
#ifdef NDEBUG
#define VARIANT_INLINE inline __attribute__((always_inline))
#else
#define VARIANT_INLINE __attribute__((noinline))
#endif
#endif
// clang-format on
#define VARIANT_MAJOR_VERSION 1
#define VARIANT_MINOR_VERSION 1
#define VARIANT_PATCH_VERSION 0
#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION)
namespace mapbox {
namespace util {
// XXX This should derive from std::logic_error instead of std::runtime_error.
// See https://github.com/mapbox/variant/issues/48 for details.
class bad_variant_access : public std::runtime_error
{
public:
explicit bad_variant_access(const std::string& what_arg)
: runtime_error(what_arg) {}
explicit bad_variant_access(const char* what_arg)
: runtime_error(what_arg) {}
}; // class bad_variant_access
template <typename R = void>
struct MAPBOX_VARIANT_DEPRECATED static_visitor
{
using result_type = R;
protected:
static_visitor() {}
~static_visitor() {}
};
namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
template <typename T, typename... Types>
struct direct_type;
template <typename T, typename First, typename... Types>
struct direct_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_same<T, First>::value
? sizeof...(Types)
: direct_type<T, Types...>::index;
};
template <typename T>
struct direct_type<T>
{
static constexpr std::size_t index = invalid_value;
};
template <typename T, typename... Types>
struct convertible_type;
template <typename T, typename First, typename... Types>
struct convertible_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_convertible<T, First>::value
? sizeof...(Types)
: convertible_type<T, Types...>::index;
};
template <typename T>
struct convertible_type<T>
{
static constexpr std::size_t index = invalid_value;
};
template <typename T, typename... Types>
struct value_traits
{
using value_type = typename std::remove_reference<T>::type;
static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
static constexpr bool is_direct = direct_index != invalid_value;
static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
static constexpr bool is_valid = index != invalid_value;
static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
};
// check if T is in Types...
template <typename T, typename... Types>
struct has_type;
template <typename T, typename First, typename... Types>
struct has_type<T, First, Types...>
{
static constexpr bool value = std::is_same<T, First>::value || has_type<T, Types...>::value;
};
template <typename T>
struct has_type<T> : std::false_type
{
};
template <typename T, typename... Types>
struct is_valid_type;
template <typename T, typename First, typename... Types>
struct is_valid_type<T, First, Types...>
{
static constexpr bool value = std::is_convertible<T, First>::value || is_valid_type<T, Types...>::value;
};
template <typename T>
struct is_valid_type<T> : std::false_type
{
};
template <typename T, typename R = void>
struct enable_if_type
{
using type = R;
};
template <typename F, typename V, typename Enable = void>
struct result_of_unary_visit
{
using type = typename std::result_of<F(V&)>::type;
};
template <typename F, typename V>
struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
{
using type = typename F::result_type;
};
template <typename F, typename V, typename Enable = void>
struct result_of_binary_visit
{
using type = typename std::result_of<F(V&, V&)>::type;
};
template <typename F, typename V>
struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
{
using type = typename F::result_type;
};
template <std::size_t arg1, std::size_t... others>
struct static_max;
template <std::size_t arg>
struct static_max<arg>
{
static const std::size_t value = arg;
};
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
struct static_max<arg1, arg2, others...>
{
static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
};
template <typename... Types>
struct variant_helper;
template <typename T, typename... Types>
struct variant_helper<T, Types...>
{
VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
{
if (type_index == sizeof...(Types))
{
reinterpret_cast<T*>(data)->~T();
}
else
{
variant_helper<Types...>::destroy(type_index, data);
}
}
VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
}
else
{
variant_helper<Types...>::move(old_type_index, old_value, new_value);
}
}
VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
new (new_value) T(*reinterpret_cast<const T*>(old_value));
}
else
{
variant_helper<Types...>::copy(old_type_index, old_value, new_value);
}
}
};
template <>
struct variant_helper<>
{
VARIANT_INLINE static void destroy(const std::size_t, void*) {}
VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
};
template <typename T>
struct unwrapper
{
static T const& apply_const(T const& obj) { return obj; }
static T& apply(T& obj) { return obj; }
};
template <typename T>
struct unwrapper<recursive_wrapper<T>>
{
static auto apply_const(recursive_wrapper<T> const& obj)
-> typename recursive_wrapper<T>::type const&
{
return obj.get();
}
static auto apply(recursive_wrapper<T>& obj)
-> typename recursive_wrapper<T>::type&
{
return obj.get();
}
};
template <typename T>
struct unwrapper<std::reference_wrapper<T>>
{
static auto apply_const(std::reference_wrapper<T> const& obj)
-> typename std::reference_wrapper<T>::type const&
{
return obj.get();
}
static auto apply(std::reference_wrapper<T>& obj)
-> typename std::reference_wrapper<T>::type&
{
return obj.get();
}
};
template <typename F, typename V, typename R, typename... Types>
struct dispatcher;
template <typename F, typename V, typename R, typename T, typename... Types>
struct dispatcher<F, V, R, T, Types...>
{
VARIANT_INLINE static R apply_const(V const& v, F&& f)
{
if (v.template is<T>())
{
return f(unwrapper<T>::apply_const(v.template get<T>()));
}
else
{
return dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& v, F&& f)
{
if (v.template is<T>())
{
return f(unwrapper<T>::apply(v.template get<T>()));
}
else
{
return dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T>
struct dispatcher<F, V, R, T>
{
VARIANT_INLINE static R apply_const(V const& v, F&& f)
{
return f(unwrapper<T>::apply_const(v.template get<T>()));
}
VARIANT_INLINE static R apply(V& v, F&& f)
{
return f(unwrapper<T>::apply(v.template get<T>()));
}
};
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher_rhs;
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
if (rhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T0>::apply_const(lhs.template get<T0>()),
unwrapper<T1>::apply_const(rhs.template get<T1>()));
}
else
{
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
if (rhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T0>::apply(lhs.template get<T0>()),
unwrapper<T1>::apply(rhs.template get<T1>()));
}
else
{
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T0, typename T1>
struct binary_dispatcher_rhs<F, V, R, T0, T1>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
return f(unwrapper<T0>::apply_const(lhs.template get<T0>()),
unwrapper<T1>::apply_const(rhs.template get<T1>()));
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
return f(unwrapper<T0>::apply(lhs.template get<T0>()),
unwrapper<T1>::apply(rhs.template get<T1>()));
}
};
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher_lhs;
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
if (lhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T1>::apply_const(lhs.template get<T1>()),
unwrapper<T0>::apply_const(rhs.template get<T0>()));
}
else
{
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
if (lhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T1>::apply(lhs.template get<T1>()),
unwrapper<T0>::apply(rhs.template get<T0>()));
}
else
{
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T0, typename T1>
struct binary_dispatcher_lhs<F, V, R, T0, T1>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
return f(unwrapper<T1>::apply_const(lhs.template get<T1>()),
unwrapper<T0>::apply_const(rhs.template get<T0>()));
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
return f(unwrapper<T1>::apply(lhs.template get<T1>()),
unwrapper<T0>::apply(rhs.template get<T0>()));
}
};
template <typename F, typename V, typename R, typename... Types>
struct binary_dispatcher;
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher<F, V, R, T, Types...>
{
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
{
if (v0.template is<T>())
{
if (v1.template is<T>())
{
return f(unwrapper<T>::apply_const(v0.template get<T>()),
unwrapper<T>::apply_const(v1.template get<T>())); // call binary functor
}
else
{
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
}
else if (v1.template is<T>())
{
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
{
if (v0.template is<T>())
{
if (v1.template is<T>())
{
return f(unwrapper<T>::apply(v0.template get<T>()),
unwrapper<T>::apply(v1.template get<T>())); // call binary functor
}
else
{
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
}
}
else if (v1.template is<T>())
{
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
}
return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
}
};
template <typename F, typename V, typename R, typename T>
struct binary_dispatcher<F, V, R, T>
{
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
{
return f(unwrapper<T>::apply_const(v0.template get<T>()),
unwrapper<T>::apply_const(v1.template get<T>())); // call binary functor
}
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
{
return f(unwrapper<T>::apply(v0.template get<T>()),
unwrapper<T>::apply(v1.template get<T>())); // call binary functor
}
};
// comparator functors
struct equal_comp
{
template <typename T>
bool operator()(T const& lhs, T const& rhs) const
{
return lhs == rhs;
}
};
struct less_comp
{
template <typename T>
bool operator()(T const& lhs, T const& rhs) const
{
return lhs < rhs;
}
};
template <typename Variant, typename Comp>
class comparer
{
public:
explicit comparer(Variant const& lhs) noexcept
: lhs_(lhs) {}
comparer& operator=(comparer const&) = delete;
// visitor
template <typename T>
bool operator()(T const& rhs_content) const
{
T const& lhs_content = lhs_.template get<T>();
return Comp()(lhs_content, rhs_content);
}
private:
Variant const& lhs_;
};
// True if Predicate matches for all of the types Ts
template <template <typename> class Predicate, typename... Ts>
struct static_all_of : std::is_same<std::tuple<std::true_type, typename Predicate<Ts>::type...>,
std::tuple<typename Predicate<Ts>::type..., std::true_type>>
{
};
// True if Predicate matches for none of the types Ts
template <template <typename> class Predicate, typename... Ts>
struct static_none_of : std::is_same<std::tuple<std::false_type, typename Predicate<Ts>::type...>,
std::tuple<typename Predicate<Ts>::type..., std::false_type>>
{
};
} // namespace detail
struct no_init
{
};
template <typename... Types>
class variant
{
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
static_assert(detail::static_none_of<std::is_reference, Types...>::value, "Variant can not hold reference types. Maybe use std::reference?");
private:
static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;
using first_type = typename std::tuple_element<0, std::tuple<Types...>>::type;
using data_type = typename std::aligned_storage<data_size, data_align>::type;
using helper_type = detail::variant_helper<Types...>;
std::size_t type_index;
data_type data;
public:
VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
: type_index(sizeof...(Types)-1)
{
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
new (&data) first_type();
}
VARIANT_INLINE variant(no_init) noexcept
: type_index(detail::invalid_value) {}
// http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
template <typename T, typename Traits = detail::value_traits<T, Types...>,
typename Enable = typename std::enable_if<Traits::is_valid>::type>
VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value)
: type_index(Traits::index)
{
new (&data) typename Traits::target_type(std::forward<T>(val));
}
VARIANT_INLINE variant(variant<Types...> const& old)
: type_index(old.type_index)
{
helper_type::copy(old.type_index, &old.data, &data);
}
VARIANT_INLINE variant(variant<Types...>&& old) noexcept(std::is_nothrow_move_constructible<std::tuple<Types...>>::value)
: type_index(old.type_index)
{
helper_type::move(old.type_index, &old.data, &data);
}
private:
VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
helper_type::copy(rhs.type_index, &rhs.data, &data);
type_index = rhs.type_index;
}
VARIANT_INLINE void move_assign(variant<Types...>&& rhs)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
helper_type::move(rhs.type_index, &rhs.data, &data);
type_index = rhs.type_index;
}
public:
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
{
move_assign(std::move(other));
return *this;
}
VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
{
copy_assign(other);
return *this;
}
// conversions
// move-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
{
variant<Types...> temp(std::forward<T>(rhs));
move_assign(std::move(temp));
return *this;
}
// copy-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
{
variant<Types...> temp(rhs);
copy_assign(temp);
return *this;
}
template <typename T>
VARIANT_INLINE bool is() const
{
static_assert(detail::has_type<T, Types...>::value, "invalid type in T in `is<T>()` for this variant");
return type_index == detail::direct_type<T, Types...>::index;
}
VARIANT_INLINE bool valid() const
{
return type_index != detail::invalid_value;
}
template <typename T, typename... Args>
VARIANT_INLINE void set(Args&&... args)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
new (&data) T(std::forward<Args>(args)...);
type_index = detail::direct_type<T, Types...>::index;
}
// get<T>()
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T*>(&data);
}
else
{
throw bad_variant_access("in get<T>()");
}
}
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T const*>(&data);
}
else
{
throw bad_variant_access("in get<T>()");
}
}
// get<T>() - T stored as recursive_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
// get<T>() - T stored as std::reference_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
{
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
// This function is deprecated because it returns an internal index field.
// Use which() instead.
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
{
return type_index;
}
VARIANT_INLINE int which() const noexcept
{
return static_cast<int>(sizeof...(Types)-type_index - 1);
}
// visitor
// unary
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
auto VARIANT_INLINE static visit(V const& v, F&& f)
-> decltype(detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f)))
{
return detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
}
// non-const
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
auto VARIANT_INLINE static visit(V& v, F&& f)
-> decltype(detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f)))
{
return detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
}
// binary
// const
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f)
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f)))
{
return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
// non-const
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f)
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f)))
{
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
}
~variant() noexcept // no-throw destructor
{
helper_type::destroy(type_index, &data);
}
// comparison operators
// equality
VARIANT_INLINE bool operator==(variant const& rhs) const
{
assert(valid() && rhs.valid());
if (this->which() != rhs.which())
{
return false;
}
detail::comparer<variant, detail::equal_comp> visitor(*this);
return visit(rhs, visitor);
}
VARIANT_INLINE bool operator!=(variant const& rhs) const
{
return !(*this == rhs);
}
// less than
VARIANT_INLINE bool operator<(variant const& rhs) const
{
assert(valid() && rhs.valid());
if (this->which() != rhs.which())
{
return this->which() < rhs.which();
}
detail::comparer<variant, detail::less_comp> visitor(*this);
return visit(rhs, visitor);
}
VARIANT_INLINE bool operator>(variant const& rhs) const
{
return rhs < *this;
}
VARIANT_INLINE bool operator<=(variant const& rhs) const
{
return !(*this > rhs);
}
VARIANT_INLINE bool operator>=(variant const& rhs) const
{
return !(*this < rhs);
}
};
// unary visitor interface
// const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
{
return V::visit(v, std::forward<F>(f));
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward<F>(f)))
{
return V::visit(v, std::forward<F>(f));
}
// binary visitor interface
// const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
{
return V::binary_visit(v0, v1, std::forward<F>(f));
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
{
return V::binary_visit(v0, v1, std::forward<F>(f));
}
// getter interface
template <typename ResultType, typename T>
ResultType& get(T& var)
{
return var.template get<ResultType>();
}
template <typename ResultType, typename T>
ResultType const& get(T const& var)
{
return var.template get<ResultType>();
}
} // namespace util
} // namespace mapbox
#endif // MAPBOX_UTIL_VARIANT_HPP

45
variant_io.hpp Normal file
View File

@ -0,0 +1,45 @@
#ifndef MAPBOX_UTIL_VARIANT_IO_HPP
#define MAPBOX_UTIL_VARIANT_IO_HPP
#include <iosfwd>
#include "variant.hpp"
namespace mapbox {
namespace util {
namespace detail {
// operator<< helper
template <typename Out>
class printer
{
public:
explicit printer(Out& out)
: out_(out) {}
printer& operator=(printer const&) = delete;
// visitor
template <typename T>
void operator()(T const& operand) const
{
out_ << operand;
}
private:
Out& out_;
};
}
// operator<<
template <typename CharT, typename Traits, typename... Types>
VARIANT_INLINE std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& out, variant<Types...> const& rhs)
{
detail::printer<std::basic_ostream<CharT, Traits>> visitor(out);
apply_visitor(visitor, rhs);
return out;
}
} // namespace util
} // namespace mapbox
#endif // MAPBOX_UTIL_VARIANT_IO_HPP