├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── .ycm_extra_conf.py ├── CHANGELOG.md ├── Jamroot ├── LICENSE ├── LICENSE_1_0.txt ├── Makefile ├── README.md ├── appveyor.yml ├── common.gypi ├── doc ├── other_implementations.md └── standards_effort.md ├── include └── mapbox │ ├── optional.hpp │ ├── recursive_wrapper.hpp │ ├── variant.hpp │ ├── variant_cast.hpp │ ├── variant_io.hpp │ └── variant_visitor.hpp ├── package.json ├── scripts ├── build-appveyor.bat ├── build-local.bat └── run_compilation_failure_tests.sh ├── test ├── bench_variant.cpp ├── binary_visitor_test.cpp ├── boost_variant_hello_world.cpp ├── compilation_failure │ ├── default_constructor.cpp │ ├── empty_typelist.cpp │ ├── equality.cpp │ ├── get_type.cpp │ ├── is_type.cpp │ ├── mutating_visitor_on_const.cpp │ └── no-reference.cpp ├── hashable_test.cpp ├── include │ ├── auto_cpu_timer.hpp │ └── catch.hpp ├── lambda_overload_test.cpp ├── our_variant_hello_world.cpp ├── recursive_wrapper_test.cpp ├── reference_wrapper_test.cpp ├── t │ ├── binary_visitor_1.cpp │ ├── binary_visitor_2.cpp │ ├── binary_visitor_3.cpp │ ├── binary_visitor_4.cpp │ ├── binary_visitor_5.cpp │ ├── binary_visitor_6.cpp │ ├── binary_visitor_impl.hpp │ ├── issue122.cpp │ ├── issue21.cpp │ ├── mutating_visitor.cpp │ ├── nothrow_move.cpp │ ├── optional.cpp │ ├── recursive_wrapper.cpp │ ├── sizeof.cpp │ ├── unary_visitor.cpp │ ├── variant.cpp │ ├── variant_alternative.cpp │ └── visitor_result_type.cpp ├── unique_ptr_test.cpp └── unit.cpp └── variant.gyp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Mapbox.Variant C/C+ style 3 | Language: Cpp 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: false 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: true 25 | AfterControlStatement: true 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: true 30 | AfterStruct: true 31 | AfterUnion: true 32 | BeforeCatch: true 33 | BeforeElse: true 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Custom 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | ColumnLimit: 0 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 42 | ConstructorInitializerIndentWidth: 4 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: false 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 51 | Priority: 2 52 | - Regex: '^(<|"(gtest|isl|json)/)' 53 | Priority: 3 54 | - Regex: '.*' 55 | Priority: 1 56 | IndentCaseLabels: false 57 | IndentWidth: 4 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: true 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | ObjCBlockIndentWidth: 2 65 | ObjCSpaceAfterProperty: false 66 | ObjCSpaceBeforeProtocolList: true 67 | PenaltyBreakBeforeFirstCallParameter: 19 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 60 73 | PointerAlignment: Left 74 | ReflowComments: true 75 | SortIncludes: true 76 | SpaceAfterCStyleCast: false 77 | SpaceBeforeAssignmentOperators: true 78 | SpaceBeforeParens: ControlStatements 79 | SpaceInEmptyParentheses: false 80 | SpacesBeforeTrailingComments: 1 81 | SpacesInAngles: false 82 | SpacesInContainerLiterals: true 83 | SpacesInCStyleCastParentheses: false 84 | SpacesInParentheses: false 85 | SpacesInSquareBrackets: false 86 | Standard: Cpp11 87 | TabWidth: 4 88 | UseTab: Never 89 | ... 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | out 3 | profiling 4 | build 5 | deps 6 | *.gcda 7 | *.gcno 8 | .ycm_extra_conf.pyc 9 | mason_packages 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".mason"] 2 | path = .mason 3 | url = https://github.com/mapbox/mason.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | # clang++ 4.0 via mason with -flto and -fsanitize=cfi 8 | - os: linux 9 | compiler: "clang++-40-mason" 10 | env: CXX=clang++-4.0 CXXFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" LDFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" 11 | addons: 12 | apt: 13 | sources: [ 'ubuntu-toolchain-r-test' ] 14 | packages: [ 'libstdc++-4.9-dev' ] 15 | before_install: 16 | - git submodule update --init 17 | - ./.mason/mason install clang++ 4.0.1 18 | - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} 19 | - ./.mason/mason install binutils 2.27 20 | - export PATH=$(./.mason/mason prefix binutils 2.27)/bin:${PATH} 21 | # clang++ 4.0 via mason with -fsanitize=address 22 | - os: linux 23 | compiler: "clang++-40-mason" 24 | env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer -fno-common" LDFLAGS="-fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1 25 | sudo: required 26 | addons: 27 | apt: 28 | sources: [ 'ubuntu-toolchain-r-test' ] 29 | packages: [ 'libstdc++-4.9-dev' ] 30 | before_install: 31 | - git submodule update --init 32 | - ./.mason/mason install clang++ 4.0.1 33 | - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} 34 | # clang++ 4.0 via mason with -fsanitize=undefined 35 | - os: linux 36 | compiler: "clang++-40-mason" 37 | env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined" 38 | addons: 39 | apt: 40 | sources: [ 'ubuntu-toolchain-r-test' ] 41 | packages: [ 'libstdc++-4.9-dev' ] 42 | before_install: 43 | - git submodule update --init 44 | - ./.mason/mason install clang++ 4.0.1 45 | - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} 46 | # clang++ 4.0 via mason with -fsanitize=integer 47 | - os: linux 48 | compiler: "clang++-40-mason" 49 | env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=integer" LDFLAGS="-fsanitize=integer" 50 | addons: 51 | apt: 52 | sources: [ 'ubuntu-toolchain-r-test' ] 53 | packages: [ 'libstdc++-4.9-dev' ] 54 | before_install: 55 | - git submodule update --init 56 | - ./.mason/mason install clang++ 4.0.1 57 | - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} 58 | # clang++ 4.0 via mason with -fsanitize=safe-stack 59 | - os: linux 60 | compiler: "clang++-40-mason" 61 | env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=safe-stack" LDFLAGS="-fsanitize=safe-stack" 62 | addons: 63 | apt: 64 | sources: [ 'ubuntu-toolchain-r-test' ] 65 | packages: [ 'libstdc++-4.9-dev' ] 66 | before_install: 67 | - git submodule update --init 68 | - ./.mason/mason install clang++ 4.0.1 69 | - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} 70 | - os: osx 71 | osx_image: xcode8 72 | env: OSX_OLDEST_SUPPORTED=10.7 TEST_GYP_BUILD=True 73 | compiler: clang 74 | - os: osx 75 | osx_image: xcode8 76 | env: OSX_OLDEST_SUPPORTED=10.12 77 | compiler: clang 78 | - os: linux 79 | compiler: "clang35" 80 | env: CXX=clang++-3.5 COVERAGE=True 81 | addons: 82 | apt: 83 | sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] 84 | packages: [ 'clang-3.5', 'libstdc++-4.9-dev' ] 85 | - os: linux 86 | compiler: "clang36" 87 | env: CXX=clang++-3.6 88 | addons: 89 | apt: 90 | sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ] 91 | packages: [ 'clang-3.6' ] 92 | - os: linux 93 | compiler: "gcc48" 94 | env: CXX=g++-4.8 95 | addons: 96 | apt: 97 | sources: [ 'ubuntu-toolchain-r-test' ] 98 | packages: [ 'g++-4.8' ] 99 | - os: linux 100 | compiler: "gcc49" 101 | env: CXX=g++-4.9 102 | addons: 103 | apt: 104 | sources: [ 'ubuntu-toolchain-r-test' ] 105 | packages: [ 'g++-4.9' ] 106 | - os: linux 107 | compiler: "gcc5" 108 | env: CXX=g++-5 109 | addons: 110 | apt: 111 | sources: [ 'ubuntu-toolchain-r-test' ] 112 | packages: [ 'g++-5' ] 113 | - os: linux 114 | compiler: "gcc6" 115 | env: CXX=g++-6 CXX_STD=c++14 116 | addons: 117 | apt: 118 | sources: [ 'ubuntu-toolchain-r-test' ] 119 | packages: [ 'g++-6' ] 120 | 121 | install: 122 | - echo ${CXX} 123 | - if [[ $(uname -s) == 'Linux' ]]; then 124 | export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages; 125 | else 126 | export PYTHONPATH=$(pwd)/.local/lib/python/site-packages; 127 | fi 128 | - if [[ ${COVERAGE:-0} == 'True' ]]; then 129 | PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls; 130 | fi 131 | 132 | script: 133 | # Build in Release 134 | - make 135 | - make test 136 | - make bench 137 | - make sizes 138 | - scripts/run_compilation_failure_tests.sh 139 | - make clean; 140 | # Build in Debug 141 | - export BUILDTYPE=Debug 142 | - make 143 | - make test 144 | - make bench 145 | - make sizes 146 | - scripts/run_compilation_failure_tests.sh 147 | - if [[ ${TEST_GYP_BUILD:-0} == 'True' ]]; then 148 | make clean; 149 | make gyp; 150 | fi 151 | 152 | after_script: 153 | - if [[ ${COVERAGE:-0} == 'True' ]]; then 154 | make clean; 155 | make coverage; 156 | ./out/cov-test; 157 | cp unit*gc* test/; 158 | ./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp variant_cast.hpp; 159 | fi 160 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # Configuration for YouCompleteMe Vim plugin 4 | # 5 | # http://valloric.github.io/YouCompleteMe/ 6 | # 7 | #----------------------------------------------------------------------------- 8 | 9 | from os.path import realpath, dirname 10 | 11 | basedir = dirname(realpath(__file__)) 12 | 13 | # some default flags 14 | # for more information install clang-3.2-doc package and 15 | # check UsersManual.html 16 | flags = [ 17 | '-Werror', 18 | '-Wall', 19 | '-Wextra', 20 | '-pedantic', 21 | 22 | '-std=c++11', 23 | 24 | # '-x' and 'c++' also required 25 | # use 'c' for C projects 26 | '-x', 27 | 'c++', 28 | 29 | # include third party libraries 30 | '-I.', 31 | ] 32 | 33 | # youcompleteme is calling this function to get flags 34 | # You can also set database for flags. Check: JSONCompilationDatabase.html in 35 | # clang-3.2-doc package 36 | def FlagsForFile( filename ): 37 | return { 38 | 'flags': flags, 39 | 'do_cache': True 40 | } 41 | -------------------------------------------------------------------------------- /Jamroot: -------------------------------------------------------------------------------- 1 | # Unofficial and incomplete build file using Boost build system. 2 | # You should use make unless you know what you are doing. 3 | 4 | import os ; 5 | 6 | local boost_dir = [ os.environ BOOST_DIR ] ; 7 | if ! $(boost_dir) 8 | { 9 | boost_dir = "/usr/local" ; 10 | } 11 | 12 | #using clang : : ; 13 | 14 | local cxx_std = [ os.environ CXX_STD ] ; 15 | if ! $(cxx_std) 16 | { 17 | cxx_std = c++11 ; 18 | } 19 | 20 | project mapbox_variant 21 | : requirements 22 | -std=$(cxx_std) 23 | $(boost_dir)/include 24 | include 25 | test/include 26 | release:-march=native 27 | single:SINGLE_THREADED 28 | : default-build 29 | release 30 | speed 31 | single 32 | ; 33 | 34 | rule exe-test ( name : reqs * : deps * ) 35 | { 36 | exe $(name) 37 | : test/$(name).cpp 38 | : $(reqs) 39 | : $(deps) 40 | ; 41 | explicit $(name) ; 42 | } 43 | 44 | exe-test bench_variant 45 | : release:-Wweak-vtables 46 | ; 47 | 48 | exe-test binary_visitor_test ; 49 | exe-test recursive_wrapper_test ; 50 | exe-test unique_ptr_test ; 51 | exe-test reference_wrapper_test ; 52 | exe-test lambda_overload_test ; 53 | exe-test hashable_test ; 54 | 55 | install out 56 | : bench_variant 57 | binary_visitor_test 58 | unique_ptr_test 59 | reference_wrapper_test 60 | lambda_overload_test 61 | hashable_test 62 | ; 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) MapBox 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | - Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | - Neither the name "MapBox" nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MASON = .mason/mason 2 | BOOST_VERSION = 1.62.0 3 | 4 | CXX := $(CXX) 5 | CXX_STD ?= c++11 6 | 7 | BOOST_ROOT = $(shell $(MASON) prefix boost $(BOOST_VERSION)) 8 | BOOST_FLAGS = -isystem $(BOOST_ROOT)/include/ 9 | RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden -fvisibility=hidden 10 | DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions -fno-omit-frame-pointer -fPIE 11 | WARNING_FLAGS = -Werror -Wall -Wextra -pedantic \ 12 | -Wformat=2 -Wsign-conversion -Wshadow -Wunused-parameter 13 | 14 | COMMON_FLAGS = -std=$(CXX_STD) 15 | COMMON_FLAGS += $(WARNING_FLAGS) 16 | 17 | CXXFLAGS := $(CXXFLAGS) 18 | LDFLAGS := $(LDFLAGS) 19 | 20 | export BUILDTYPE ?= Release 21 | 22 | OS := $(shell uname -s) 23 | ifeq ($(OS), Linux) 24 | EXTRA_FLAGS = -pthread 25 | endif 26 | ifeq ($(OS), Darwin) 27 | OSX_OLDEST_SUPPORTED ?= 10.7 28 | # we need to explicitly ask for libc++ otherwise the 29 | # default will flip back to libstdc++ for mmacosx-version-min < 10.9 30 | EXTRA_FLAGS = -stdlib=libc++ -mmacosx-version-min=$(OSX_OLDEST_SUPPORTED) 31 | endif 32 | 33 | 34 | ifeq ($(BUILDTYPE),Release) 35 | FINAL_CXXFLAGS := $(COMMON_FLAGS) $(RELEASE_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) 36 | else 37 | FINAL_CXXFLAGS := $(COMMON_FLAGS) $(DEBUG_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) 38 | endif 39 | 40 | 41 | 42 | ALL_HEADERS = $(shell find include/mapbox/ '(' -name '*.hpp' ')') 43 | 44 | all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test out/lambda_overload_test out/hashable_test 45 | 46 | $(MASON): 47 | git submodule update --init .mason 48 | 49 | mason_packages/headers/boost: $(MASON) 50 | $(MASON) install boost $(BOOST_VERSION) 51 | 52 | ./deps/gyp: 53 | git clone --depth 1 https://chromium.googlesource.com/external/gyp.git ./deps/gyp 54 | 55 | gyp: ./deps/gyp 56 | deps/gyp/gyp --depth=. -Goutput_dir=./ --generator-output=./out -f make 57 | make V=1 -C ./out tests 58 | ./out/$(BUILDTYPE)/tests 59 | 60 | out/bench-variant-debug: Makefile mason_packages/headers/boost test/bench_variant.cpp 61 | mkdir -p ./out 62 | $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 63 | 64 | out/bench-variant: Makefile mason_packages/headers/boost test/bench_variant.cpp 65 | mkdir -p ./out 66 | $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 67 | 68 | out/unique_ptr_test: Makefile mason_packages/headers/boost test/unique_ptr_test.cpp 69 | mkdir -p ./out 70 | $(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 71 | 72 | out/recursive_wrapper_test: Makefile mason_packages/headers/boost test/recursive_wrapper_test.cpp 73 | mkdir -p ./out 74 | $(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 75 | 76 | out/binary_visitor_test: Makefile mason_packages/headers/boost test/binary_visitor_test.cpp 77 | mkdir -p ./out 78 | $(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 79 | 80 | out/lambda_overload_test: Makefile mason_packages/headers/boost test/lambda_overload_test.cpp 81 | mkdir -p ./out 82 | $(CXX) -o out/lambda_overload_test test/lambda_overload_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 83 | 84 | out/hashable_test: Makefile mason_packages/headers/boost test/hashable_test.cpp 85 | mkdir -p ./out 86 | $(CXX) -o out/hashable_test test/hashable_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) 87 | 88 | bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test 89 | ./out/bench-variant 100000 90 | ./out/unique_ptr_test 100000 91 | ./out/recursive_wrapper_test 100000 92 | ./out/binary_visitor_test 100000 93 | 94 | out/unit.o: Makefile test/unit.cpp 95 | mkdir -p ./out 96 | $(CXX) -c -o $@ test/unit.cpp -isystem test/include $(FINAL_CXXFLAGS) 97 | 98 | out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS) 99 | mkdir -p ./out 100 | $(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS) 101 | 102 | out/unit: out/unit.o \ 103 | out/binary_visitor_1.o \ 104 | out/binary_visitor_2.o \ 105 | out/binary_visitor_3.o \ 106 | out/binary_visitor_4.o \ 107 | out/binary_visitor_5.o \ 108 | out/binary_visitor_6.o \ 109 | out/issue21.o \ 110 | out/issue122.o \ 111 | out/mutating_visitor.o \ 112 | out/optional.o \ 113 | out/recursive_wrapper.o \ 114 | out/sizeof.o \ 115 | out/unary_visitor.o \ 116 | out/variant.o \ 117 | out/variant_alternative.o \ 118 | out/nothrow_move.o \ 119 | out/visitor_result_type.o \ 120 | 121 | mkdir -p ./out 122 | $(CXX) -o $@ $^ $(LDFLAGS) 123 | 124 | test: out/unit 125 | ./out/unit 126 | 127 | coverage: 128 | mkdir -p ./out 129 | $(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) 130 | 131 | sizes: Makefile 132 | mkdir -p ./out 133 | @$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world.out 134 | @$(CXX) -o ./out/boost_variant_hello_world.out $(BOOST_ROOT)/include/boost/variant.hpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world.out 135 | @$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world 136 | @$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world 137 | 138 | profile: out/bench-variant-debug 139 | mkdir -p profiling/ 140 | rm -rf profiling/* 141 | iprofiler -timeprofiler -d profiling/ ./out/bench-variant-debug 500000 142 | 143 | clean: 144 | rm -rf ./out 145 | rm -rf *.dSYM 146 | rm -f unit.gc* 147 | rm -f *gcov 148 | rm -f test/unit.gc* 149 | rm -f test/*gcov 150 | rm -f *.gcda *.gcno 151 | 152 | pgo: out Makefile 153 | $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -pg -fprofile-generate 154 | ./test-variant 500000 >/dev/null 2>/dev/null 155 | $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -fprofile-use 156 | 157 | .PHONY: sizes test 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mapbox Variant 2 | 3 | An header-only alternative to `boost::variant` for C++11 and C++14 4 | 5 | [![Build Status](https://secure.travis-ci.com/mapbox/variant.svg?branch=master)](https://travis-ci.com/mapbox/variant) 6 | [![Build status](https://ci.appveyor.com/api/projects/status/v9tatx21j1k0fcgy)](https://ci.appveyor.com/project/Mapbox/variant) 7 | [![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master&service=github)](https://coveralls.io/r/mapbox/variant?branch=master) 8 | 9 | ## Introduction 10 | 11 | Variant's basic building blocks are: 12 | 13 | - `variant` - a type-safe representation for sum-types / discriminated unions 14 | - `recursive_wrapper` - a helper type to represent recursive "tree-like" variants 15 | - `apply_visitor(visitor, myVariant)` - to invoke a custom visitor on the variant's underlying type 16 | - `get()` - a function to directly unwrap a variant's underlying type 17 | - `.match([](Type){})` - a variant convenience member function taking an arbitrary number of lambdas creating a visitor behind the scenes and applying it to the variant 18 | 19 | ### Basic Usage - HTTP API Example 20 | 21 | Suppose you want to represent a HTTP API response which is either a JSON result or an error: 22 | 23 | ```c++ 24 | struct Result { 25 | Json object; 26 | }; 27 | 28 | struct Error { 29 | int32_t code; 30 | string message; 31 | }; 32 | ``` 33 | 34 | You can represent this at type level using a variant which is either an `Error` or a `Result`: 35 | 36 | ```c++ 37 | using Response = variant; 38 | 39 | Response makeRequest() { 40 | return Error{501, "Not Implemented"}; 41 | } 42 | 43 | Response ret = makeRequest(); 44 | ``` 45 | 46 | To see which type the `Response` holds you pattern match on the variant unwrapping the underlying value: 47 | 48 | ```c++ 49 | ret.match([] (Result r) { print(r.object); }, 50 | [] (Error e) { print(e.message); }); 51 | ``` 52 | 53 | Instead of using the variant's convenience `.match` pattern matching function you can create a type visitor functor and use `apply_visitor` manually: 54 | 55 | ```c++ 56 | struct ResponseVisitor { 57 | void operator()(Result r) const { 58 | print(r.object); 59 | } 60 | 61 | void operator()(Error e) const { 62 | print(e.message); 63 | } 64 | }; 65 | 66 | ResponseVisitor visitor; 67 | apply_visitor(visitor, ret); 68 | ``` 69 | 70 | In both cases the compiler makes sure you handle all types the variant can represent at compile. 71 | 72 | ### Recursive Variants - JSON Example 73 | 74 | [JSON](http://www.json.org/) consists of types `String`, `Number`, `True`, `False`, `Null`, `Array` and `Object`. 75 | 76 | ```c++ 77 | struct String { string value; }; 78 | struct Number { double value; }; 79 | struct True { }; 80 | struct False { }; 81 | struct Null { }; 82 | struct Array { vector values; }; 83 | struct Object { unordered_map values; }; 84 | ``` 85 | 86 | This works for primitive types but how do we represent recursive types such as `Array` which can hold multiple elements and `Array` itself, too? 87 | 88 | For these use cases Variant provides a `recursive_wrapper` helper type which lets you express recursive Variants. 89 | 90 | ```c++ 91 | struct String { string value; }; 92 | struct Number { double value; }; 93 | struct True { }; 94 | struct False { }; 95 | struct Null { }; 96 | 97 | // Forward declarations only 98 | struct Array; 99 | struct Object; 100 | 101 | using Value = variant, recursive_wrapper>; 102 | 103 | struct Array { 104 | vector values; 105 | }; 106 | 107 | struct Object { 108 | unordered_map values; 109 | }; 110 | ``` 111 | 112 | For walking the JSON representation you can again either create a `JSONVisitor`: 113 | 114 | ```c++ 115 | struct JSONVisitor { 116 | 117 | void operator()(Null) const { 118 | print("null"); 119 | } 120 | 121 | // same for all other JSON types 122 | }; 123 | 124 | JSONVisitor visitor; 125 | apply_visitor(visitor, json); 126 | ``` 127 | 128 | Or use the convenience `.match` pattern matching function: 129 | 130 | ```c++ 131 | json.match([] (Null) { print("null"); }, 132 | ...); 133 | ``` 134 | 135 | To summarize: use `recursive_wrapper` to represent recursive "tree-like" representations: 136 | 137 | ```c++ 138 | struct Empty { }; 139 | struct Node; 140 | 141 | using Tree = variant>; 142 | 143 | struct Node { 144 | uint64_t value; 145 | } 146 | ``` 147 | 148 | ### Advanced Usage Tips 149 | 150 | Creating type aliases for variants is a great way to reduce repetition. 151 | Keep in mind those type aliases are not checked at type level, though. 152 | We recommend creating a new type for all but basic variant usage: 153 | 154 | ```c++ 155 | // the compiler can't tell the following two apart 156 | using APIResult = variant; 157 | using FilesystemResult = variant; 158 | 159 | // new type 160 | struct APIResult : variant { 161 | using Base = variant; 162 | using Base::Base; 163 | } 164 | ``` 165 | 166 | ## Why use Mapbox Variant? 167 | 168 | Mapbox variant has the same speedy performance of `boost::variant` but is 169 | faster to compile, results in smaller binaries, and has no dependencies. 170 | 171 | For example on OS X 10.9 with clang++ and libc++: 172 | 173 | Test | Mapbox Variant | Boost Variant 174 | ---- | -------------- | ------------- 175 | Size of pre-compiled header (release / debug) | 2.8/2.8 MB | 12/15 MB 176 | Size of simple program linking variant (release / debug) | 8/24 K | 12/40 K 177 | Time to compile header | 185 ms | 675 ms 178 | 179 | (Numbers from an older version of Mapbox variant.) 180 | 181 | ## Goals 182 | 183 | Mapbox `variant` has been a very valuable, lightweight alternative for apps 184 | that can use c++11 or c++14 but that do not want a boost dependency. 185 | Mapbox `variant` has also been useful in apps that do depend on boost, like 186 | mapnik, to help (slightly) with compile times and to majorly lessen dependence 187 | on boost in core headers. The original goal and near term goal is to maintain 188 | external API compatibility with `boost::variant` such that Mapbox `variant` 189 | can be a "drop in". At the same time the goal is to stay minimal: Only 190 | implement the features that are actually needed in existing software. So being 191 | an "incomplete" implementation is just fine. 192 | 193 | Currently Mapbox variant doesn't try to be API compatible with the upcoming 194 | variant standard, because the standard is not finished and it would be too much 195 | work. But we'll revisit this decision in the future if needed. 196 | 197 | If Mapbox variant is not for you, have a look at [these other 198 | implementations](doc/other_implementations.md). 199 | 200 | Want to know more about the upcoming standard? Have a look at our 201 | [overview](doc/standards_effort.md). 202 | 203 | Most modern high-level languages provide ways to express sum types directly. 204 | If you're curious have a look at Haskell's pattern matching or Rust's and Swift's enums. 205 | 206 | ## Depends 207 | 208 | - Compiler supporting `-std=c++11` or `-std=c++14` 209 | 210 | Tested with: 211 | 212 | - g++-4.7 213 | - g++-4.8 214 | - g++-4.9 215 | - g++-5.2 216 | - clang++-3.5 217 | - clang++-3.6 218 | - clang++-3.7 219 | - clang++-3.8 220 | - clang++-3.9 221 | - Visual Studio 2015 222 | 223 | ## Unit Tests 224 | 225 | On Unix systems compile and run the unit tests with `make test`. 226 | 227 | On Windows run `scripts/build-local.bat`. 228 | 229 | ## Limitations 230 | 231 | - The `variant` can not hold references (something like `variant` is 232 | not possible). You might want to try `std::reference_wrapper` instead. 233 | 234 | ## Deprecations 235 | 236 | - The included implementation of `optional` is deprecated and will be removed 237 | in a future version. See [issue #64](https://github.com/mapbox/variant/issues/64). 238 | - Old versions of the code needed visitors to derive from `static_visitor`. 239 | This is not needed any more and marked as deprecated. The `static_visitor` 240 | class will be removed in future versions. 241 | 242 | ## Benchmarks 243 | 244 | make bench 245 | 246 | ## Check object sizes 247 | 248 | make sizes /path/to/boost/variant.hpp 249 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | 2 | platform: 3 | - x64 4 | - x86 5 | 6 | configuration: 7 | - Debug 8 | - Release 9 | 10 | os: Visual Studio 2015 11 | 12 | install: 13 | - CALL scripts\build-appveyor.bat 14 | 15 | build: off 16 | test: off 17 | deploy: off 18 | -------------------------------------------------------------------------------- /common.gypi: -------------------------------------------------------------------------------- 1 | { 2 | "conditions": [ 3 | ["OS=='win'", { 4 | "target_defaults": { 5 | "default_configuration": "Release_x64", 6 | "msbuild_toolset":"v140", 7 | "msvs_settings": { 8 | "VCCLCompilerTool": { 9 | "ExceptionHandling": 1, # /EHsc 10 | "RuntimeTypeInfo": "true" # /GR 11 | } 12 | }, 13 | "configurations": { 14 | "Debug_Win32": { 15 | "msvs_configuration_platform": "Win32", 16 | "defines": [ "DEBUG","_DEBUG"], 17 | "msvs_settings": { 18 | "VCCLCompilerTool": { 19 | "RuntimeLibrary": "1", # static debug /MTd 20 | "Optimization": 0, # /Od, no optimization 21 | "MinimalRebuild": "false", 22 | "OmitFramePointers": "false", 23 | "BasicRuntimeChecks": 3 # /RTC1 24 | } 25 | } 26 | }, 27 | "Debug_x64": { 28 | "msvs_configuration_platform": "x64", 29 | "defines": [ "DEBUG","_DEBUG"], 30 | "msvs_settings": { 31 | "VCCLCompilerTool": { 32 | "RuntimeLibrary": "1", # static debug /MTd 33 | "Optimization": 0, # /Od, no optimization 34 | "MinimalRebuild": "false", 35 | "OmitFramePointers": "false", 36 | "BasicRuntimeChecks": 3 # /RTC1 37 | } 38 | } 39 | }, 40 | "Release_Win32": { 41 | "msvs_configuration_platform": "Win32", 42 | "defines": [ "NDEBUG"], 43 | "msvs_settings": { 44 | "VCCLCompilerTool": { 45 | "RuntimeLibrary": 0, # static release 46 | "Optimization": 3, # /Ox, full optimization 47 | "FavorSizeOrSpeed": 1, # /Ot, favour speed over size 48 | "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible 49 | "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG 50 | "OmitFramePointers": "true", 51 | "EnableFunctionLevelLinking": "true", 52 | "EnableIntrinsicFunctions": "true", 53 | "AdditionalOptions": [ 54 | "/MP", # compile across multiple CPUs 55 | ], 56 | "DebugInformationFormat": "0" 57 | }, 58 | "VCLibrarianTool": { 59 | "AdditionalOptions": [ 60 | "/LTCG" # link time code generation 61 | ], 62 | }, 63 | "VCLinkerTool": { 64 | "LinkTimeCodeGeneration": 1, # link-time code generation 65 | "OptimizeReferences": 2, # /OPT:REF 66 | "EnableCOMDATFolding": 2, # /OPT:ICF 67 | "LinkIncremental": 1, # disable incremental linking 68 | "GenerateDebugInformation": "false" 69 | } 70 | } 71 | }, 72 | "Release_x64": { 73 | "msvs_configuration_platform": "x64", 74 | "defines": [ "NDEBUG"], 75 | "msvs_settings": { 76 | "VCCLCompilerTool": { 77 | "RuntimeLibrary": 0, # static release 78 | "Optimization": 3, # /Ox, full optimization 79 | "FavorSizeOrSpeed": 1, # /Ot, favour speed over size 80 | "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible 81 | "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG 82 | "OmitFramePointers": "true", 83 | "EnableFunctionLevelLinking": "true", 84 | "EnableIntrinsicFunctions": "true", 85 | "AdditionalOptions": [ 86 | "/MP", # compile across multiple CPUs 87 | ], 88 | "DebugInformationFormat": "0" 89 | }, 90 | "VCLibrarianTool": { 91 | "AdditionalOptions": [ 92 | "/LTCG" # link time code generation 93 | ], 94 | }, 95 | "VCLinkerTool": { 96 | "LinkTimeCodeGeneration": 1, # link-time code generation 97 | "OptimizeReferences": 2, # /OPT:REF 98 | "EnableCOMDATFolding": 2, # /OPT:ICF 99 | "LinkIncremental": 1, # disable incremental linking 100 | "GenerateDebugInformation": "false" 101 | } 102 | } 103 | } 104 | } 105 | } 106 | }, { 107 | "target_defaults": { 108 | "default_configuration": "Release", 109 | "xcode_settings": { 110 | "CLANG_CXX_LIBRARY": "libc++", 111 | "CLANG_CXX_LANGUAGE_STANDARD":"c++11", 112 | "GCC_VERSION": "com.apple.compilers.llvm.clang.1_0", 113 | }, 114 | "cflags_cc": ["-std=c++11"], 115 | "configurations": { 116 | "Debug": { 117 | "defines": [ 118 | "DEBUG" 119 | ], 120 | "xcode_settings": { 121 | "GCC_OPTIMIZATION_LEVEL": "0", 122 | "GCC_GENERATE_DEBUGGING_SYMBOLS": "YES", 123 | "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-g", "-O0" ] 124 | } 125 | }, 126 | "Release": { 127 | "defines": [ 128 | "NDEBUG" 129 | ], 130 | "xcode_settings": { 131 | "GCC_OPTIMIZATION_LEVEL": "3", 132 | "GCC_GENERATE_DEBUGGING_SYMBOLS": "NO", 133 | "DEAD_CODE_STRIPPING": "YES", 134 | "GCC_INLINES_ARE_PRIVATE_EXTERN": "YES", 135 | "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-O3" ] 136 | } 137 | } 138 | } 139 | } 140 | }] 141 | ] 142 | } 143 | 144 | -------------------------------------------------------------------------------- /doc/other_implementations.md: -------------------------------------------------------------------------------- 1 | 2 | # Other implementations of variant and optional 3 | 4 | These are some other implementations of `variant` and/or `optional` types. 5 | They are not necessarily compatible with this implementation. This is an 6 | incomplete list. 7 | 8 | * [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) 9 | * [Eggs Variant](http://eggs-cpp.github.io/variant/) by [Agustín Bergé](http://talesofcpp.fusionfenix.com/) 10 | * [anthonyw/variant](https://bitbucket.org/anthonyw/variant) (implementation of P0110R0) 11 | * [JasonL9000/cppcon14](https://github.com/JasonL9000/cppcon14) 12 | * [tomilov/variant](https://github.com/tomilov/variant) 13 | * [akrzemi1/Optional](https://github.com/akrzemi1/Optional) 14 | * [mpark/variant](https://github.com/mpark/variant) 15 | 16 | -------------------------------------------------------------------------------- /doc/standards_effort.md: -------------------------------------------------------------------------------- 1 | 2 | # Standards efforts 3 | 4 | A `variant` type is on planned for inclusion in the C++ Standard, probably in 5 | C++17. Current working papers are (list extracted from [2015 working group 6 | papers](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/)): 7 | 8 | * 2015-09-28: Variant design review. [P0086R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0086r0.pdf) 9 | * 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) 10 | * 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) 11 | * 2015-09-24: Simply a Strong Variant [P0093R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0093r0.html) 12 | * 2015-09-24: Simply a Basic Variant [P0094R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0094r0.html) 13 | * 2015-09-24: The Case for a Language Based Variant [P0096R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0095r0.html) 14 | * 2015-09-25: Implementing the strong guarantee for variant<> assignment [P0110R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0110r0.html) 15 | * 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) 16 | 17 | Last state can be seen from 18 | [The Variant Saga: A happy ending?](https://isocpp.org/blog/2015/11/the-variant-saga-a-happy-ending). 19 | 20 | The `optional` type is also on the way into the standard. The papers are: 21 | * 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) 22 | * 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) 23 | 24 | ## Older Papers 25 | 26 | * 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). 27 | * 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. 28 | 29 | -------------------------------------------------------------------------------- /include/mapbox/optional.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPBOX_UTIL_OPTIONAL_HPP 2 | #define MAPBOX_UTIL_OPTIONAL_HPP 3 | 4 | #pragma message("This implementation of optional is deprecated. See https://github.com/mapbox/variant/issues/64.") 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace mapbox { 12 | namespace util { 13 | 14 | template 15 | class optional 16 | { 17 | static_assert(!std::is_reference::value, "optional doesn't support references"); 18 | 19 | struct none_type 20 | { 21 | }; 22 | 23 | variant variant_; 24 | 25 | public: 26 | optional() = default; 27 | 28 | optional(optional const& rhs) 29 | { 30 | if (this != &rhs) 31 | { // protect against invalid self-assignment 32 | variant_ = rhs.variant_; 33 | } 34 | } 35 | 36 | optional(T const& v) { variant_ = v; } 37 | 38 | explicit operator bool() const noexcept { return variant_.template is(); } 39 | 40 | T const& get() const { return variant_.template get(); } 41 | T& get() { return variant_.template get(); } 42 | 43 | T const& operator*() const { return this->get(); } 44 | T operator*() { return this->get(); } 45 | 46 | optional& operator=(T const& v) 47 | { 48 | variant_ = v; 49 | return *this; 50 | } 51 | 52 | optional& operator=(optional const& rhs) 53 | { 54 | if (this != &rhs) 55 | { 56 | variant_ = rhs.variant_; 57 | } 58 | return *this; 59 | } 60 | 61 | template 62 | void emplace(Args&&... args) 63 | { 64 | variant_ = T{std::forward(args)...}; 65 | } 66 | 67 | void reset() { variant_ = none_type{}; } 68 | 69 | }; // class optional 70 | 71 | } // namespace util 72 | } // namespace mapbox 73 | 74 | #endif // MAPBOX_UTIL_OPTIONAL_HPP 75 | -------------------------------------------------------------------------------- /include/mapbox/recursive_wrapper.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP 2 | #define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP 3 | 4 | // Based on variant/recursive_wrapper.hpp from boost. 5 | // 6 | // Original license: 7 | // 8 | // Copyright (c) 2002-2003 9 | // Eric Friedman, Itay Maman 10 | // 11 | // Distributed under the Boost Software License, Version 1.0. (See 12 | // accompanying file LICENSE_1_0.txt or copy at 13 | // http://www.boost.org/LICENSE_1_0.txt) 14 | 15 | #include 16 | #include 17 | 18 | namespace mapbox { 19 | namespace util { 20 | 21 | template 22 | class recursive_wrapper 23 | { 24 | 25 | T* p_; 26 | 27 | void assign(T const& rhs) 28 | { 29 | this->get() = rhs; 30 | } 31 | 32 | public: 33 | using type = T; 34 | 35 | /** 36 | * Default constructor default initializes the internally stored value. 37 | * For POD types this means nothing is done and the storage is 38 | * uninitialized. 39 | * 40 | * @throws std::bad_alloc if there is insufficient memory for an object 41 | * of type T. 42 | * @throws any exception thrown by the default constructur of T. 43 | */ 44 | recursive_wrapper() 45 | : p_(new T){} 46 | 47 | ~recursive_wrapper() noexcept { delete p_; } 48 | 49 | recursive_wrapper(recursive_wrapper const& operand) 50 | : p_(new T(operand.get())) {} 51 | 52 | recursive_wrapper(T const& operand) 53 | : p_(new T(operand)) {} 54 | 55 | recursive_wrapper(recursive_wrapper&& operand) 56 | : p_(new T(std::move(operand.get()))) {} 57 | 58 | recursive_wrapper(T&& operand) 59 | : p_(new T(std::move(operand))) {} 60 | 61 | inline recursive_wrapper& operator=(recursive_wrapper const& rhs) 62 | { 63 | assign(rhs.get()); 64 | return *this; 65 | } 66 | 67 | inline recursive_wrapper& operator=(T const& rhs) 68 | { 69 | assign(rhs); 70 | return *this; 71 | } 72 | 73 | inline void swap(recursive_wrapper& operand) noexcept 74 | { 75 | T* temp = operand.p_; 76 | operand.p_ = p_; 77 | p_ = temp; 78 | } 79 | 80 | recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept 81 | { 82 | swap(rhs); 83 | return *this; 84 | } 85 | 86 | recursive_wrapper& operator=(T&& rhs) 87 | { 88 | get() = std::move(rhs); 89 | return *this; 90 | } 91 | 92 | T& get() 93 | { 94 | assert(p_); 95 | return *get_pointer(); 96 | } 97 | 98 | T const& get() const 99 | { 100 | assert(p_); 101 | return *get_pointer(); 102 | } 103 | 104 | T* get_pointer() { return p_; } 105 | 106 | const T* get_pointer() const { return p_; } 107 | 108 | operator T const&() const { return this->get(); } 109 | 110 | operator T&() { return this->get(); } 111 | 112 | }; // class recursive_wrapper 113 | 114 | template 115 | inline void swap(recursive_wrapper& lhs, recursive_wrapper& rhs) noexcept 116 | { 117 | lhs.swap(rhs); 118 | } 119 | } // namespace util 120 | } // namespace mapbox 121 | 122 | #endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP 123 | -------------------------------------------------------------------------------- /include/mapbox/variant.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPBOX_UTIL_VARIANT_HPP 2 | #define MAPBOX_UTIL_VARIANT_HPP 3 | 4 | #include 5 | #include // size_t 6 | #include // operator new 7 | #include // runtime_error 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | // clang-format off 20 | // [[deprecated]] is only available in C++14, use this for the time being 21 | #if __cplusplus <= 201103L 22 | # ifdef __GNUC__ 23 | # define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated)) 24 | # elif defined(_MSC_VER) 25 | # define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated) 26 | # else 27 | # define MAPBOX_VARIANT_DEPRECATED 28 | # endif 29 | #else 30 | # define MAPBOX_VARIANT_DEPRECATED [[deprecated]] 31 | #endif 32 | 33 | 34 | #ifdef _MSC_VER 35 | // https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx 36 | # ifdef NDEBUG 37 | # define VARIANT_INLINE __forceinline 38 | # else 39 | # define VARIANT_INLINE //__declspec(noinline) 40 | # endif 41 | #else 42 | # ifdef NDEBUG 43 | # define VARIANT_INLINE //inline __attribute__((always_inline)) 44 | # else 45 | # define VARIANT_INLINE __attribute__((noinline)) 46 | # endif 47 | #endif 48 | // clang-format on 49 | 50 | // Exceptions 51 | #if defined( __EXCEPTIONS) || defined( _MSC_VER) 52 | #define HAS_EXCEPTIONS 53 | #endif 54 | 55 | #define VARIANT_MAJOR_VERSION 1 56 | #define VARIANT_MINOR_VERSION 1 57 | #define VARIANT_PATCH_VERSION 0 58 | 59 | #define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION) 60 | 61 | namespace mapbox { 62 | namespace util { 63 | 64 | // XXX This should derive from std::logic_error instead of std::runtime_error. 65 | // See https://github.com/mapbox/variant/issues/48 for details. 66 | class bad_variant_access : public std::runtime_error 67 | { 68 | 69 | public: 70 | explicit bad_variant_access(const std::string& what_arg) 71 | : runtime_error(what_arg) {} 72 | 73 | explicit bad_variant_access(const char* what_arg) 74 | : runtime_error(what_arg) {} 75 | 76 | }; // class bad_variant_access 77 | 78 | #if !defined(MAPBOX_VARIANT_MINIMIZE_SIZE) 79 | using type_index_t = unsigned int; 80 | #else 81 | #if defined(MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED) 82 | using type_index_t = std::uint_fast8_t; 83 | #else 84 | using type_index_t = std::uint_least8_t; 85 | #endif 86 | #endif 87 | 88 | namespace detail { 89 | 90 | static constexpr type_index_t invalid_value = type_index_t(-1); 91 | 92 | template 93 | struct direct_type; 94 | 95 | template 96 | struct direct_type 97 | { 98 | static constexpr type_index_t index = std::is_same::value 99 | ? sizeof...(Types) 100 | : direct_type::index; 101 | }; 102 | 103 | template 104 | struct direct_type 105 | { 106 | static constexpr type_index_t index = invalid_value; 107 | }; 108 | 109 | #if __cpp_lib_logical_traits >= 201510L 110 | 111 | using std::conjunction; 112 | using std::disjunction; 113 | 114 | #else 115 | 116 | template 117 | struct conjunction : std::true_type {}; 118 | 119 | template 120 | struct conjunction : B1 {}; 121 | 122 | template 123 | struct conjunction : std::conditional::type {}; 124 | 125 | template 126 | struct conjunction : std::conditional, B1>::type {}; 127 | 128 | template 129 | struct disjunction : std::false_type {}; 130 | 131 | template 132 | struct disjunction : B1 {}; 133 | 134 | template 135 | struct disjunction : std::conditional::type {}; 136 | 137 | template 138 | struct disjunction : std::conditional>::type {}; 139 | 140 | #endif 141 | 142 | template 143 | struct convertible_type; 144 | 145 | template 146 | struct convertible_type 147 | { 148 | static constexpr type_index_t index = std::is_convertible::value 149 | ? disjunction...>::value ? invalid_value : sizeof...(Types) 150 | : convertible_type::index; 151 | }; 152 | 153 | template 154 | struct convertible_type 155 | { 156 | static constexpr type_index_t index = invalid_value; 157 | }; 158 | 159 | template 160 | struct value_traits 161 | { 162 | using value_type = typename std::remove_const::type>::type; 163 | using value_type_wrapper = recursive_wrapper; 164 | static constexpr type_index_t direct_index = direct_type::index; 165 | static constexpr bool is_direct = direct_index != invalid_value; 166 | static constexpr type_index_t index_direct_or_wrapper = is_direct ? direct_index : direct_type::index; 167 | static constexpr bool is_direct_or_wrapper = index_direct_or_wrapper != invalid_value; 168 | static constexpr type_index_t index = is_direct_or_wrapper ? index_direct_or_wrapper : convertible_type::index; 169 | static constexpr bool is_valid = index != invalid_value; 170 | static constexpr type_index_t tindex = is_valid ? sizeof...(Types)-index : 0; 171 | using target_type = typename std::tuple_element>::type; 172 | }; 173 | 174 | template 175 | struct copy_cvref 176 | { 177 | using type = Dest; 178 | }; 179 | 180 | template 181 | struct copy_cvref 182 | { 183 | using type = Dest const&; 184 | }; 185 | 186 | template 187 | struct copy_cvref 188 | { 189 | using type = Dest&; 190 | }; 191 | 192 | template 193 | struct copy_cvref 194 | { 195 | using type = Dest&&; 196 | }; 197 | 198 | template 199 | struct deduced_result_type 200 | {}; 201 | 202 | template 203 | struct deduced_result_type()(std::declval()...))> 204 | { 205 | using type = decltype(std::declval()(std::declval()...)); 206 | }; 207 | 208 | template 209 | struct visitor_result_type : deduced_result_type 210 | {}; 211 | 212 | // specialization for explicit result_type member in visitor class 213 | template 214 | struct visitor_result_type::type::result_type>())> 215 | { 216 | using type = typename std::decay::type::result_type; 217 | }; 218 | 219 | template 220 | using result_of_unary_visit = typename visitor_result_type::type; 221 | 222 | template 223 | using result_of_binary_visit = typename visitor_result_type::type; 224 | 225 | template 226 | struct static_max; 227 | 228 | template 229 | struct static_max 230 | { 231 | static const type_index_t value = arg; 232 | }; 233 | 234 | template 235 | struct static_max 236 | { 237 | static const type_index_t value = arg1 >= arg2 ? static_max::value : static_max::value; 238 | }; 239 | 240 | template 241 | struct variant_helper; 242 | 243 | template 244 | struct variant_helper 245 | { 246 | VARIANT_INLINE static void destroy(const type_index_t type_index, void* data) 247 | { 248 | if (type_index == sizeof...(Types)) 249 | { 250 | reinterpret_cast(data)->~T(); 251 | } 252 | else 253 | { 254 | variant_helper::destroy(type_index, data); 255 | } 256 | } 257 | 258 | VARIANT_INLINE static void move(const type_index_t old_type_index, void* old_value, void* new_value) 259 | { 260 | if (old_type_index == sizeof...(Types)) 261 | { 262 | new (new_value) T(std::move(*reinterpret_cast(old_value))); 263 | } 264 | else 265 | { 266 | variant_helper::move(old_type_index, old_value, new_value); 267 | } 268 | } 269 | 270 | VARIANT_INLINE static void copy(const type_index_t old_type_index, const void* old_value, void* new_value) 271 | { 272 | if (old_type_index == sizeof...(Types)) 273 | { 274 | new (new_value) T(*reinterpret_cast(old_value)); 275 | } 276 | else 277 | { 278 | variant_helper::copy(old_type_index, old_value, new_value); 279 | } 280 | } 281 | }; 282 | 283 | template <> 284 | struct variant_helper<> 285 | { 286 | VARIANT_INLINE static void destroy(const type_index_t, void*) {} 287 | VARIANT_INLINE static void move(const type_index_t, void*, void*) {} 288 | VARIANT_INLINE static void copy(const type_index_t, const void*, void*) {} 289 | }; 290 | 291 | template 292 | struct unwrapper 293 | { 294 | using value_type = T; 295 | 296 | template 297 | static auto apply(typename std::remove_reference::type& var) 298 | -> typename std::enable_if::value, 299 | decltype(var.template get_unchecked())>::type 300 | { 301 | return var.template get_unchecked(); 302 | } 303 | 304 | template 305 | static auto apply(typename std::remove_reference::type& var) 306 | -> typename std::enable_if::value, 307 | decltype(std::move(var.template get_unchecked()))>::type 308 | { 309 | return std::move(var.template get_unchecked()); 310 | } 311 | }; 312 | 313 | template 314 | struct unwrapper> : unwrapper 315 | {}; 316 | 317 | template 318 | struct unwrapper> : unwrapper 319 | {}; 320 | 321 | template 322 | struct dispatcher; 323 | 324 | template 325 | struct dispatcher 326 | { 327 | template 328 | VARIANT_INLINE static R apply(V&& v, F&& f) 329 | { 330 | if (v.template is()) 331 | { 332 | return std::forward(f)(unwrapper::template apply(v)); 333 | } 334 | else 335 | { 336 | return dispatcher::apply(std::forward(v), std::forward(f)); 337 | } 338 | } 339 | }; 340 | 341 | template 342 | struct dispatcher 343 | { 344 | template 345 | VARIANT_INLINE static R apply(V&& v, F&& f) 346 | { 347 | return std::forward(f)(unwrapper::template apply(v)); 348 | } 349 | }; 350 | 351 | template 352 | struct binary_dispatcher_rhs; 353 | 354 | template 355 | struct binary_dispatcher_rhs 356 | { 357 | template 358 | VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) 359 | { 360 | if (rhs.template is()) // call binary functor 361 | { 362 | return std::forward(f)(unwrapper::template apply(lhs), 363 | unwrapper::template apply(rhs)); 364 | } 365 | else 366 | { 367 | return binary_dispatcher_rhs::apply(std::forward(lhs), 368 | std::forward(rhs), 369 | std::forward(f)); 370 | } 371 | } 372 | }; 373 | 374 | template 375 | struct binary_dispatcher_rhs 376 | { 377 | template 378 | VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) 379 | { 380 | return std::forward(f)(unwrapper::template apply(lhs), 381 | unwrapper::template apply(rhs)); 382 | } 383 | }; 384 | 385 | template 386 | struct binary_dispatcher_lhs; 387 | 388 | template 389 | struct binary_dispatcher_lhs 390 | { 391 | template 392 | VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) 393 | { 394 | if (lhs.template is()) // call binary functor 395 | { 396 | return std::forward(f)(unwrapper::template apply(lhs), 397 | unwrapper::template apply(rhs)); 398 | } 399 | else 400 | { 401 | return binary_dispatcher_lhs::apply(std::forward(lhs), 402 | std::forward(rhs), 403 | std::forward(f)); 404 | } 405 | } 406 | }; 407 | 408 | template 409 | struct binary_dispatcher_lhs 410 | { 411 | template 412 | VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) 413 | { 414 | return std::forward(f)(unwrapper::template apply(lhs), 415 | unwrapper::template apply(rhs)); 416 | } 417 | }; 418 | 419 | template 420 | struct binary_dispatcher; 421 | 422 | template 423 | struct binary_dispatcher 424 | { 425 | template 426 | VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) 427 | { 428 | if (v0.template is()) 429 | { 430 | if (v1.template is()) 431 | { 432 | return std::forward(f)(unwrapper::template apply(v0), 433 | unwrapper::template apply(v1)); // call binary functor 434 | } 435 | else 436 | { 437 | return binary_dispatcher_rhs::apply(std::forward(v0), 438 | std::forward(v1), 439 | std::forward(f)); 440 | } 441 | } 442 | else if (v1.template is()) 443 | { 444 | return binary_dispatcher_lhs::apply(std::forward(v0), 445 | std::forward(v1), 446 | std::forward(f)); 447 | } 448 | return binary_dispatcher::apply(std::forward(v0), 449 | std::forward(v1), 450 | std::forward(f)); 451 | } 452 | }; 453 | 454 | template 455 | struct binary_dispatcher 456 | { 457 | template 458 | VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) 459 | { 460 | return std::forward(f)(unwrapper::template apply(v0), 461 | unwrapper::template apply(v1)); // call binary functor 462 | } 463 | }; 464 | 465 | // comparator functors 466 | struct equal_comp 467 | { 468 | template 469 | bool operator()(T const& lhs, T const& rhs) const 470 | { 471 | return lhs == rhs; 472 | } 473 | }; 474 | 475 | struct less_comp 476 | { 477 | template 478 | bool operator()(T const& lhs, T const& rhs) const 479 | { 480 | return lhs < rhs; 481 | } 482 | }; 483 | 484 | template 485 | class comparer 486 | { 487 | public: 488 | explicit comparer(Variant const& lhs) noexcept 489 | : lhs_(lhs) {} 490 | comparer& operator=(comparer const&) = delete; 491 | // visitor 492 | template 493 | bool operator()(T const& rhs_content) const 494 | { 495 | T const& lhs_content = lhs_.template get_unchecked(); 496 | return Comp()(lhs_content, rhs_content); 497 | } 498 | 499 | private: 500 | Variant const& lhs_; 501 | }; 502 | 503 | // hashing visitor 504 | struct hasher 505 | { 506 | template 507 | std::size_t operator()(const T& hashable) const 508 | { 509 | return std::hash{}(hashable); 510 | } 511 | }; 512 | 513 | } // namespace detail 514 | 515 | struct no_init {}; 516 | 517 | template 518 | class variant 519 | { 520 | static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty."); 521 | static_assert(!detail::disjunction...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?"); 522 | static_assert(!detail::disjunction...>::value, "Variant can not hold array types."); 523 | static_assert(sizeof...(Types) < std::numeric_limits::max(), "Internal index type must be able to accommodate all alternatives."); 524 | private: 525 | static const std::size_t data_size = detail::static_max::value; 526 | static const std::size_t data_align = detail::static_max::value; 527 | public: 528 | struct adapted_variant_tag; 529 | using types = std::tuple; 530 | private: 531 | using first_type = typename std::tuple_element<0, types>::type; 532 | using unwrap_first_type = typename detail::unwrapper::value_type; 533 | using data_type = typename std::aligned_storage::type; 534 | using helper_type = detail::variant_helper; 535 | 536 | template 537 | using alternative_ref = typename detail::copy_cvref::type; 538 | 539 | type_index_t type_index; 540 | #ifdef __clang_analyzer__ 541 | data_type data {}; 542 | #else 543 | data_type data; 544 | #endif 545 | 546 | public: 547 | VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible::value) 548 | : type_index(sizeof...(Types)-1) 549 | { 550 | static_assert(std::is_default_constructible::value, "First type in variant must be default constructible to allow default construction of variant."); 551 | new (&data) first_type(); 552 | } 553 | 554 | VARIANT_INLINE variant(no_init) noexcept 555 | : type_index(detail::invalid_value) {} 556 | 557 | // http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers 558 | template , 559 | typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > 560 | VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible::value) 561 | : type_index(Traits::index) 562 | { 563 | new (&data) typename Traits::target_type(std::forward(val)); 564 | } 565 | 566 | VARIANT_INLINE variant(variant const& old) 567 | : type_index(old.type_index) 568 | { 569 | helper_type::copy(old.type_index, &old.data, &data); 570 | } 571 | 572 | VARIANT_INLINE variant(variant&& old) 573 | noexcept(detail::conjunction...>::value) 574 | : type_index(old.type_index) 575 | { 576 | helper_type::move(old.type_index, &old.data, &data); 577 | } 578 | 579 | private: 580 | VARIANT_INLINE void copy_assign(variant const& rhs) 581 | { 582 | helper_type::destroy(type_index, &data); 583 | type_index = detail::invalid_value; 584 | helper_type::copy(rhs.type_index, &rhs.data, &data); 585 | type_index = rhs.type_index; 586 | } 587 | 588 | VARIANT_INLINE void move_assign(variant&& rhs) 589 | { 590 | helper_type::destroy(type_index, &data); 591 | type_index = detail::invalid_value; 592 | helper_type::move(rhs.type_index, &rhs.data, &data); 593 | type_index = rhs.type_index; 594 | } 595 | 596 | public: 597 | VARIANT_INLINE variant& operator=(variant&& other) 598 | // note we check for nothrow-constructible, not nothrow-assignable, since 599 | // move_assign uses move-construction via placement new. 600 | noexcept(detail::conjunction...>::value) 601 | { 602 | if (this == &other) { // playing safe in release mode, hit assertion in debug. 603 | assert(false); 604 | return *this; 605 | } 606 | move_assign(std::move(other)); 607 | return *this; 608 | } 609 | 610 | VARIANT_INLINE variant& operator=(variant const& other) 611 | { 612 | if (this != &other) 613 | copy_assign(other); 614 | return *this; 615 | } 616 | 617 | // conversions 618 | // move-assign 619 | template , 620 | typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > 621 | VARIANT_INLINE variant& operator=(T&& rhs) 622 | // not that we check is_nothrow_constructible, not is_nothrow_move_assignable, 623 | // since we construct a temporary 624 | noexcept(std::is_nothrow_constructible::value 625 | && std::is_nothrow_move_assignable>::value) 626 | { 627 | variant temp(std::forward(rhs)); 628 | move_assign(std::move(temp)); 629 | return *this; 630 | } 631 | 632 | // copy-assign 633 | template 634 | VARIANT_INLINE variant& operator=(T const& rhs) 635 | { 636 | variant temp(rhs); 637 | copy_assign(temp); 638 | return *this; 639 | } 640 | 641 | template ::index != detail::invalid_value)>::type* = nullptr> 643 | VARIANT_INLINE bool is() const 644 | { 645 | return type_index == detail::direct_type::index; 646 | } 647 | 648 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 650 | VARIANT_INLINE bool is() const 651 | { 652 | return type_index == detail::direct_type, Types...>::index; 653 | } 654 | 655 | VARIANT_INLINE bool valid() const 656 | { 657 | return type_index != detail::invalid_value; 658 | } 659 | 660 | template 661 | VARIANT_INLINE void set(Args&&... args) 662 | { 663 | helper_type::destroy(type_index, &data); 664 | type_index = detail::invalid_value; 665 | new (&data) T(std::forward(args)...); 666 | type_index = detail::direct_type::index; 667 | } 668 | 669 | // get_unchecked() 670 | template ::index != detail::invalid_value)>::type* = nullptr> 672 | VARIANT_INLINE T& get_unchecked() 673 | { 674 | return *reinterpret_cast(&data); 675 | } 676 | 677 | #ifdef HAS_EXCEPTIONS 678 | // get() 679 | template ::index != detail::invalid_value)>::type* = nullptr> 681 | VARIANT_INLINE T& get() 682 | { 683 | if (type_index == detail::direct_type::index) 684 | { 685 | return *reinterpret_cast(&data); 686 | } 687 | else 688 | { 689 | throw bad_variant_access("in get()"); 690 | } 691 | } 692 | #endif 693 | 694 | template ::index != detail::invalid_value)>::type* = nullptr> 696 | VARIANT_INLINE T const& get_unchecked() const 697 | { 698 | return *reinterpret_cast(&data); 699 | } 700 | 701 | #ifdef HAS_EXCEPTIONS 702 | template ::index != detail::invalid_value)>::type* = nullptr> 704 | VARIANT_INLINE T const& get() const 705 | { 706 | if (type_index == detail::direct_type::index) 707 | { 708 | return *reinterpret_cast(&data); 709 | } 710 | else 711 | { 712 | throw bad_variant_access("in get()"); 713 | } 714 | } 715 | #endif 716 | 717 | // get_unchecked() - T stored as recursive_wrapper 718 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 720 | VARIANT_INLINE T& get_unchecked() 721 | { 722 | return (*reinterpret_cast*>(&data)).get(); 723 | } 724 | 725 | #ifdef HAS_EXCEPTIONS 726 | // get() - T stored as recursive_wrapper 727 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 729 | VARIANT_INLINE T& get() 730 | { 731 | if (type_index == detail::direct_type, Types...>::index) 732 | { 733 | return (*reinterpret_cast*>(&data)).get(); 734 | } 735 | else 736 | { 737 | throw bad_variant_access("in get()"); 738 | } 739 | } 740 | #endif 741 | 742 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 744 | VARIANT_INLINE T const& get_unchecked() const 745 | { 746 | return (*reinterpret_cast const*>(&data)).get(); 747 | } 748 | 749 | #ifdef HAS_EXCEPTIONS 750 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 752 | VARIANT_INLINE T const& get() const 753 | { 754 | if (type_index == detail::direct_type, Types...>::index) 755 | { 756 | return (*reinterpret_cast const*>(&data)).get(); 757 | } 758 | else 759 | { 760 | throw bad_variant_access("in get()"); 761 | } 762 | } 763 | #endif 764 | 765 | // get_unchecked() - T stored as std::reference_wrapper 766 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 768 | VARIANT_INLINE T& get_unchecked() 769 | { 770 | return (*reinterpret_cast*>(&data)).get(); 771 | } 772 | 773 | #ifdef HAS_EXCEPTIONS 774 | // get() - T stored as std::reference_wrapper 775 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 777 | VARIANT_INLINE T& get() 778 | { 779 | if (type_index == detail::direct_type, Types...>::index) 780 | { 781 | return (*reinterpret_cast*>(&data)).get(); 782 | } 783 | else 784 | { 785 | throw bad_variant_access("in get()"); 786 | } 787 | } 788 | #endif 789 | 790 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 792 | VARIANT_INLINE T const& get_unchecked() const 793 | { 794 | return (*reinterpret_cast const*>(&data)).get(); 795 | } 796 | 797 | #ifdef HAS_EXCEPTIONS 798 | template , Types...>::index != detail::invalid_value)>::type* = nullptr> 800 | VARIANT_INLINE T const& get() const 801 | { 802 | if (type_index == detail::direct_type, Types...>::index) 803 | { 804 | return (*reinterpret_cast const*>(&data)).get(); 805 | } 806 | else 807 | { 808 | throw bad_variant_access("in get()"); 809 | } 810 | } 811 | #endif 812 | 813 | // This function is deprecated because it returns an internal index field. 814 | // Use which() instead. 815 | MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE type_index_t get_type_index() const 816 | { 817 | return type_index; 818 | } 819 | 820 | VARIANT_INLINE int which() const noexcept 821 | { 822 | return static_cast(sizeof...(Types) - type_index - 1); 823 | } 824 | 825 | template ::index != detail::invalid_value)>::type* = nullptr> 827 | VARIANT_INLINE static constexpr int which() noexcept 828 | { 829 | return static_cast(sizeof...(Types)-detail::direct_type::index - 1); 830 | } 831 | 832 | // visitor 833 | // unary 834 | template , 835 | typename R = detail::result_of_unary_visit> 836 | VARIANT_INLINE static R visit(V&& v, F&& f) 837 | { 838 | return detail::dispatcher::apply(std::forward(v), std::forward(f)); 839 | } 840 | 841 | // binary 842 | template , 843 | typename R = detail::result_of_binary_visit> 844 | VARIANT_INLINE static R binary_visit(V&& v0, V&& v1, F&& f) 845 | { 846 | return detail::binary_dispatcher::apply(std::forward(v0), 847 | std::forward(v1), 848 | std::forward(f)); 849 | } 850 | 851 | // match 852 | // unary 853 | template 854 | auto VARIANT_INLINE match(Fs&&... fs) const& 855 | -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) 856 | { 857 | return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); 858 | } 859 | // non-const 860 | template 861 | auto VARIANT_INLINE match(Fs&&... fs) & 862 | -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) 863 | { 864 | return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); 865 | } 866 | template 867 | auto VARIANT_INLINE match(Fs&&... fs) && 868 | -> decltype(variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...))) 869 | { 870 | return variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...)); 871 | } 872 | 873 | ~variant() noexcept // no-throw destructor 874 | { 875 | helper_type::destroy(type_index, &data); 876 | } 877 | 878 | // comparison operators 879 | // equality 880 | VARIANT_INLINE bool operator==(variant const& rhs) const 881 | { 882 | assert(valid() && rhs.valid()); 883 | if (this->which() != rhs.which()) 884 | { 885 | return false; 886 | } 887 | detail::comparer visitor(*this); 888 | return visit(rhs, visitor); 889 | } 890 | 891 | VARIANT_INLINE bool operator!=(variant const& rhs) const 892 | { 893 | return !(*this == rhs); 894 | } 895 | 896 | // less than 897 | VARIANT_INLINE bool operator<(variant const& rhs) const 898 | { 899 | assert(valid() && rhs.valid()); 900 | if (this->which() != rhs.which()) 901 | { 902 | return this->which() < rhs.which(); 903 | } 904 | detail::comparer visitor(*this); 905 | return visit(rhs, visitor); 906 | } 907 | VARIANT_INLINE bool operator>(variant const& rhs) const 908 | { 909 | return rhs < *this; 910 | } 911 | VARIANT_INLINE bool operator<=(variant const& rhs) const 912 | { 913 | return !(*this > rhs); 914 | } 915 | VARIANT_INLINE bool operator>=(variant const& rhs) const 916 | { 917 | return !(*this < rhs); 918 | } 919 | }; 920 | 921 | // unary visitor interface 922 | template 923 | auto VARIANT_INLINE apply_visitor(F&& f, V&& v) 924 | -> decltype(v.visit(std::forward(v), std::forward(f))) 925 | { 926 | return v.visit(std::forward(v), std::forward(f)); 927 | } 928 | 929 | // binary visitor interface 930 | template 931 | auto VARIANT_INLINE apply_visitor(F&& f, V&& v0, V&& v1) 932 | -> decltype(v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f))) 933 | { 934 | return v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f)); 935 | } 936 | 937 | // getter interface 938 | 939 | #ifdef HAS_EXCEPTIONS 940 | template 941 | auto get(T& var)->decltype(var.template get()) 942 | { 943 | return var.template get(); 944 | } 945 | #endif 946 | 947 | template 948 | ResultType& get_unchecked(T& var) 949 | { 950 | return var.template get_unchecked(); 951 | } 952 | 953 | #ifdef HAS_EXCEPTIONS 954 | template 955 | auto get(T const& var)->decltype(var.template get()) 956 | { 957 | return var.template get(); 958 | } 959 | #endif 960 | 961 | template 962 | ResultType const& get_unchecked(T const& var) 963 | { 964 | return var.template get_unchecked(); 965 | } 966 | // variant_size 967 | template 968 | struct variant_size; 969 | 970 | //variable templates is c++14 971 | //template 972 | //constexpr std::size_t variant_size_v = variant_size::value; 973 | 974 | template 975 | struct variant_size 976 | : variant_size {}; 977 | 978 | template 979 | struct variant_size 980 | : variant_size {}; 981 | 982 | template 983 | struct variant_size 984 | : variant_size {}; 985 | 986 | template 987 | struct variant_size> 988 | : std::integral_constant {}; 989 | 990 | // variant_alternative 991 | template 992 | struct variant_alternative; 993 | 994 | #if defined(__clang__) 995 | #if __has_builtin(__type_pack_element) 996 | #define has_type_pack_element 997 | #endif 998 | #endif 999 | 1000 | #if defined(has_type_pack_element) 1001 | template 1002 | struct variant_alternative> 1003 | { 1004 | static_assert(sizeof...(Types) > Index , "Index out of range"); 1005 | using type = __type_pack_element; 1006 | }; 1007 | #else 1008 | template 1009 | struct variant_alternative> 1010 | : variant_alternative> 1011 | { 1012 | static_assert(sizeof...(Types) > Index -1 , "Index out of range"); 1013 | }; 1014 | 1015 | template 1016 | struct variant_alternative<0, variant> 1017 | { 1018 | using type = First; 1019 | }; 1020 | 1021 | #endif 1022 | 1023 | template 1024 | using variant_alternative_t = typename variant_alternative::type; 1025 | 1026 | template 1027 | struct variant_alternative 1028 | : std::add_const> {}; 1029 | 1030 | template 1031 | struct variant_alternative 1032 | : std::add_volatile> {}; 1033 | 1034 | template 1035 | struct variant_alternative 1036 | : std::add_cv> {}; 1037 | 1038 | } // namespace util 1039 | } // namespace mapbox 1040 | 1041 | // hashable iff underlying types are hashable 1042 | namespace std { 1043 | template 1044 | struct hash< ::mapbox::util::variant> { 1045 | std::size_t operator()(const ::mapbox::util::variant& v) const noexcept 1046 | { 1047 | return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v); 1048 | } 1049 | }; 1050 | 1051 | } 1052 | 1053 | #endif // MAPBOX_UTIL_VARIANT_HPP 1054 | -------------------------------------------------------------------------------- /include/mapbox/variant_cast.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VARIANT_CAST_HPP 2 | #define VARIANT_CAST_HPP 3 | 4 | #include 5 | 6 | namespace mapbox { 7 | namespace util { 8 | 9 | namespace detail { 10 | 11 | template 12 | class static_caster 13 | { 14 | public: 15 | template 16 | T& operator()(V& v) const 17 | { 18 | return static_cast(v); 19 | } 20 | }; 21 | 22 | template 23 | class dynamic_caster 24 | { 25 | public: 26 | using result_type = T&; 27 | template 28 | T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const 29 | { 30 | throw std::bad_cast(); 31 | } 32 | template 33 | T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const 34 | { 35 | return dynamic_cast(v); 36 | } 37 | }; 38 | 39 | template 40 | class dynamic_caster 41 | { 42 | public: 43 | using result_type = T*; 44 | template 45 | T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const 46 | { 47 | return nullptr; 48 | } 49 | template 50 | T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const 51 | { 52 | return dynamic_cast(&v); 53 | } 54 | }; 55 | } 56 | 57 | template 58 | typename detail::dynamic_caster::result_type 59 | dynamic_variant_cast(V& v) 60 | { 61 | return mapbox::util::apply_visitor(detail::dynamic_caster(), v); 62 | } 63 | 64 | template 65 | typename detail::dynamic_caster::result_type 66 | dynamic_variant_cast(const V& v) 67 | { 68 | return mapbox::util::apply_visitor(detail::dynamic_caster(), v); 69 | } 70 | 71 | template 72 | T& static_variant_cast(V& v) 73 | { 74 | return mapbox::util::apply_visitor(detail::static_caster(), v); 75 | } 76 | 77 | template 78 | const T& static_variant_cast(const V& v) 79 | { 80 | return mapbox::util::apply_visitor(detail::static_caster(), v); 81 | } 82 | } 83 | } 84 | 85 | #endif // VARIANT_CAST_HPP 86 | -------------------------------------------------------------------------------- /include/mapbox/variant_io.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPBOX_UTIL_VARIANT_IO_HPP 2 | #define MAPBOX_UTIL_VARIANT_IO_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace mapbox { 9 | namespace util { 10 | 11 | namespace detail { 12 | // operator<< helper 13 | template 14 | class printer 15 | { 16 | public: 17 | explicit printer(Out& out) 18 | : out_(out) {} 19 | printer& operator=(printer const&) = delete; 20 | 21 | // visitor 22 | template 23 | void operator()(T const& operand) const 24 | { 25 | out_ << operand; 26 | } 27 | 28 | private: 29 | Out& out_; 30 | }; 31 | } 32 | 33 | // operator<< 34 | template 35 | VARIANT_INLINE std::basic_ostream& 36 | operator<<(std::basic_ostream& out, variant const& rhs) 37 | { 38 | detail::printer> visitor(out); 39 | apply_visitor(visitor, rhs); 40 | return out; 41 | } 42 | } // namespace util 43 | } // namespace mapbox 44 | 45 | #endif // MAPBOX_UTIL_VARIANT_IO_HPP 46 | -------------------------------------------------------------------------------- /include/mapbox/variant_visitor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP 2 | #define MAPBOX_UTIL_VARIANT_VISITOR_HPP 3 | 4 | #include 5 | 6 | namespace mapbox { 7 | namespace util { 8 | 9 | template 10 | struct visitor; 11 | 12 | template 13 | struct visitor : Fn 14 | { 15 | using Fn::operator(); 16 | 17 | template 18 | visitor(T&& fn) : Fn(std::forward(fn)) {} 19 | }; 20 | 21 | template 22 | struct visitor : Fn, visitor 23 | { 24 | using Fn::operator(); 25 | using visitor::operator(); 26 | 27 | template 28 | visitor(T&& fn, Ts&&... fns) 29 | : Fn(std::forward(fn)) 30 | , visitor(std::forward(fns)...) {} 31 | }; 32 | 33 | template 34 | visitor::type...> make_visitor(Fns&&... fns) 35 | { 36 | return visitor::type...> 37 | (std::forward(fns)...); 38 | } 39 | 40 | } // namespace util 41 | } // namespace mapbox 42 | 43 | #endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "variant", 3 | "version": "1.1.6", 4 | "description": "C++11/C++14 variant", 5 | "main": "./package.json", 6 | "repository" : { 7 | "type" : "git", 8 | "url" : "git://github.com/mapbox/variant.git" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/build-appveyor.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL 3 | 4 | SET PATH=c:\python27;%PATH% 5 | 6 | ECHO activating VS command prompt 7 | IF /I "%PLATFORM"=="x64" ( 8 | CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 9 | ) ELSE ( 10 | CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 11 | ) 12 | 13 | IF NOT EXIST deps\gyp git clone --quiet --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp 14 | 15 | CALL deps\gyp\gyp.bat variant.gyp --depth=. ^ 16 | -f msvs ^ 17 | -G msvs_version=2015 ^ 18 | --generator-output=build 19 | 20 | SET MSBUILD_PLATFORM=%platform% 21 | IF /I "%MSBUILD_PLATFORM%" == "x86" SET MSBUILD_PLATFORM=Win32 22 | 23 | 24 | msbuild ^ 25 | build\variant.sln ^ 26 | /nologo ^ 27 | /toolsversion:14.0 ^ 28 | /p:PlatformToolset=v140 ^ 29 | /p:Configuration=%configuration% ^ 30 | /p:Platform=%MSBUILD_PLATFORM% 31 | 32 | build\"%configuration%"\tests.exe 33 | -------------------------------------------------------------------------------- /scripts/build-local.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL 3 | 4 | SET platform=x64 5 | SET configuration=Release 6 | 7 | CALL scripts\build-appveyor.bat -------------------------------------------------------------------------------- /scripts/run_compilation_failure_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Try to compile all programs in the test/compilation_failure directory. 4 | # Compilation must fail and the error message must match the pattern in the 5 | # corresponding .pattern file. 6 | # 7 | 8 | DIR="test/compilation_failure" 9 | CXX=${CXX:-clang++} 10 | 11 | if [ `uname -s` = "Darwin" ]; then 12 | CXXFLAGS="$CXXFLAGS -stdlib=libc++" 13 | fi 14 | 15 | error_msg() { 16 | if [ ! -z "$1" ]; then 17 | printf 'output was:\n=======\n%s\n=======\n' "$1" 18 | fi 19 | } 20 | 21 | exit_code=0 22 | for test_code in $DIR/*.cpp; do 23 | name=`basename $test_code .cpp` 24 | 25 | result=`${CXX} -std=c++11 -c -o /dev/null -I./include ${CXXFLAGS} ${test_code} 2>&1` 26 | status=$? 27 | 28 | if [ $status = 1 ]; then 29 | expected=`sed -n -e '/@EXPECTED/s/.*: \+//p' ${test_code}` 30 | if echo $result | grep -q "$expected"; then 31 | echo "$name [OK]" 32 | else 33 | echo "$name [FAILED - wrong error message]" 34 | echo "Expected error message: $expected" 35 | error_msg "$result" 36 | exit_code=1 37 | fi 38 | elif [ $status = 0 ]; then 39 | echo "$name [FAILED - compile was successful]" 40 | error_msg "$result" 41 | exit_code=1 42 | else 43 | echo "$name [FAILED - unknown error in compile]" 44 | error_msg "$result" 45 | exit_code=1 46 | fi 47 | done 48 | 49 | exit ${exit_code} 50 | -------------------------------------------------------------------------------- /test/bench_variant.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "auto_cpu_timer.hpp" 11 | 12 | #include 13 | #include 14 | 15 | #define TEXT_SHORT "Test" 16 | #define TEXT_LONG "Testing various variant implementations with a longish string ........................................." 17 | #define NUM_SAMPLES 3 18 | //#define BOOST_VARIANT_MINIMIZE_SIZE 19 | 20 | using namespace mapbox; 21 | 22 | namespace test { 23 | 24 | template 25 | struct Holder 26 | { 27 | typedef V value_type; 28 | std::vector data; 29 | 30 | template 31 | void append_move(T&& obj) 32 | { 33 | data.emplace_back(std::forward(obj)); 34 | } 35 | 36 | template 37 | void append(T const& obj) 38 | { 39 | data.push_back(obj); 40 | } 41 | }; 42 | 43 | } // namespace test 44 | 45 | struct print 46 | { 47 | template 48 | void operator()(T const& val) const 49 | { 50 | std::cerr << val << ":" << typeid(T).name() << std::endl; 51 | } 52 | }; 53 | 54 | template 55 | struct dummy : boost::static_visitor<> 56 | { 57 | dummy(V& v) 58 | : v_(v) {} 59 | 60 | template 61 | void operator()(T&& val) const 62 | { 63 | v_ = std::move(val); 64 | } 65 | V& v_; 66 | }; 67 | 68 | template 69 | struct dummy2 70 | { 71 | dummy2(V& v) 72 | : v_(v) {} 73 | 74 | template 75 | void operator()(T&& val) const 76 | { 77 | v_ = std::move(val); 78 | } 79 | V& v_; 80 | }; 81 | 82 | void run_boost_test(std::size_t runs) 83 | { 84 | test::Holder> h; 85 | h.data.reserve(runs); 86 | for (std::size_t i = 0; i < runs; ++i) 87 | { 88 | h.append_move(std::string(TEXT_SHORT)); 89 | h.append_move(std::string(TEXT_LONG)); 90 | h.append_move(123); 91 | h.append_move(3.14159); 92 | } 93 | 94 | boost::variant v; 95 | for (auto const& v2 : h.data) 96 | { 97 | dummy> d(v); 98 | boost::apply_visitor(d, v2); 99 | } 100 | } 101 | 102 | void run_variant_test(std::size_t runs) 103 | { 104 | test::Holder> h; 105 | h.data.reserve(runs); 106 | for (std::size_t i = 0; i < runs; ++i) 107 | { 108 | h.append_move(std::string(TEXT_SHORT)); 109 | h.append_move(std::string(TEXT_LONG)); 110 | h.append_move(123); 111 | h.append_move(3.14159); 112 | } 113 | 114 | util::variant v; 115 | for (auto const& v2 : h.data) 116 | { 117 | dummy2> d(v); 118 | util::apply_visitor(d, v2); 119 | } 120 | } 121 | 122 | int main(int argc, char** argv) 123 | { 124 | if (argc != 2) 125 | { 126 | std::cerr << "Usage:" << argv[0] << " " << std::endl; 127 | return 1; 128 | } 129 | 130 | #ifndef SINGLE_THREADED 131 | const std::size_t THREADS = 4; 132 | #endif 133 | const std::size_t NUM_RUNS = static_cast(std::stol(argv[1])); 134 | 135 | #ifdef SINGLE_THREADED 136 | 137 | for (std::size_t j = 0; j < NUM_SAMPLES; ++j) 138 | { 139 | 140 | { 141 | std::cerr << "custom variant: "; 142 | auto_cpu_timer t; 143 | run_variant_test(NUM_RUNS); 144 | } 145 | { 146 | std::cerr << "boost variant: "; 147 | auto_cpu_timer t; 148 | run_boost_test(NUM_RUNS); 149 | } 150 | } 151 | 152 | #else 153 | for (std::size_t j = 0; j < NUM_SAMPLES; ++j) 154 | { 155 | { 156 | typedef std::vector> thread_group; 157 | typedef thread_group::value_type value_type; 158 | thread_group tg; 159 | std::cerr << "custom variant: "; 160 | auto_cpu_timer timer; 161 | for (std::size_t i = 0; i < THREADS; ++i) 162 | { 163 | tg.emplace_back(new std::thread(run_variant_test, NUM_RUNS)); 164 | } 165 | std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); 166 | } 167 | 168 | { 169 | typedef std::vector> thread_group; 170 | typedef thread_group::value_type value_type; 171 | thread_group tg; 172 | std::cerr << "boost variant: "; 173 | auto_cpu_timer timer; 174 | for (std::size_t i = 0; i < THREADS; ++i) 175 | { 176 | tg.emplace_back(new std::thread(run_boost_test, NUM_RUNS)); 177 | } 178 | std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); 179 | } 180 | } 181 | #endif 182 | 183 | return EXIT_SUCCESS; 184 | } 185 | -------------------------------------------------------------------------------- /test/binary_visitor_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | using namespace mapbox; 16 | 17 | namespace test { 18 | 19 | template 20 | struct string_to_number 21 | { 22 | }; 23 | 24 | template <> 25 | struct string_to_number 26 | { 27 | double operator()(std::string const& str) const 28 | { 29 | return std::stod(str); 30 | } 31 | }; 32 | 33 | template <> 34 | struct string_to_number 35 | { 36 | std::int64_t operator()(std::string const& str) const 37 | { 38 | return std::stoll(str); 39 | } 40 | }; 41 | 42 | template <> 43 | struct string_to_number 44 | { 45 | std::uint64_t operator()(std::string const& str) const 46 | { 47 | return std::stoull(str); 48 | } 49 | }; 50 | 51 | template <> 52 | struct string_to_number 53 | { 54 | bool operator()(std::string const& str) const 55 | { 56 | bool result; 57 | std::istringstream(str) >> std::boolalpha >> result; 58 | return result; 59 | } 60 | }; 61 | 62 | struct javascript_equal_visitor 63 | { 64 | template 65 | bool operator()(T lhs, T rhs) const 66 | { 67 | return lhs == rhs; 68 | } 69 | 70 | template ::value>::type> 71 | bool operator()(T lhs, std::string const& rhs) const 72 | { 73 | return lhs == string_to_number()(rhs); 74 | } 75 | 76 | template ::value>::type> 77 | bool operator()(std::string const& lhs, T rhs) const 78 | { 79 | return string_to_number()(lhs) == rhs; 80 | } 81 | 82 | template 83 | bool operator()(T0 lhs, T1 rhs) const 84 | { 85 | return lhs == static_cast(rhs); 86 | } 87 | }; 88 | 89 | template 90 | struct javascript_equal 91 | { 92 | javascript_equal(T const& lhs) 93 | : lhs_(lhs) {} 94 | 95 | bool operator()(T const& rhs) const 96 | { 97 | return util::apply_visitor(test::javascript_equal_visitor(), lhs_, rhs); 98 | } 99 | T const& lhs_; 100 | }; 101 | 102 | } // namespace test 103 | 104 | int main() 105 | { 106 | typedef util::variant variant_type; 107 | variant_type v0(3.14159); 108 | variant_type v1(std::string("3.14159")); 109 | variant_type v2(std::uint64_t(1)); 110 | 111 | std::cerr << v0 << " == " << v1 << " -> " 112 | << std::boolalpha << util::apply_visitor(test::javascript_equal_visitor(), v0, v1) << std::endl; 113 | 114 | std::vector vec; 115 | 116 | vec.emplace_back(std::string("1")); 117 | vec.push_back(variant_type(std::uint64_t(2))); 118 | vec.push_back(variant_type(std::uint64_t(3))); 119 | vec.push_back(std::string("3.14159")); 120 | vec.emplace_back(3.14159); 121 | 122 | //auto itr = std::find_if(vec.begin(), vec.end(), [&v0](variant_type const& val) { 123 | // return util::apply_visitor(test::javascript_equal_visitor(), v0, val); 124 | // }); 125 | 126 | auto itr = std::find_if(vec.begin(), vec.end(), test::javascript_equal(v2)); 127 | 128 | if (itr != std::end(vec)) 129 | { 130 | std::cout << "found " << *itr << std::endl; 131 | } 132 | else 133 | { 134 | std::cout << "can't find " << v2 << '\n'; 135 | } 136 | 137 | return EXIT_SUCCESS; 138 | } 139 | -------------------------------------------------------------------------------- /test/boost_variant_hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | struct check : boost::static_visitor<> 6 | { 7 | template 8 | void operator()(T const& val) const 9 | { 10 | if (val != 0) throw std::runtime_error("invalid"); 11 | } 12 | }; 13 | 14 | int main() 15 | { 16 | typedef boost::variant variant_type; 17 | variant_type v(0); 18 | boost::apply_visitor(check(), v); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/compilation_failure/default_constructor.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: First type in variant must be default constructible to allow default construction of variant 2 | 3 | #include 4 | 5 | // Checks that the first type in a variant must be default constructible to 6 | // make the variant default constructible. 7 | 8 | struct no_def_constructor 9 | { 10 | 11 | int value; 12 | 13 | no_def_constructor() = delete; 14 | 15 | no_def_constructor(int v) : value(v) {} 16 | }; 17 | 18 | int main() 19 | { 20 | mapbox::util::variant x; 21 | } 22 | -------------------------------------------------------------------------------- /test/compilation_failure/empty_typelist.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: Template parameter type list of variant can not be empty 2 | 3 | #include 4 | 5 | // Empty type list should not work. 6 | 7 | int main() 8 | { 9 | mapbox::util::variant<> x; 10 | } 11 | -------------------------------------------------------------------------------- /test/compilation_failure/equality.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | mapbox::util::variant x; 8 | mapbox::util::variant y; 9 | x == y; 10 | } 11 | -------------------------------------------------------------------------------- /test/compilation_failure/get_type.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: no matching .*\ 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | mapbox::util::variant x; 8 | x.get(); 9 | } 10 | -------------------------------------------------------------------------------- /test/compilation_failure/is_type.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | mapbox::util::variant x; 8 | x.is(); 9 | } 10 | -------------------------------------------------------------------------------- /test/compilation_failure/mutating_visitor_on_const.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: no matching function for call to .*\ 2 | 3 | #include 4 | 5 | struct mutating_visitor 6 | { 7 | mutating_visitor(int val) 8 | : val_(val) {} 9 | 10 | void operator()(int& val) const 11 | { 12 | val = val_; 13 | } 14 | 15 | int val_; 16 | }; 17 | 18 | int main() 19 | { 20 | const mapbox::util::variant var(123); 21 | const mutating_visitor visitor(456); 22 | mapbox::util::apply_visitor(visitor, var); 23 | } 24 | -------------------------------------------------------------------------------- /test/compilation_failure/no-reference.cpp: -------------------------------------------------------------------------------- 1 | // @EXPECTED: Variant can not hold reference types 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | mapbox::util::variant x{mapbox::util::no_init()}; 8 | } 9 | -------------------------------------------------------------------------------- /test/hashable_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using namespace mapbox::util; 12 | 13 | void test_singleton() 14 | { 15 | using V = variant; 16 | 17 | V singleton = 5; 18 | 19 | if (std::hash{}(singleton) != std::hash{}(5)) 20 | { 21 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 22 | std::exit(EXIT_FAILURE); 23 | } 24 | } 25 | 26 | void test_default_hashable() 27 | { 28 | using V = variant; 29 | 30 | V var; 31 | 32 | // Check int hashes 33 | var = 1; 34 | 35 | if (std::hash{}(var) != std::hash{}(1)) 36 | { 37 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 38 | std::exit(EXIT_FAILURE); 39 | } 40 | 41 | // Check double hashes 42 | var = 23.4; 43 | 44 | if (std::hash{}(var) != std::hash{}(23.4)) 45 | { 46 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 47 | std::exit(EXIT_FAILURE); 48 | } 49 | 50 | // Check string hashes 51 | var = std::string{"Hello, World!"}; 52 | 53 | if (std::hash{}(var) != std::hash{}("Hello, World!")) 54 | { 55 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 56 | std::exit(EXIT_FAILURE); 57 | } 58 | } 59 | 60 | struct Hashable 61 | { 62 | static const constexpr auto const_hash = 5; 63 | }; 64 | 65 | namespace std { 66 | template <> 67 | struct hash 68 | { 69 | std::size_t operator()(const Hashable&) const noexcept 70 | { 71 | return Hashable::const_hash; 72 | } 73 | }; 74 | } 75 | 76 | void test_custom_hasher() 77 | { 78 | using V = variant; 79 | 80 | V var; 81 | 82 | var = Hashable{}; 83 | 84 | if (std::hash{}(var) != Hashable::const_hash) 85 | { 86 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 87 | std::exit(EXIT_FAILURE); 88 | } 89 | } 90 | 91 | void test_hashable_in_container() 92 | { 93 | using V = variant; 94 | 95 | // won't compile if V is not Hashable 96 | std::unordered_set vs; 97 | 98 | vs.insert(1); 99 | vs.insert(2.3); 100 | vs.insert("4"); 101 | } 102 | 103 | struct Empty 104 | { 105 | }; 106 | 107 | struct Node; 108 | 109 | using Tree = variant>; 110 | 111 | struct Node 112 | { 113 | Node(Tree left_, Tree right_) : left(std::move(left_)), right(std::move(right_)) {} 114 | 115 | Tree left = Empty{}; 116 | Tree right = Empty{}; 117 | }; 118 | 119 | namespace std { 120 | template <> 121 | struct hash 122 | { 123 | std::size_t operator()(const Empty&) const noexcept 124 | { 125 | return 3; 126 | } 127 | }; 128 | 129 | template <> 130 | struct hash 131 | { 132 | std::size_t operator()(const Node& n) const noexcept 133 | { 134 | return 5 + std::hash{}(n.left) + std::hash{}(n.right); 135 | } 136 | }; 137 | } 138 | 139 | void test_recursive_hashable() 140 | { 141 | 142 | Tree tree = Node{Node{Empty{}, Empty{}}, Empty{}}; 143 | 144 | if (std::hash{}(tree) != ((5 + (5 + (3 + 3))) + 3)) 145 | { 146 | std::cerr << "Expected variant hash to be the same as hash of its value\n"; 147 | std::exit(EXIT_FAILURE); 148 | } 149 | } 150 | 151 | int main() 152 | { 153 | test_singleton(); 154 | test_default_hashable(); 155 | test_custom_hasher(); 156 | test_hashable_in_container(); 157 | test_recursive_hashable(); 158 | } 159 | -------------------------------------------------------------------------------- /test/include/auto_cpu_timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct auto_cpu_timer { 7 | std::chrono::time_point start; 8 | auto_cpu_timer() : start(std::chrono::high_resolution_clock::now()) { 9 | } 10 | ~auto_cpu_timer() { 11 | auto end = std::chrono::high_resolution_clock::now(); 12 | std::chrono::microseconds elapsed = 13 | std::chrono::duration_cast(end - start); 14 | std::cerr << elapsed.count() << "us" << std::endl; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/lambda_overload_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #if __cplusplus >= 201402L 8 | #define HAS_CPP14_SUPPORT 9 | #endif 10 | 11 | using namespace mapbox::util; 12 | 13 | template 14 | struct tag 15 | { 16 | static void dump(const char* prefix) 17 | { 18 | std::cout << prefix << ": " << typeid(tag).name() << std::endl; 19 | } 20 | }; 21 | 22 | template 23 | using Either = mapbox::util::variant; 24 | 25 | struct Response 26 | { 27 | }; 28 | 29 | struct Error 30 | { 31 | }; 32 | 33 | void test_lambda_overloads() 34 | { 35 | Either rv; 36 | 37 | rv = Response{}; 38 | 39 | auto visitor = make_visitor([](Response) { std::cout << "Response\n"; }, // 40 | [](Error) { std::cout << "Error\n"; }); // 41 | apply_visitor(visitor, rv); 42 | } 43 | 44 | void test_lambda_overloads_capture() 45 | { 46 | Either rv; 47 | 48 | rv = Error{}; 49 | 50 | int ok = 0; 51 | int err = 0; 52 | 53 | auto visitor = make_visitor([&](Response) { ok += 1; }, // 54 | [&](Error) { err += 1; }); // 55 | apply_visitor(visitor, rv); 56 | 57 | std::cout << "Got " << ok << " ok, " << err << " err" << std::endl; 58 | } 59 | 60 | void test_singleton_variant() 61 | { 62 | 63 | variant singleton; 64 | apply_visitor(make_visitor([](int) {}), singleton); 65 | } 66 | 67 | // See #180 68 | struct test_call_nonconst_member_visitor 69 | { 70 | template 71 | void operator() (T & obj) const 72 | { 73 | tag::dump("test_call_nonconst_member: visitor"); 74 | obj.foo(); 75 | } 76 | }; 77 | 78 | void test_call_nonconst_member() 79 | { 80 | struct object 81 | { 82 | void foo() { val = 42;} 83 | int val = 0; 84 | }; 85 | 86 | variant v = object{}; 87 | apply_visitor(test_call_nonconst_member_visitor{}, v); 88 | 89 | #ifdef HAS_CPP14_SUPPORT 90 | apply_visitor([](auto& obj) 91 | { 92 | tag::dump("test_call_nonconst_member: lambda"); 93 | obj.foo(); 94 | }, v); 95 | #endif 96 | } 97 | 98 | void test_lambda_overloads_sfinae() 99 | #ifdef HAS_CPP14_SUPPORT 100 | { 101 | variant> var; 102 | 103 | auto visitor = make_visitor([](auto range) -> decltype(std::begin(range), void()) { 104 | for (auto each : range) 105 | std::cout << each << ' '; }, 106 | [](auto x) -> decltype(std::cout << x, void()) { 107 | std::cout << x << std::endl; 108 | }); 109 | 110 | var = 1; 111 | apply_visitor(visitor, var); 112 | 113 | var = 2.f; 114 | apply_visitor(visitor, var); 115 | 116 | var = std::vector{4, 5, 6}; 117 | apply_visitor(visitor, var); 118 | } 119 | #else 120 | { 121 | } 122 | #endif 123 | 124 | void test_match_singleton() 125 | { 126 | variant singleton = 5; 127 | singleton.match([](int) {}); 128 | 129 | auto lambda = [](int) {}; 130 | singleton.match(lambda); 131 | } 132 | 133 | void test_match_overloads() 134 | { 135 | Either rv; 136 | 137 | rv = Response{}; 138 | 139 | rv.match([](Response) { std::cout << "Response\n"; }, // 140 | [](Error) { std::cout << "Error\n"; }); // 141 | } 142 | 143 | void test_match_overloads_capture() 144 | { 145 | Either rv; 146 | 147 | rv = Error{}; 148 | 149 | int ok = 0; 150 | int err = 0; 151 | 152 | rv.match([&](Response) { ok += 1; }, // 153 | [&](Error) { err += 1; }); // 154 | 155 | std::cout << "Got " << ok << " ok, " << err << " err" << std::endl; 156 | } 157 | 158 | struct MovableOnly 159 | { 160 | MovableOnly() = default; 161 | 162 | MovableOnly(MovableOnly&&) = default; 163 | MovableOnly& operator=(MovableOnly&&) = default; 164 | }; 165 | 166 | struct MovableCopyable 167 | { 168 | MovableCopyable() = default; 169 | 170 | MovableCopyable(MovableCopyable&&) = default; 171 | MovableCopyable& operator=(MovableCopyable&&) = default; 172 | 173 | MovableCopyable(const MovableCopyable&) = default; 174 | MovableCopyable& operator=(const MovableCopyable&) = default; 175 | }; 176 | 177 | void test_match_overloads_init_capture() 178 | #ifdef HAS_CPP14_SUPPORT 179 | { 180 | Either rv; 181 | 182 | rv = Error{}; 183 | 184 | rv.match([p = MovableOnly{}](auto&&) {}); 185 | { 186 | auto lambda = [p = MovableCopyable{}](auto&&) {}; 187 | rv.match(lambda); 188 | 189 | rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, 190 | [p = MovableOnly{}](Error) { std::cout << "Error\n"; }); 191 | } 192 | { 193 | auto lambda = [](Error) { std::cout << "Error\n"; }; 194 | rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, 195 | lambda); 196 | rv.match(lambda, 197 | [p = MovableOnly{}](Response) { std::cout << "Response\n"; }); 198 | } 199 | } 200 | #else 201 | { 202 | } 203 | #endif 204 | 205 | // See #140 206 | void test_match_overloads_otherwise() 207 | #ifdef HAS_CPP14_SUPPORT 208 | { 209 | 210 | struct Center 211 | { 212 | }; 213 | struct Indent 214 | { 215 | }; 216 | struct Justify 217 | { 218 | }; 219 | struct None 220 | { 221 | }; 222 | 223 | using Properties = mapbox::util::variant; 224 | 225 | Properties props = Justify{}; 226 | 227 | props.match([&](Center) { std::cout << "Center\n"; }, // 228 | [&](Indent) { std::cout << "Indent\n"; }, // 229 | [&](auto&&) { std::cout << "Otherwise\n"; }); // 230 | } 231 | #else 232 | { 233 | } 234 | #endif 235 | 236 | template 237 | struct Moveable 238 | { 239 | Moveable() = default; // Default constructible 240 | 241 | Moveable(const Moveable&) = delete; // Disable copy ctor 242 | Moveable& operator=(const Moveable&) = delete; // Disable copy assign op 243 | 244 | Moveable(Moveable&&) = default; // Enable move ctor 245 | Moveable& operator=(Moveable&&) = default; // Enable move assign op 246 | }; 247 | 248 | void test_match_move_out_of_variant() 249 | { 250 | // Distinguishable at type level 251 | using T1 = Moveable; 252 | using T2 = Moveable; 253 | using T3 = mapbox::util::recursive_wrapper; 254 | 255 | mapbox::util::variant v = T1{}; 256 | 257 | std::move(v).match([](T1&&) {}, // Consume T1 by value 258 | [](T2&&) {}); // Consume T2 by value 259 | 260 | mapbox::util::variant w = T2{}; 261 | 262 | std::move(w).match([](int&&) {}, // Consume unwrapped int 263 | [](T2&&) {}); // Consume T2 by value 264 | } 265 | 266 | int main() 267 | { 268 | test_lambda_overloads(); 269 | test_singleton_variant(); 270 | test_call_nonconst_member(); 271 | test_lambda_overloads_capture(); 272 | test_lambda_overloads_sfinae(); 273 | 274 | test_match_singleton(); 275 | test_match_overloads(); 276 | test_match_overloads_capture(); 277 | test_match_overloads_init_capture(); 278 | test_match_overloads_otherwise(); 279 | test_match_move_out_of_variant(); 280 | } 281 | 282 | #undef HAS_CPP14_SUPPORT 283 | -------------------------------------------------------------------------------- /test/our_variant_hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | struct check 6 | { 7 | template 8 | void operator()(T const& val) const 9 | { 10 | if (val != 0) throw std::runtime_error("invalid"); 11 | } 12 | }; 13 | 14 | int main() 15 | { 16 | typedef mapbox::util::variant variant_type; 17 | variant_type v(0); 18 | mapbox::util::apply_visitor(check(), v); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/recursive_wrapper_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "auto_cpu_timer.hpp" 8 | 9 | #include 10 | 11 | using namespace mapbox; 12 | 13 | namespace test { 14 | 15 | struct add; 16 | struct sub; 17 | 18 | template 19 | struct binary_op; 20 | 21 | typedef util::variant>, 23 | util::recursive_wrapper>> 24 | expression; 25 | 26 | template 27 | struct binary_op 28 | { 29 | expression left; // variant instantiated here... 30 | expression right; 31 | 32 | binary_op(expression&& lhs, expression&& rhs) 33 | : left(std::move(lhs)), right(std::move(rhs)) 34 | { 35 | } 36 | }; 37 | 38 | struct print 39 | { 40 | template 41 | void operator()(T const& val) const 42 | { 43 | std::cerr << val << ":" << typeid(T).name() << std::endl; 44 | } 45 | }; 46 | 47 | struct test 48 | { 49 | template 50 | std::string operator()(T const& obj) const 51 | { 52 | return std::string("TYPE_ID=") + typeid(obj).name(); 53 | } 54 | }; 55 | 56 | struct calculator 57 | { 58 | public: 59 | int operator()(int value) const 60 | { 61 | return value; 62 | } 63 | 64 | int operator()(binary_op const& binary) const 65 | { 66 | return util::apply_visitor(*this, binary.left) + util::apply_visitor(*this, binary.right); 67 | } 68 | 69 | int operator()(binary_op const& binary) const 70 | { 71 | return util::apply_visitor(*this, binary.left) - util::apply_visitor(*this, binary.right); 72 | } 73 | }; 74 | 75 | struct to_string 76 | { 77 | public: 78 | std::string operator()(int value) const 79 | { 80 | return std::to_string(value); 81 | } 82 | 83 | std::string operator()(binary_op const& binary) const 84 | { 85 | return util::apply_visitor(*this, binary.left) + std::string("+") + util::apply_visitor(*this, binary.right); 86 | } 87 | 88 | std::string operator()(binary_op const& binary) const 89 | { 90 | return util::apply_visitor(*this, binary.left) + std::string("-") + util::apply_visitor(*this, binary.right); 91 | } 92 | }; 93 | 94 | } // namespace test 95 | 96 | int main(int argc, char** argv) 97 | { 98 | if (argc != 2) 99 | { 100 | std::cerr << "Usage" << argv[0] << " " << std::endl; 101 | return EXIT_FAILURE; 102 | } 103 | 104 | const std::size_t NUM_ITER = static_cast(std::stol(argv[1])); 105 | 106 | test::expression sum(test::binary_op(2, 3)); 107 | test::expression result(test::binary_op(std::move(sum), 4)); 108 | 109 | std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; 110 | 111 | int total = 0; 112 | { 113 | auto_cpu_timer t; 114 | for (std::size_t i = 0; i < NUM_ITER; ++i) 115 | { 116 | total += util::apply_visitor(test::calculator(), result); 117 | } 118 | } 119 | std::cerr << "total=" << total << std::endl; 120 | 121 | std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; 122 | 123 | return EXIT_SUCCESS; 124 | } 125 | -------------------------------------------------------------------------------- /test/reference_wrapper_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using namespace mapbox; 12 | 13 | namespace test { 14 | 15 | struct point 16 | { 17 | public: 18 | point(double x_, double y_) 19 | : x(x_), y(y_) {} 20 | double x; 21 | double y; 22 | }; 23 | 24 | struct line_string : std::vector 25 | { 26 | }; 27 | struct polygon : std::vector 28 | { 29 | }; 30 | using variant = util::variant, 31 | std::reference_wrapper, 32 | std::reference_wrapper>; 33 | 34 | struct print 35 | { 36 | using result_type = void; 37 | void operator()(point const& pt) const 38 | { 39 | std::cerr << "Point(" << pt.x << "," << pt.y << ")" << std::endl; 40 | } 41 | void operator()(line_string const& line) const 42 | { 43 | std::cerr << "Line("; 44 | bool first = true; 45 | for (auto const& pt : line) 46 | { 47 | if (!first) std::cerr << ","; 48 | std::cerr << pt.x << " " << pt.y; 49 | if (first) first = false; 50 | } 51 | std::cerr << ")" << std::endl; 52 | } 53 | template 54 | void operator()(T const& val) const 55 | { 56 | std::cerr << typeid(T).name() << std::endl; 57 | } 58 | }; 59 | } 60 | 61 | int main() 62 | { 63 | std::cerr << sizeof(test::polygon) << std::endl; 64 | std::cerr << sizeof(test::variant) << std::endl; 65 | test::point pt(123, 456); 66 | test::variant var = std::cref(pt); 67 | util::apply_visitor(test::print(), var); 68 | test::line_string line; 69 | line.push_back(pt); 70 | line.push_back(pt); 71 | line.push_back(test::point(999, 333)); 72 | var = std::cref(line); 73 | util::apply_visitor(test::print(), var); 74 | std::cerr << "Is line (cref) ? " << var.is>() << std::endl; 75 | auto const& line2 = var.get(); // accessing underlying type of std::reference_wrapper 76 | test::print printer; 77 | printer(line2); 78 | return EXIT_SUCCESS; 79 | } 80 | -------------------------------------------------------------------------------- /test/t/binary_visitor_1.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " i-d" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " b-i-d" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_3.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " i-d-b" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_4.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " b-i-d-c" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_5.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " b-i-c-d-i" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_6.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define NAME_EXT " b-i-i-d-c-u" 5 | using variant_type = mapbox::util::variant; 6 | 7 | #include "binary_visitor_impl.hpp" 8 | -------------------------------------------------------------------------------- /test/t/binary_visitor_impl.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "catch.hpp" 5 | 6 | #include 7 | 8 | struct add_visitor 9 | { 10 | add_visitor() {} 11 | 12 | template 13 | double operator()(A a, B b) const 14 | { 15 | return a + b; 16 | } 17 | }; 18 | 19 | TEST_CASE("const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") 20 | { 21 | const variant_type a{7}; 22 | const variant_type b = 3; 23 | const variant_type c{7.1}; 24 | const variant_type d = 2.9; 25 | 26 | const add_visitor v; 27 | 28 | REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); 29 | REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); 30 | REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); 31 | REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); 32 | 33 | REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); 34 | REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); 35 | REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); 36 | REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); 37 | } 38 | 39 | TEST_CASE("non-const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") 40 | { 41 | const variant_type a = 7; 42 | const variant_type b = 3; 43 | const variant_type c = 7.1; 44 | const variant_type d = 2.9; 45 | 46 | add_visitor v; 47 | 48 | REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); 49 | REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); 50 | REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); 51 | REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); 52 | 53 | REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); 54 | REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); 55 | REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); 56 | REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); 57 | } 58 | 59 | TEST_CASE("const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") 60 | { 61 | variant_type a = 7; 62 | variant_type b = 3; 63 | variant_type c = 7.1; 64 | variant_type d = 2.9; 65 | 66 | const add_visitor v; 67 | 68 | REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); 69 | REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); 70 | REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); 71 | REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); 72 | 73 | REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); 74 | REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); 75 | REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); 76 | REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); 77 | } 78 | 79 | TEST_CASE("non-const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") 80 | { 81 | variant_type a = 7; 82 | variant_type b = 3; 83 | variant_type c = 7.1; 84 | variant_type d = 2.9; 85 | 86 | add_visitor v; 87 | 88 | REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); 89 | REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); 90 | REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); 91 | REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); 92 | 93 | REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); 94 | REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); 95 | REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); 96 | REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); 97 | } 98 | 99 | TEST_CASE("rvalue binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") 100 | { 101 | const variant_type a = 7; 102 | const variant_type b = 3; 103 | const variant_type c = 7.1; 104 | const variant_type d = 2.9; 105 | 106 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, b) == Approx(10)); 107 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, d) == Approx(10)); 108 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, c) == Approx(14.1)); 109 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, d) == Approx(9.9)); 110 | 111 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, b, a) == Approx(10)); 112 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, c) == Approx(10)); 113 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, a) == Approx(14.1)); 114 | REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, a) == Approx(9.9)); 115 | } 116 | 117 | struct sum_mul_visitor 118 | { 119 | double sum; 120 | 121 | sum_mul_visitor() : sum(0.0) {} 122 | 123 | template 124 | double operator()(A a, B b) 125 | { 126 | double m = a * b; 127 | sum += m; 128 | return m; 129 | } 130 | }; 131 | 132 | TEST_CASE("mutable binary visitor works" NAME_EXT, "[visitor][binary visitor]") 133 | { 134 | const variant_type a = 2; 135 | const variant_type b = 3; 136 | const variant_type c = 0.1; 137 | const variant_type d = 0.2; 138 | 139 | sum_mul_visitor v; 140 | 141 | REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(6)); 142 | REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(0.02)); 143 | REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(0.2)); 144 | REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(0.4)); 145 | 146 | REQUIRE(v.sum == Approx(6.62)); 147 | 148 | REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(6)); 149 | REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(0.02)); 150 | REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(0.2)); 151 | REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(0.4)); 152 | } 153 | 154 | struct swap_visitor 155 | { 156 | swap_visitor(){}; 157 | 158 | template 159 | void operator()(A& a, B& b) const 160 | { 161 | using T = typename std::common_type::type; 162 | T tmp = a; 163 | a = static_cast(b); 164 | b = static_cast(tmp); 165 | } 166 | }; 167 | 168 | TEST_CASE("static mutating visitor on mutable variants works" NAME_EXT, "[visitor][binary visitor]") 169 | { 170 | variant_type a = 2; 171 | variant_type b = 3; 172 | variant_type c = 0.1; 173 | variant_type d = 0.2; 174 | 175 | const swap_visitor v; 176 | 177 | SECTION("swap a and b") 178 | { 179 | mapbox::util::apply_visitor(v, a, b); 180 | REQUIRE(a.get() == 3); 181 | REQUIRE(b.get() == 2); 182 | } 183 | 184 | SECTION("swap c and d") 185 | { 186 | mapbox::util::apply_visitor(v, c, d); 187 | REQUIRE(c.get() == Approx(0.2)); 188 | REQUIRE(d.get() == Approx(0.1)); 189 | } 190 | 191 | SECTION("swap a and c") 192 | { 193 | mapbox::util::apply_visitor(v, a, c); 194 | REQUIRE(a.get() == 0); 195 | REQUIRE(c.get() == Approx(2.0)); 196 | } 197 | 198 | SECTION("swap c and a") 199 | { 200 | mapbox::util::apply_visitor(v, c, a); 201 | REQUIRE(a.get() == 0); 202 | REQUIRE(c.get() == Approx(2.0)); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /test/t/issue122.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | // https://github.com/mapbox/variant/issues/122 7 | 8 | struct X 9 | { 10 | template 11 | X(const ValueType&) {} 12 | }; 13 | 14 | 15 | TEST_CASE("Correctly choose appropriate constructor", "[variant]") 16 | { 17 | mapbox::util::variant a{123}; 18 | decltype(a) b(a); 19 | REQUIRE(a.which() == b.which()); 20 | } 21 | -------------------------------------------------------------------------------- /test/t/issue21.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | // https://github.com/mapbox/variant/issues/21 7 | 8 | static int count; 9 | 10 | struct t1 11 | { 12 | int value; 13 | 14 | t1(t1 const& rhs) 15 | : value(rhs.value) 16 | { 17 | ++count; 18 | } 19 | t1(int v) : value(v) 20 | { 21 | ++count; 22 | } 23 | ~t1() 24 | { 25 | --count; 26 | } 27 | }; 28 | 29 | struct t2 30 | { 31 | int value; 32 | t2(int v) : value(v) 33 | { // constructor fails 34 | throw std::runtime_error("fail"); 35 | } 36 | }; 37 | 38 | TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]") 39 | { 40 | 41 | using variant_type = mapbox::util::variant; 42 | 43 | count = 0; 44 | { 45 | t1 obj{42}; 46 | variant_type v = obj; 47 | REQUIRE(v.is()); 48 | REQUIRE(v.get().value == 42); 49 | REQUIRE_THROWS(v.set(13)); 50 | } 51 | REQUIRE(count == 0); 52 | } 53 | -------------------------------------------------------------------------------- /test/t/mutating_visitor.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | template 10 | struct mutating_visitor 11 | { 12 | mutating_visitor(T& val) 13 | : val_(val) {} 14 | 15 | void operator()(T& val) const 16 | { 17 | val = val_; 18 | } 19 | 20 | template 21 | void operator()(T1&) const 22 | { 23 | } // no-op 24 | 25 | T& val_; 26 | }; 27 | 28 | TEST_CASE("variant visitation", "[visitor][unary visitor]") 29 | { 30 | mapbox::util::variant var(123); 31 | REQUIRE(var.get() == 123); 32 | int val = 456; 33 | const mutating_visitor visitor(val); 34 | mapbox::util::apply_visitor(visitor, var); 35 | REQUIRE(var.get() == 456); 36 | } 37 | -------------------------------------------------------------------------------- /test/t/nothrow_move.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | using namespace mapbox; 7 | 8 | namespace test { 9 | 10 | struct t_noexcept_true_1 { 11 | t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default; 12 | t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default; 13 | }; 14 | 15 | struct t_noexcept_true_2 { 16 | t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default; 17 | t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default; 18 | }; 19 | 20 | struct t_noexcept_false_1 { 21 | t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {} 22 | t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; } 23 | }; 24 | 25 | using should_be_no_throw_copyable = util::variant; 26 | static_assert(std::is_nothrow_move_assignable::value, 27 | "variants with no-throw move assignable types should be " 28 | "no-throw move nothrow assignable"); 29 | 30 | using should_be_no_throw_assignable = util::variant; 31 | static_assert(std::is_nothrow_move_constructible::value, 32 | "variants with no-throw move assignable types should be " 33 | "no-throw move nothrow assignable"); 34 | 35 | using should_not_be_no_throw_copyable = util::variant; 36 | static_assert(not std::is_nothrow_move_assignable::value, 37 | "variants with no-throw move assignable types should be " 38 | "no-throw move nothrow assignable"); 39 | 40 | using should_not_be_no_throw_assignable = util::variant; 41 | static_assert(not std::is_nothrow_move_constructible::value, 42 | "variants with no-throw move assignable types should be " 43 | "no-throw move nothrow assignable"); 44 | 45 | 46 | // this type cannot be nothrow converted from either of its types, even the nothrow moveable one, 47 | // because the conversion operator moves the whole variant. 48 | using convertable_test_type = util::variant; 49 | 50 | // this type can be nothrow converted from either of its types. 51 | using convertable_test_type_2 = util::variant; 52 | 53 | static_assert(not std::is_nothrow_assignable::value, 54 | "variants with noexcept(true) move constructible types should be nothrow-convertible " 55 | "from those types only IF the variant itself is nothrow_move_assignable"); 56 | 57 | static_assert(not std::is_nothrow_assignable::value, 58 | "variants with noexcept(false) move constructible types should not be nothrow-convertible " 59 | "from those types"); 60 | 61 | static_assert(std::is_nothrow_assignable::value, 62 | "variants with noexcept(true) move constructible types should be nothrow-convertible " 63 | "from those types only IF the variant itself is nothrow_move_assignable"); 64 | 65 | 66 | } // namespace test 67 | -------------------------------------------------------------------------------- /test/t/optional.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | 5 | struct dummy 6 | { 7 | dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {} 8 | int m_1; 9 | int m_2; 10 | }; 11 | 12 | TEST_CASE("optional can be instantiated with a POD type", "[optional]") 13 | { 14 | mapbox::util::optional dbl_opt; 15 | 16 | REQUIRE(!dbl_opt); 17 | dbl_opt = 3; 18 | REQUIRE(dbl_opt); 19 | 20 | REQUIRE(dbl_opt.get() == 3); 21 | REQUIRE(*dbl_opt == 3); 22 | } 23 | 24 | TEST_CASE("copy c'tor", "[optional]") 25 | { 26 | mapbox::util::optional dbl_opt; 27 | 28 | REQUIRE(!dbl_opt); 29 | dbl_opt = 3; 30 | REQUIRE(dbl_opt); 31 | 32 | mapbox::util::optional other = dbl_opt; 33 | 34 | REQUIRE(other.get() == 3); 35 | REQUIRE(*other == 3); 36 | } 37 | 38 | TEST_CASE("const operator*, const get()", "[optional]") 39 | { 40 | const mapbox::util::optional dbl_opt = 3; 41 | 42 | REQUIRE(dbl_opt); 43 | 44 | auto pi1 = dbl_opt.get(); 45 | auto pi2 = *dbl_opt; 46 | 47 | REQUIRE(pi1 == 3); 48 | REQUIRE(pi2 == 3); 49 | } 50 | 51 | TEST_CASE("non-const operator*, non-const get()", "[optional]") 52 | { 53 | mapbox::util::optional dbl_opt = 3; 54 | 55 | REQUIRE(dbl_opt); 56 | 57 | auto pi1 = dbl_opt.get(); 58 | auto pi2 = *dbl_opt; 59 | 60 | REQUIRE(pi1 == 3); 61 | REQUIRE(pi2 == 3); 62 | } 63 | 64 | TEST_CASE("emplace initialization, reset", "[optional]") 65 | { 66 | mapbox::util::optional dummy_opt; 67 | REQUIRE(!dummy_opt); 68 | 69 | // rvalues, baby! 70 | dummy_opt.emplace(1, 2); 71 | REQUIRE(dummy_opt); 72 | REQUIRE(dummy_opt.get().m_1 == 1); 73 | REQUIRE((*dummy_opt).m_2 == 2); 74 | 75 | dummy_opt.reset(); 76 | REQUIRE(!dummy_opt); 77 | } 78 | 79 | TEST_CASE("assignment", "[optional]") 80 | { 81 | mapbox::util::optional a; 82 | mapbox::util::optional b; 83 | 84 | a = 1; 85 | b = 3; 86 | REQUIRE(a.get() == 1); 87 | REQUIRE(b.get() == 3); 88 | b = a; 89 | REQUIRE(a.get() == b.get()); 90 | REQUIRE(b.get() == 1); 91 | } 92 | 93 | TEST_CASE("self assignment", "[optional]") 94 | { 95 | mapbox::util::optional a; 96 | 97 | a = 1; 98 | REQUIRE(a.get() == 1); 99 | #if !defined(__clang__) 100 | a = a; 101 | REQUIRE(a.get() == 1); 102 | #endif 103 | } 104 | -------------------------------------------------------------------------------- /test/t/recursive_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using rwi = mapbox::util::recursive_wrapper; 10 | using rwp = mapbox::util::recursive_wrapper>; 11 | 12 | static_assert(std::is_same::value, "type check failed"); 13 | 14 | TEST_CASE("recursive wrapper of int") 15 | { 16 | 17 | SECTION("construct with value") 18 | { 19 | rwi a{7}; 20 | 21 | REQUIRE(a.get() == 7); 22 | REQUIRE(*a.get_pointer() == 7); 23 | 24 | a = 8; 25 | REQUIRE(a.get() == 8); 26 | 27 | rwi b{a}; 28 | REQUIRE(b.get() == 8); 29 | 30 | rwi c; 31 | c = b; 32 | REQUIRE(b.get() == 8); 33 | REQUIRE(c.get() == 8); 34 | 35 | c = 9; 36 | REQUIRE(c.get() == 9); 37 | 38 | int x = 10; 39 | c = x; 40 | REQUIRE(c.get() == 10); 41 | 42 | b = std::move(c); 43 | REQUIRE(b.get() == 10); 44 | } 45 | 46 | SECTION("construct with const reference") 47 | { 48 | int i = 7; 49 | rwi a{i}; 50 | 51 | REQUIRE(a.get() == 7); 52 | } 53 | 54 | SECTION("implicit conversion to reference of underlying type") 55 | { 56 | 57 | SECTION("const") 58 | { 59 | rwi const a{7}; 60 | REQUIRE(a.get() == 7); 61 | REQUIRE(*a.get_pointer() == 7); 62 | 63 | rwi::type const& underlying = a; 64 | REQUIRE(underlying == 7); 65 | } 66 | 67 | SECTION("non const") 68 | { 69 | rwi a{7}; 70 | REQUIRE(a.get() == 7); 71 | REQUIRE(*a.get_pointer() == 7); 72 | 73 | rwi::type& underlying = a; 74 | REQUIRE(underlying == 7); 75 | a = 8; 76 | REQUIRE(underlying == 8); 77 | } 78 | } 79 | } 80 | 81 | TEST_CASE("move of recursive wrapper") 82 | { 83 | rwi a{1}; 84 | 85 | SECTION("move constructor") 86 | { 87 | rwi b{std::move(a)}; 88 | REQUIRE(b.get() == 1); 89 | } 90 | 91 | SECTION("operator= on rvalue") 92 | { 93 | rwi b{2}; 94 | b = std::move(a); 95 | REQUIRE(b.get() == 1); 96 | } 97 | } 98 | 99 | TEST_CASE("swap") 100 | { 101 | rwi a{1}; 102 | rwi b{2}; 103 | 104 | REQUIRE(a.get() == 1); 105 | REQUIRE(b.get() == 2); 106 | 107 | using std::swap; 108 | swap(a, b); 109 | 110 | REQUIRE(a.get() == 2); 111 | REQUIRE(b.get() == 1); 112 | } 113 | 114 | TEST_CASE("recursive wrapper of pair") 115 | { 116 | 117 | SECTION("default constructed") 118 | { 119 | rwp a; 120 | REQUIRE(a.get().first == 0); 121 | REQUIRE(a.get().second == 0); 122 | } 123 | 124 | SECTION("construct with value") 125 | { 126 | rwp a{std::make_pair(1, 2)}; 127 | 128 | REQUIRE(a.get().first == 1); 129 | REQUIRE(a.get().second == 2); 130 | 131 | REQUIRE(a.get_pointer()->first == 1); 132 | REQUIRE(a.get_pointer()->second == 2); 133 | 134 | a = {3, 4}; 135 | REQUIRE(a.get().first == 3); 136 | REQUIRE(a.get().second == 4); 137 | 138 | rwp b{a}; 139 | REQUIRE(b.get().first == 3); 140 | REQUIRE(b.get().second == 4); 141 | 142 | rwp c; 143 | c = b; 144 | REQUIRE(b.get().first == 3); 145 | REQUIRE(b.get().second == 4); 146 | REQUIRE(c.get().first == 3); 147 | REQUIRE(c.get().second == 4); 148 | 149 | c = {5, 6}; 150 | REQUIRE(c.get().first == 5); 151 | REQUIRE(c.get().second == 6); 152 | 153 | b = std::move(c); 154 | REQUIRE(b.get().first == 5); 155 | REQUIRE(b.get().second == 6); 156 | //REQUIRE(c.get_pointer() == nullptr); 157 | } 158 | 159 | SECTION("Multiple recurssive wrappers of polymorphic types") 160 | { 161 | // https://github.com/mapbox/variant/issues/146 162 | // (Visual Studio 2015 update 3) 163 | using namespace mapbox::util; 164 | struct Base; 165 | struct Derived; 166 | using Variant = variant, recursive_wrapper>; 167 | struct Base { }; 168 | struct Derived : public Base { }; 169 | { 170 | Base base; 171 | Derived derived; 172 | Variant v; 173 | v = base; 174 | v = derived; // compile error prior https://github.com/mapbox/variant/pull/147 175 | CHECK(v.is()); 176 | } 177 | { 178 | Derived derived; 179 | Variant v(derived); // compile error prior https://github.com/mapbox/variant/pull/147 180 | CHECK(v.is()); 181 | } 182 | 183 | 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /test/t/sizeof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "catch.hpp" 6 | 7 | #include 8 | #include 9 | 10 | struct some_struct 11 | { 12 | int a; 13 | bool b; 14 | std::string c; 15 | }; 16 | 17 | using variant_internal_index_type = mapbox::util::type_index_t; 18 | 19 | TEST_CASE("size of variants") 20 | { 21 | constexpr const auto min_overhead = sizeof(variant_internal_index_type); 22 | 23 | using namespace std; // workaround for bug in GCC <= 4.8 where max_align_t is not in std 24 | constexpr const auto max_overhead = alignof(max_align_t) + min_overhead; 25 | 26 | using v1 = mapbox::util::variant; 27 | using v2 = mapbox::util::variant; 28 | using v3 = mapbox::util::variant; 29 | using v4 = mapbox::util::variant; 30 | using v5 = mapbox::util::variant; 31 | 32 | constexpr const auto si = sizeof(int); 33 | constexpr const auto sb = sizeof(bool); 34 | constexpr const auto si64 = sizeof(int64_t); 35 | constexpr const auto sd = sizeof(double); 36 | constexpr const auto sstr = sizeof(std::string); 37 | constexpr const auto spi = sizeof(std::pair); 38 | constexpr const auto ss = sizeof(some_struct); 39 | 40 | REQUIRE(sizeof(v1) <= max_overhead + si); 41 | REQUIRE(sizeof(v2) <= max_overhead + std::max({si, sb, si64})); 42 | REQUIRE(sizeof(v3) <= max_overhead + std::max({si, sstr})); 43 | REQUIRE(sizeof(v4) <= max_overhead + sstr); 44 | REQUIRE(sizeof(v5) <= max_overhead + ss); 45 | 46 | REQUIRE(sizeof(v1) >= min_overhead + si); 47 | REQUIRE(sizeof(v2) >= min_overhead + std::max({si, sb, si64})); 48 | REQUIRE(sizeof(v3) >= min_overhead + std::max({si, sstr})); 49 | REQUIRE(sizeof(v4) >= min_overhead + sstr); 50 | REQUIRE(sizeof(v5) >= min_overhead + ss); 51 | } 52 | -------------------------------------------------------------------------------- /test/t/unary_visitor.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct some_visitor 10 | { 11 | int var_; 12 | 13 | some_visitor(int init) 14 | : var_(init) {} 15 | 16 | int operator()(int val) const 17 | { 18 | return var_ + val; 19 | } 20 | 21 | int operator()(double val) const 22 | { 23 | return var_ + int(val); 24 | } 25 | 26 | int operator()(const std::string&) const 27 | { 28 | return 0; 29 | } 30 | }; 31 | 32 | TEST_CASE("non-const visitor works on const variants", "[visitor][unary visitor]") 33 | { 34 | using variant_type = const mapbox::util::variant; 35 | variant_type var1(123); 36 | variant_type var2(3.2); 37 | variant_type var3("foo"); 38 | REQUIRE(var1.get() == 123); 39 | REQUIRE(var2.get() == Approx(3.2)); 40 | REQUIRE(var3.get() == "foo"); 41 | 42 | some_visitor visitor{1}; 43 | 44 | REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); 45 | REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); 46 | REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); 47 | } 48 | 49 | TEST_CASE("const visitor works on const variants", "[visitor][unary visitor]") 50 | { 51 | using variant_type = const mapbox::util::variant; 52 | variant_type var1(123); 53 | variant_type var2(3.2); 54 | variant_type var3("foo"); 55 | REQUIRE(var1.get() == 123); 56 | REQUIRE(var2.get() == Approx(3.2)); 57 | REQUIRE(var3.get() == "foo"); 58 | 59 | const some_visitor visitor{1}; 60 | 61 | REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); 62 | REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); 63 | REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); 64 | } 65 | 66 | TEST_CASE("rvalue visitor works on const variants", "[visitor][unary visitor]") 67 | { 68 | using variant_type = const mapbox::util::variant; 69 | variant_type var1(123); 70 | variant_type var2(3.2); 71 | variant_type var3("foo"); 72 | REQUIRE(var1.get() == 123); 73 | REQUIRE(var2.get() == Approx(3.2)); 74 | REQUIRE(var3.get() == "foo"); 75 | 76 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var1) == 124); 77 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var2) == 4); 78 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var3) == 0); 79 | } 80 | 81 | TEST_CASE("visitor works on rvalue variants", "[visitor][unary visitor]") 82 | { 83 | using variant_type = const mapbox::util::variant; 84 | 85 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{123}) == 124); 86 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{3.2}) == 4); 87 | REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{"foo"}) == 0); 88 | } 89 | 90 | struct total_sizeof 91 | { 92 | total_sizeof() : total_(0) {} 93 | 94 | template 95 | int operator()(const Value&) const 96 | { 97 | total_ += int(sizeof(Value)); 98 | return total_; 99 | } 100 | 101 | int result() const 102 | { 103 | return total_; 104 | } 105 | 106 | mutable int total_; 107 | 108 | }; // total_sizeof 109 | 110 | TEST_CASE("changes in visitor should be visible", "[visitor][unary visitor]") 111 | { 112 | using variant_type = mapbox::util::variant; 113 | variant_type v; 114 | total_sizeof ts; 115 | v = 5.9; 116 | REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(double)); 117 | REQUIRE(ts.result() == sizeof(double)); 118 | } 119 | 120 | TEST_CASE("changes in const visitor (with mutable internals) should be visible", "[visitor][unary visitor]") 121 | { 122 | using variant_type = const mapbox::util::variant; 123 | variant_type v{"foo"}; 124 | const total_sizeof ts; 125 | REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(std::string)); 126 | REQUIRE(ts.result() == sizeof(std::string)); 127 | } 128 | -------------------------------------------------------------------------------- /test/t/variant.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Hack to make nullptr work with Catch 17 | namespace std { 18 | 19 | template 20 | std::basic_ostream& operator<<(std::basic_ostream& os, std::nullptr_t) 21 | { 22 | return os << (void*)nullptr; 23 | } 24 | } 25 | 26 | TEST_CASE("variant can be moved into vector", "[variant]") 27 | { 28 | using variant_type = mapbox::util::variant; 29 | variant_type v(std::string("test")); 30 | std::vector vec; 31 | vec.emplace_back(std::move(v)); 32 | REQUIRE(v.get() != std::string("test")); 33 | REQUIRE(vec.at(0).get() == std::string("test")); 34 | } 35 | 36 | TEST_CASE("variant should support built-in types", "[variant]") 37 | { 38 | SECTION("bool") 39 | { 40 | mapbox::util::variant v(true); 41 | REQUIRE(v.valid()); 42 | REQUIRE(v.is()); 43 | REQUIRE(v.which() == 0); 44 | REQUIRE(v.get() == true); 45 | v.set(false); 46 | REQUIRE(v.get() == false); 47 | v = true; 48 | REQUIRE(v == mapbox::util::variant(true)); 49 | } 50 | SECTION("nullptr") 51 | { 52 | using value_type = std::nullptr_t; 53 | mapbox::util::variant v(nullptr); 54 | REQUIRE(v.valid()); 55 | REQUIRE(v.is()); 56 | REQUIRE(v.which() == 0); 57 | REQUIRE(v.get() == nullptr); 58 | REQUIRE(v == mapbox::util::variant(nullptr)); 59 | } 60 | SECTION("unique_ptr") 61 | { 62 | using value_type = std::unique_ptr; 63 | mapbox::util::variant v(value_type(new std::string("hello"))); 64 | REQUIRE(v.valid()); 65 | REQUIRE(v.is()); 66 | REQUIRE(v.which() == 0); 67 | REQUIRE(*v.get().get() == *value_type(new std::string("hello")).get()); 68 | REQUIRE(*v.get() == "hello"); 69 | } 70 | SECTION("string") 71 | { 72 | using value_type = std::string; 73 | mapbox::util::variant v(value_type("hello")); 74 | REQUIRE(v.valid()); 75 | REQUIRE(v.is()); 76 | REQUIRE(v.which() == 0); 77 | REQUIRE(v.get() == value_type("hello")); 78 | v.set(value_type("there")); 79 | REQUIRE(v.get() == value_type("there")); 80 | v = value_type("variant"); 81 | REQUIRE(v == mapbox::util::variant(value_type("variant"))); 82 | } 83 | SECTION("size_t") 84 | { 85 | using value_type = std::size_t; 86 | mapbox::util::variant v(std::numeric_limits::max()); 87 | REQUIRE(v.valid()); 88 | REQUIRE(v.is()); 89 | REQUIRE(v.which() == 0); 90 | REQUIRE(v.get() == std::numeric_limits::max()); 91 | v.set(value_type(0)); 92 | REQUIRE(v.get() == value_type(0)); 93 | v = value_type(1); 94 | REQUIRE(v == mapbox::util::variant(value_type(1))); 95 | } 96 | SECTION("int8_t") 97 | { 98 | using value_type = std::int8_t; 99 | mapbox::util::variant v(std::numeric_limits::max()); 100 | REQUIRE(v.valid()); 101 | REQUIRE(v.is()); 102 | REQUIRE(v.which() == 0); 103 | REQUIRE(v.get() == std::numeric_limits::max()); 104 | v.set(0); 105 | REQUIRE(v.get() == value_type(0)); 106 | v = value_type(1); 107 | REQUIRE(v == mapbox::util::variant(value_type(1))); 108 | } 109 | SECTION("int16_t") 110 | { 111 | using value_type = std::int16_t; 112 | mapbox::util::variant v(std::numeric_limits::max()); 113 | REQUIRE(v.valid()); 114 | REQUIRE(v.is()); 115 | REQUIRE(v.which() == 0); 116 | REQUIRE(v.get() == std::numeric_limits::max()); 117 | v.set(0); 118 | REQUIRE(v.get() == value_type(0)); 119 | v = value_type(1); 120 | REQUIRE(v == mapbox::util::variant(value_type(1))); 121 | } 122 | SECTION("int32_t") 123 | { 124 | using value_type = std::int32_t; 125 | mapbox::util::variant v(std::numeric_limits::max()); 126 | REQUIRE(v.valid()); 127 | REQUIRE(v.is()); 128 | REQUIRE(v.which() == 0); 129 | REQUIRE(v.get() == std::numeric_limits::max()); 130 | v.set(0); 131 | REQUIRE(v.get() == value_type(0)); 132 | v = value_type(1); 133 | REQUIRE(v == mapbox::util::variant(value_type(1))); 134 | } 135 | SECTION("int64_t") 136 | { 137 | using value_type = std::int64_t; 138 | mapbox::util::variant v(std::numeric_limits::max()); 139 | REQUIRE(v.valid()); 140 | REQUIRE(v.is()); 141 | REQUIRE(v.which() == 0); 142 | REQUIRE(v.get() == std::numeric_limits::max()); 143 | v.set(0); 144 | REQUIRE(v.get() == value_type(0)); 145 | v = value_type(1); 146 | REQUIRE(v == mapbox::util::variant(value_type(1))); 147 | } 148 | } 149 | 150 | struct MissionInteger 151 | { 152 | using value_type = uint64_t; 153 | value_type val_; 154 | 155 | public: 156 | MissionInteger(uint64_t val) : val_(val) {} 157 | 158 | bool operator==(MissionInteger const& rhs) const 159 | { 160 | return (val_ == rhs.get()); 161 | } 162 | 163 | uint64_t get() const 164 | { 165 | return val_; 166 | } 167 | }; 168 | 169 | std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs) 170 | { 171 | os << rhs.get(); 172 | return os; 173 | } 174 | 175 | TEST_CASE("variant should support custom types", "[variant]") 176 | { 177 | // http://www.missionintegers.com/integer/34838300 178 | mapbox::util::variant v(MissionInteger(34838300)); 179 | REQUIRE(v.valid()); 180 | REQUIRE(v.is()); 181 | REQUIRE(v.which() == 0); 182 | REQUIRE(v.get() == MissionInteger(34838300)); 183 | REQUIRE(v.get().get() == MissionInteger::value_type(34838300)); 184 | // TODO: should both of the set usages below compile? 185 | v.set(MissionInteger::value_type(0)); 186 | v.set(MissionInteger(0)); 187 | REQUIRE(v.get().get() == MissionInteger::value_type(0)); 188 | v = MissionInteger(1); 189 | REQUIRE(v == mapbox::util::variant(MissionInteger(1))); 190 | } 191 | 192 | TEST_CASE("variant::which() returns zero based index of stored type", "[variant]") 193 | { 194 | using variant_type = mapbox::util::variant; 195 | // which() returns index in forward order 196 | REQUIRE(0 == variant_type(true).which()); 197 | REQUIRE(1 == variant_type(std::string("test")).which()); 198 | REQUIRE(2 == variant_type(std::uint64_t(0)).which()); 199 | REQUIRE(3 == variant_type(std::int64_t(0)).which()); 200 | REQUIRE(4 == variant_type(double(0.0)).which()); 201 | REQUIRE(5 == variant_type(float(0.0)).which()); 202 | } 203 | 204 | TEST_CASE("get with wrong type (here: double) should throw", "[variant]") 205 | { 206 | using variant_type = mapbox::util::variant; 207 | variant_type var = 5; 208 | REQUIRE(var.is()); 209 | REQUIRE_FALSE(var.is()); 210 | REQUIRE(var.get() == 5); 211 | REQUIRE_THROWS_AS(var.get(), 212 | mapbox::util::bad_variant_access&); 213 | } 214 | 215 | TEST_CASE("get with wrong type (here: int) should throw", "[variant]") 216 | { 217 | using variant_type = mapbox::util::variant; 218 | variant_type var = 5.0; 219 | REQUIRE(var.is()); 220 | REQUIRE_FALSE(var.is()); 221 | REQUIRE(var.get() == 5.0); 222 | REQUIRE(mapbox::util::get(var) == 5.0); 223 | REQUIRE_THROWS_AS(var.get(), 224 | mapbox::util::bad_variant_access&); 225 | REQUIRE_THROWS_AS(mapbox::util::get(var), 226 | mapbox::util::bad_variant_access&); 227 | } 228 | 229 | TEST_CASE("get on const varint with wrong type (here: int) should throw", "[variant]") 230 | { 231 | using variant_type = mapbox::util::variant; 232 | const variant_type var = 5.0; 233 | REQUIRE(var.is()); 234 | REQUIRE_FALSE(var.is()); 235 | REQUIRE(var.get() == 5.0); 236 | REQUIRE(mapbox::util::get(var) == 5.0); 237 | REQUIRE_THROWS_AS(var.get(), 238 | mapbox::util::bad_variant_access&); 239 | REQUIRE_THROWS_AS(mapbox::util::get(var), 240 | mapbox::util::bad_variant_access&); 241 | } 242 | 243 | TEST_CASE("get with any type should throw if not initialized", "[variant]") 244 | { 245 | mapbox::util::variant var{mapbox::util::no_init()}; 246 | REQUIRE_THROWS_AS(var.get(), 247 | mapbox::util::bad_variant_access&); 248 | REQUIRE_THROWS_AS(var.get(), 249 | mapbox::util::bad_variant_access&); 250 | } 251 | 252 | TEST_CASE("no_init variant can be copied and moved from", "[variant]") 253 | { 254 | using variant_type = mapbox::util::variant; 255 | 256 | variant_type v1{mapbox::util::no_init()}; 257 | variant_type v2{42}; 258 | variant_type v3{23}; 259 | 260 | REQUIRE(v2.get() == 42); 261 | v2 = v1; 262 | REQUIRE_THROWS_AS(v2.get(), 263 | mapbox::util::bad_variant_access&); 264 | 265 | REQUIRE(v3.get() == 23); 266 | v3 = std::move(v1); 267 | REQUIRE_THROWS_AS(v3.get(), 268 | mapbox::util::bad_variant_access&); 269 | } 270 | 271 | TEST_CASE("no_init variant can be copied and moved to", "[variant]") 272 | { 273 | using variant_type = mapbox::util::variant; 274 | 275 | variant_type v1{42}; 276 | variant_type v2{mapbox::util::no_init()}; 277 | variant_type v3{mapbox::util::no_init()}; 278 | 279 | REQUIRE_THROWS_AS(v2.get(), 280 | mapbox::util::bad_variant_access&); 281 | 282 | REQUIRE(v1.get() == 42); 283 | v2 = v1; 284 | REQUIRE(v2.get() == 42); 285 | REQUIRE(v1.get() == 42); 286 | 287 | REQUIRE_THROWS_AS(v3.get(), 288 | mapbox::util::bad_variant_access&); 289 | 290 | v3 = std::move(v1); 291 | REQUIRE(v3.get() == 42); 292 | } 293 | 294 | TEST_CASE("implicit conversion", "[variant][implicit conversion]") 295 | { 296 | using variant_type = mapbox::util::variant; 297 | variant_type var(5.0); // converted to int 298 | REQUIRE(var.get() == 5); 299 | var = 6.0; // works for operator=, too 300 | REQUIRE(var.get() == 6); 301 | } 302 | 303 | TEST_CASE("implicit conversion to first type in variant type list", "[variant][implicit conversion]") 304 | { 305 | using variant_type = mapbox::util::variant; 306 | variant_type var = 5l; // converted to long 307 | REQUIRE(var.get() == 5); 308 | REQUIRE_THROWS_AS(var.get(), 309 | mapbox::util::bad_variant_access&); 310 | } 311 | 312 | TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]") 313 | { 314 | using variant_type = mapbox::util::variant; 315 | variant_type var = 100.0; 316 | CHECK(var.get() == static_cast(100.0)); 317 | CHECK(var.get() == static_cast(static_cast(100.0))); 318 | } 319 | 320 | struct dummy 321 | { 322 | }; 323 | 324 | TEST_CASE("implicit conversion to a suitable type", "[variant][implicit conversion]") 325 | { 326 | using mapbox::util::variant; 327 | CHECK_NOTHROW((variant(123)).get()); 328 | CHECK_NOTHROW((variant("foo")).get()); 329 | } 330 | 331 | TEST_CASE("value_traits for non-convertible type", "[variant::detail]") 332 | { 333 | namespace detail = mapbox::util::detail; 334 | using target_type = detail::value_traits::target_type; 335 | CHECK((std::is_same::value) == true); 336 | } 337 | 338 | TEST_CASE("Type indexing should work with variants with duplicated types", "[variant::detail]") 339 | { 340 | // Index is in reverse order 341 | REQUIRE((mapbox::util::detail::value_traits::index == 3)); 342 | REQUIRE((mapbox::util::detail::value_traits::index == 3)); 343 | REQUIRE((mapbox::util::detail::value_traits::index == 2)); 344 | REQUIRE((mapbox::util::detail::value_traits::index == 2)); 345 | REQUIRE((mapbox::util::detail::value_traits::index == 1)); 346 | REQUIRE((mapbox::util::detail::value_traits::index == 3)); 347 | REQUIRE((mapbox::util::detail::value_traits::index == 0)); 348 | REQUIRE((mapbox::util::detail::value_traits::index == mapbox::util::detail::invalid_value)); 349 | REQUIRE((mapbox::util::detail::value_traits, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value)); 350 | } 351 | 352 | TEST_CASE("variant default constructor", "[variant][default constructor]") 353 | { 354 | // By default variant is initialised with (default constructed) first type in template parameters pack 355 | // As a result first type in Types... must be default constructable 356 | // NOTE: index in reverse order -> index = N - 1 357 | using variant_type = mapbox::util::variant; 358 | REQUIRE(variant_type{}.which() == 0); 359 | REQUIRE(variant_type{}.valid()); 360 | REQUIRE_FALSE(variant_type{mapbox::util::no_init()}.valid()); 361 | } 362 | 363 | TEST_CASE("variant printer", "[visitor][unary visitor][printer]") 364 | { 365 | using variant_type = mapbox::util::variant; 366 | std::vector var = {2.1, 123, "foo", 456}; 367 | std::stringstream out; 368 | std::copy(var.begin(), var.end(), std::ostream_iterator(out, ",")); 369 | out << var[2]; 370 | REQUIRE(out.str() == "2.1,123,foo,456,foo"); 371 | } 372 | 373 | TEST_CASE("swapping variants should do the right thing", "[variant]") 374 | { 375 | using variant_type = mapbox::util::variant; 376 | variant_type a = 7; 377 | variant_type b = 3; 378 | variant_type c = 3.141; 379 | variant_type d = "foo"; 380 | variant_type e = "a long string that is longer than small string optimization"; 381 | 382 | using std::swap; 383 | swap(a, b); 384 | REQUIRE(a.get() == 3); 385 | REQUIRE(a.which() == 0); 386 | REQUIRE(b.get() == 7); 387 | REQUIRE(b.which() == 0); 388 | 389 | swap(b, c); 390 | REQUIRE(b.get() == Approx(3.141)); 391 | REQUIRE(b.which() == 1); 392 | REQUIRE(c.get() == 7); 393 | REQUIRE(c.which() == 0); 394 | 395 | swap(b, d); 396 | REQUIRE(b.get() == "foo"); 397 | REQUIRE(b.which() == 2); 398 | REQUIRE(d.get() == Approx(3.141)); 399 | REQUIRE(d.which() == 1); 400 | 401 | swap(b, e); 402 | REQUIRE(b.get() == "a long string that is longer than small string optimization"); 403 | REQUIRE(b.which() == 2); 404 | REQUIRE(e.get() == "foo"); 405 | REQUIRE(e.which() == 2); 406 | } 407 | 408 | TEST_CASE("variant should work with equality operators") 409 | { 410 | using variant_type = mapbox::util::variant; 411 | 412 | variant_type a{1}; 413 | variant_type b{1}; 414 | variant_type c{2}; 415 | variant_type s{"foo"}; 416 | 417 | REQUIRE(a == a); 418 | REQUIRE(a == b); 419 | REQUIRE_FALSE(a == c); 420 | REQUIRE_FALSE(a == s); 421 | REQUIRE_FALSE(c == s); 422 | 423 | REQUIRE_FALSE(a != a); 424 | REQUIRE_FALSE(a != b); 425 | REQUIRE(a != c); 426 | REQUIRE(a != s); 427 | REQUIRE(c != s); 428 | } 429 | 430 | TEST_CASE("variant should work with comparison operators") 431 | { 432 | using variant_type = mapbox::util::variant; 433 | 434 | variant_type a{1}; 435 | variant_type b{1}; 436 | variant_type c{2}; 437 | variant_type s{"foo"}; 438 | variant_type t{"bar"}; 439 | 440 | REQUIRE_FALSE(a < a); 441 | REQUIRE_FALSE(a < b); 442 | REQUIRE(a < c); 443 | REQUIRE(a < s); 444 | REQUIRE(c < s); 445 | REQUIRE(t < s); 446 | 447 | REQUIRE_FALSE(a > a); 448 | REQUIRE_FALSE(a > b); 449 | REQUIRE_FALSE(a > c); 450 | REQUIRE_FALSE(a > s); 451 | REQUIRE_FALSE(c > s); 452 | REQUIRE_FALSE(t > s); 453 | 454 | REQUIRE(a <= a); 455 | REQUIRE(a <= b); 456 | REQUIRE(a <= c); 457 | REQUIRE(a <= s); 458 | REQUIRE(c <= s); 459 | REQUIRE(t <= s); 460 | 461 | REQUIRE(a >= a); 462 | REQUIRE(a >= b); 463 | REQUIRE_FALSE(a >= c); 464 | REQUIRE_FALSE(a >= s); 465 | REQUIRE_FALSE(c >= s); 466 | REQUIRE_FALSE(t >= s); 467 | } 468 | 469 | TEST_CASE("storing reference wrappers works") 470 | { 471 | using variant_type = mapbox::util::variant, std::reference_wrapper>; 472 | 473 | int a = 1; 474 | variant_type v{std::ref(a)}; 475 | REQUIRE(v.get() == 1); 476 | REQUIRE(mapbox::util::get(v) == 1); 477 | REQUIRE_THROWS_AS(v.get(), 478 | mapbox::util::bad_variant_access&); 479 | REQUIRE_THROWS_AS(mapbox::util::get(v), 480 | mapbox::util::bad_variant_access&); 481 | a = 2; 482 | REQUIRE(v.get() == 2); 483 | v.get() = 3; 484 | REQUIRE(a == 3); 485 | 486 | double b = 3.141; 487 | v = std::ref(b); 488 | REQUIRE(v.get() == Approx(3.141)); 489 | REQUIRE(mapbox::util::get(v) == Approx(3.141)); 490 | REQUIRE_THROWS_AS(v.get(), 491 | mapbox::util::bad_variant_access&); 492 | REQUIRE_THROWS_AS(mapbox::util::get(v), 493 | mapbox::util::bad_variant_access&); 494 | b = 2.718; 495 | REQUIRE(v.get() == Approx(2.718)); 496 | a = 3; 497 | REQUIRE(v.get() == Approx(2.718)); 498 | v.get() = 4.1; 499 | REQUIRE(b == Approx(4.1)); 500 | 501 | REQUIRE_THROWS_AS(v.get() = 4, 502 | mapbox::util::bad_variant_access&); 503 | } 504 | 505 | TEST_CASE("storing reference wrappers to consts works") 506 | { 507 | using variant_type = mapbox::util::variant, std::reference_wrapper>; 508 | 509 | int a = 1; 510 | variant_type v{std::cref(a)}; 511 | REQUIRE(v.get() == 1); 512 | REQUIRE(v.get() == 1); 513 | REQUIRE(mapbox::util::get(v) == 1); 514 | REQUIRE(mapbox::util::get(v) == 1); 515 | REQUIRE_THROWS_AS(v.get(), 516 | mapbox::util::bad_variant_access&); 517 | REQUIRE_THROWS_AS(mapbox::util::get(v), 518 | mapbox::util::bad_variant_access&); 519 | 520 | double b = 3.141; 521 | v = std::cref(b); 522 | REQUIRE(v.get() == Approx(3.141)); 523 | REQUIRE(mapbox::util::get(v) == Approx(3.141)); 524 | REQUIRE_THROWS_AS(v.get(), 525 | mapbox::util::bad_variant_access&); 526 | REQUIRE_THROWS_AS(mapbox::util::get(v), 527 | mapbox::util::bad_variant_access&); 528 | } 529 | 530 | TEST_CASE("recursive wrapper") 531 | { 532 | using variant_type = mapbox::util::variant>; 533 | variant_type v(1); 534 | REQUIRE(v.is()); 535 | REQUIRE(v.get() == 1); 536 | } 537 | 538 | 539 | TEST_CASE("variant : direct_type helper should match T, references (T&) and const references (T const&) to the original type T)") 540 | { 541 | using value = mapbox::util::variant; 542 | 543 | std::uint64_t u(1234); 544 | REQUIRE(value(u).is()); // matches T 545 | 546 | std::uint64_t& ur(u); 547 | REQUIRE(value(ur).is()); // matches T& 548 | 549 | std::uint64_t const& ucr(u); 550 | REQUIRE(value(ucr).is()); // matches T const& 551 | } 552 | -------------------------------------------------------------------------------- /test/t/variant_alternative.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | TEST_CASE("variant_alternative", "[types]") 9 | { 10 | using variant_type = mapbox::util::variant; 11 | using type_0 = mapbox::util::variant_alternative<0, variant_type>::type; 12 | using type_1 = mapbox::util::variant_alternative<1, variant_type>::type; 13 | using type_2 = mapbox::util::variant_alternative<2, variant_type>::type; 14 | //using type_3 = mapbox::util::variant_alternative<3, variant_type>::type; // compile error 15 | constexpr bool check_0 = std::is_same::value; 16 | constexpr bool check_1 = std::is_same::value; 17 | constexpr bool check_2 = std::is_same::value; 18 | CHECK(check_0); 19 | CHECK(check_1); 20 | CHECK(check_2); 21 | } 22 | 23 | TEST_CASE("variant_size", "[types]") 24 | { 25 | constexpr auto value_0 = mapbox::util::variant_size>::value; 26 | constexpr auto value_1 = mapbox::util::variant_size>::value; 27 | constexpr auto value_2 = mapbox::util::variant_size>::value; 28 | CHECK(value_0 == 0); 29 | CHECK(value_1 == 1); 30 | CHECK(value_2 == 2); 31 | } 32 | -------------------------------------------------------------------------------- /test/t/visitor_result_type.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace mapbox::util; 4 | 5 | namespace { 6 | 7 | template 8 | struct tag {}; 9 | 10 | struct deduced_result_visitor 11 | { 12 | template 13 | tag operator() (T); 14 | 15 | template 16 | tag operator() (T) const; 17 | 18 | template 19 | tag operator() (T, U); 20 | 21 | template 22 | tag operator() (T, U) const; 23 | }; 24 | 25 | struct explicit_result_visitor : deduced_result_visitor 26 | { 27 | using result_type = tag; 28 | }; 29 | 30 | // Doing this compile-time test via assignment to typed tag objects gives 31 | // more useful error messages when something goes wrong, than std::is_same 32 | // in a static_assert would. Here if result_of_unary_visit returns anything 33 | // other than the expected type on the left hand side, the conversion error 34 | // message will tell you exactly what it was. 35 | 36 | #ifdef __clang__ 37 | # pragma clang diagnostic ignored "-Wunused-variable" 38 | #endif 39 | 40 | tag d1m = detail::result_of_unary_visit{}; 41 | tag d1c = detail::result_of_unary_visit{}; 42 | 43 | tag e1m = detail::result_of_unary_visit{}; 44 | tag e1c = detail::result_of_unary_visit{}; 45 | 46 | tag d2m = detail::result_of_binary_visit{}; 47 | tag d2c = detail::result_of_binary_visit{}; 48 | 49 | tag e2m = detail::result_of_binary_visit{}; 50 | tag e2c = detail::result_of_binary_visit{}; 51 | 52 | } // namespace 53 | -------------------------------------------------------------------------------- /test/unique_ptr_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "auto_cpu_timer.hpp" 10 | 11 | #include 12 | 13 | using namespace mapbox; 14 | 15 | namespace test { 16 | 17 | struct add; 18 | struct sub; 19 | 20 | template 21 | struct binary_op; 22 | 23 | typedef util::variant>, 25 | std::unique_ptr>> 26 | expression; 27 | 28 | template 29 | struct binary_op 30 | { 31 | expression left; // variant instantiated here... 32 | expression right; 33 | 34 | binary_op(expression&& lhs, expression&& rhs) 35 | : left(std::move(lhs)), right(std::move(rhs)) 36 | { 37 | } 38 | }; 39 | 40 | struct print 41 | { 42 | template 43 | void operator()(T const& val) const 44 | { 45 | std::cerr << val << ":" << typeid(T).name() << std::endl; 46 | } 47 | }; 48 | 49 | struct test 50 | { 51 | template 52 | std::string operator()(T const& obj) const 53 | { 54 | return std::string("TYPE_ID=") + typeid(obj).name(); 55 | } 56 | }; 57 | 58 | struct calculator 59 | { 60 | public: 61 | int operator()(int value) const 62 | { 63 | return value; 64 | } 65 | 66 | int operator()(std::unique_ptr> const& binary) const 67 | { 68 | return util::apply_visitor(calculator(), binary->left) + util::apply_visitor(calculator(), binary->right); 69 | } 70 | 71 | int operator()(std::unique_ptr> const& binary) const 72 | { 73 | return util::apply_visitor(calculator(), binary->left) - util::apply_visitor(calculator(), binary->right); 74 | } 75 | }; 76 | 77 | struct to_string 78 | { 79 | public: 80 | std::string operator()(int value) const 81 | { 82 | return std::to_string(value); 83 | } 84 | 85 | std::string operator()(std::unique_ptr> const& binary) const 86 | { 87 | return util::apply_visitor(to_string(), binary->left) + std::string("+") + util::apply_visitor(to_string(), binary->right); 88 | } 89 | 90 | std::string operator()(std::unique_ptr> const& binary) const 91 | { 92 | return util::apply_visitor(to_string(), binary->left) + std::string("-") + util::apply_visitor(to_string(), binary->right); 93 | } 94 | }; 95 | 96 | } // namespace test 97 | 98 | int main(int argc, char** argv) 99 | { 100 | if (argc != 2) 101 | { 102 | std::cerr << "Usage" << argv[0] << " " << std::endl; 103 | return EXIT_FAILURE; 104 | } 105 | 106 | const std::size_t NUM_ITER = static_cast(std::stol(argv[1])); 107 | 108 | test::expression sum(std::unique_ptr>(new test::binary_op(2, 3))); 109 | test::expression result(std::unique_ptr>(new test::binary_op(std::move(sum), 4))); 110 | 111 | std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; 112 | 113 | int total = 0; 114 | { 115 | auto_cpu_timer t; 116 | for (std::size_t i = 0; i < NUM_ITER; ++i) 117 | { 118 | total += util::apply_visitor(test::calculator(), result); 119 | } 120 | } 121 | std::cerr << "total=" << total << std::endl; 122 | 123 | std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; 124 | 125 | return EXIT_SUCCESS; 126 | } 127 | -------------------------------------------------------------------------------- /test/unit.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | -------------------------------------------------------------------------------- /variant.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "includes": [ 3 | "common.gypi" 4 | ], 5 | "targets": [ 6 | { 7 | "target_name": "tests", 8 | "type": "executable", 9 | "sources": [ 10 | "test/unit.cpp", 11 | "test/t/binary_visitor_1.cpp", 12 | "test/t/binary_visitor_2.cpp", 13 | "test/t/binary_visitor_3.cpp", 14 | "test/t/binary_visitor_4.cpp", 15 | "test/t/binary_visitor_5.cpp", 16 | "test/t/binary_visitor_6.cpp", 17 | "test/t/issue21.cpp", 18 | "test/t/mutating_visitor.cpp", 19 | "test/t/optional.cpp", 20 | "test/t/recursive_wrapper.cpp", 21 | "test/t/sizeof.cpp", 22 | "test/t/unary_visitor.cpp", 23 | "test/t/variant.cpp" 24 | ], 25 | "xcode_settings": { 26 | "SDKROOT": "macosx", 27 | "SUPPORTED_PLATFORMS":["macosx"] 28 | }, 29 | "include_dirs": [ 30 | "./include", 31 | "test/include" 32 | ] 33 | } 34 | ] 35 | } 36 | --------------------------------------------------------------------------------