├── .github └── recursive-variant-authority.png ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── config.cmake.in └── helper.cmake ├── include └── rva │ └── variant.hpp └── test ├── test_replace.cpp ├── test_rva.cpp └── test_variant.cpp /.github/recursive-variant-authority.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeinred/recursive-variant/9159bc55de7c2e08bc03ebaedbf393653771f4ad/.github/recursive-variant-authority.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .ccls-cache/ 3 | 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project( 4 | "rva" 5 | VERSION 0.9.5 6 | DESCRIPTION "A Simple Library for Recursive Variant Types" 7 | HOMEPAGE_URL "https://github.com/codeinred/recursive-variant/" 8 | LANGUAGES CXX 9 | ) 10 | 11 | add_library(rva INTERFACE) 12 | add_library(rva::rva ALIAS rva) 13 | 14 | # For ${CMAKE_INSTALL_} variables that are standarized 15 | include(GNUInstallDirs) 16 | 17 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | 20 | target_compile_features( 21 | rva 22 | INTERFACE 23 | cxx_std_20 24 | ) 25 | 26 | target_include_directories( 27 | rva 28 | INTERFACE 29 | $ 30 | $ 31 | ) 32 | 33 | # Installation 34 | # See: https://dominikberner.ch/cmake-interface-lib/ 35 | 36 | install( 37 | TARGETS rva 38 | EXPORT ${PROJECT_NAME}_Targets 39 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 40 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 41 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 42 | ) 43 | 44 | include(CMakePackageConfigHelpers) 45 | write_basic_package_version_file( 46 | "${PROJECT_NAME}ConfigVersion.cmake" 47 | VERSION ${PROJECT_VERSION} 48 | COMPATIBILITY SameMajorVersion 49 | ) 50 | 51 | configure_package_config_file( 52 | "${PROJECT_SOURCE_DIR}/cmake/config.cmake.in" 53 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 54 | INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 55 | ) 56 | 57 | install( 58 | EXPORT ${PROJECT_NAME}_Targets 59 | FILE ${PROJECT_NAME}Targets.cmake 60 | NAMESPACE rva:: 61 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 62 | ) 63 | 64 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 65 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 66 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 67 | ) 68 | 69 | install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/rva 70 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 71 | ) 72 | 73 | 74 | include(helper) 75 | 76 | option( 77 | BUILD_TESTING 78 | "Build testing for RVA" 79 | ON) 80 | 81 | if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING) 82 | include(CTest) 83 | 84 | find_or_fetch( 85 | Catch2 86 | https://github.com/catchorg/catch2.git 87 | devel 88 | 3.0.0) 89 | FetchContent_MakeAvailable(${remote_dependencies}) 90 | 91 | add_executable( 92 | test_rva 93 | test/test_rva.cpp) 94 | target_link_libraries( 95 | test_rva 96 | rva::rva 97 | Catch2::Catch2WithMain) 98 | 99 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/lib/cmake/Catch2) 100 | list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) 101 | include(CTest) 102 | include(Catch) 103 | catch_discover_tests(test_rva) 104 | endif() 105 | 106 | 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | © 2021 Alecto Irene Perez 4 | 5 | Permission is hereby granted, free of charge, to any person or organization 6 | obtaining a copy of the software and accompanying documentation covered by this 7 | license (the "Software") to use, reproduce, display, distribute, execute, and 8 | transmit the Software, and to prepare derivative works of the Software, and to 9 | permit third-parties to whom the Software is furnished to do so, all subject to 10 | the following: 11 | 12 | The copyright notices in the Software and this entire statement, including the 13 | above license grant, this restriction and the following disclaimer, must be 14 | included in all copies of the Software, in whole or in part, and all derivative 15 | works of the Software, especially those created in whole or in part by Deep 16 | Neural Networks, Language Models, or other such programs advertised as "AI" or 17 | as "Artificial Intelligence" or as "Machine Learning", either with or without 18 | human input or intervention, unless such copies or derivative works are solely 19 | in the form of machine-executable object code generated by a source language 20 | processor. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 24 | FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 25 | COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES 26 | OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 27 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `rva::variant` — Recursive Sum Types for C++ 2 | 3 | _Provided by the Recursive Variant Authority. We stand united in opposition to 4 | the [TVA](https://youtu.be/nW948Va-l10). May your variants never be pruned._ 5 | 6 | Variants are exceedingly useful in C++, but they suffer from a singular and 7 | fundamental shortcoming: unlike sum types in many other languages, there's no 8 | mechanism to define recursive variants in C++. 9 | 10 | The Recursive Variant Authority provides a solution in the form of 11 | `rva::variant`, which allows you to write arbitrary recursive sum types with 12 | functionality identical to that provided by `std::variant`. 13 | 14 | ![](.github/recursive-variant-authority.png) 15 | 16 | ## Understanding Recursive Variants 17 | 18 | To understand what a recursive variant is, and why it's useful, consider 19 | something like a value in json. It can be any of the following: 20 | 21 | - A null value 22 | - A string 23 | - A number (usually represented as a double) 24 | - A boolean value (either true or false), 25 | - An object (aka, a map between strings and json values) 26 | - An array of values. 27 | 28 | In a language like haskell, we could copy that definition pretty much verbatim 29 | (accounting for syntax, of course): 30 | 31 | ```hs 32 | data JsonValue = JsonNull 33 | | JsonBoolean Bool 34 | | JsonNumber Double 35 | | JsonStr String 36 | | JsonObject (Map String JsonValue) 37 | | JsonArray [JsonValue] 38 | ``` 39 | 40 | It's important to note that `JsonValue` is being used in it's own definition: 41 | it's a recursive type. 42 | 43 | ## Recursive types in C++ 44 | 45 | While this may seem a bit strange at first, recursive types aren't all that 46 | unusual. C++ has them too, and it's common for a type to store pointers to 47 | itself in situations such as a binary tree or a linked list. We can even define 48 | `json_value` as a recursive type using a union: 49 | 50 | ```cpp 51 | class json_value { 52 | int state = -1; 53 | union { 54 | /* state 0 */ std::nullptr_t m_null; 55 | /* state 1 */ bool m_bool; 56 | /* state 2 */ double m_number; 57 | /* state 3 */ std::string m_string; 58 | /* state 4 */ std::map m_object; 59 | /* state 5 */ std::vector m_array; 60 | }; 61 | 62 | public: 63 | json_value(std::nullptr_t) : state(0), m_null() {} 64 | json_value(bool b) : state(1), m_bool(b) {} 65 | json_value(double d) : state(2), m_number(d) {} 66 | json_value(std::string const& str) : state(3), m_string(str) {} 67 | json_value(std::map const& map) : state(4), m_object(map) {} 68 | json_value(std::vector const& arr) : state(5), m_object(m_array) {} 69 | 70 | // Now we need to write move constructors... 71 | json_value(std::string&& str) : state(3), m_string(std::move(str)) {} 72 | json_value(std::map&& map) : state(4), m_object(std::move(map)) {} 73 | json_value(std::vector&& arr) : state(5), m_object(std::move(m_array)) {} 74 | 75 | // Now assignment operators... 76 | 77 | // Now comparison operators... 78 | 79 | // .emplace() might be nice 80 | 81 | // Oh hey did we forget copy and move assignment for json_value? 82 | 83 | // maybe we should have a way to check what the index of the currently active element is 84 | }; 85 | ``` 86 | 87 | ## `std::variant` is only a partial solution 88 | 89 | It quickly becomes apparent that the class we're writing is essentially a 90 | specialization of `std::variant`, which (thankfully) simplifies a lot of the 91 | code: 92 | 93 | ```cpp 94 | class json_value { 95 | std::variant< 96 | std::nullptr_t, 97 | bool, 98 | double, 99 | std::string, 100 | std::map, 101 | std::vector> value; 102 | 103 | public: 104 | json_value() = default; 105 | json_value(json_value const&) = default; 106 | json_value(json_value&&) = default; 107 | template 108 | json_value(T&& object) : value(std::forward(object)) {} 109 | 110 | 111 | // Now assignment operators... 112 | 113 | // Now comparison operators... 114 | 115 | // .emplace() might be nice 116 | 117 | // maybe we should have a way to check what the index of the currently active element is 118 | }; 119 | ``` 120 | 121 | This is better, and signifigantly less bug prone, but there's still a lot of 122 | boilerplate code that needs to be written. 123 | 124 | Here's the thing: at it's core, some types (like json_value) are best expressed 125 | as sum types! Anything we write to wrap one will just be a specialization of a 126 | sum type, and will usually involve a lot of boilerplate code to do the wrapping. 127 | 128 | At this point, it might be prudent to ask: can we define `json_value` directly 129 | as a `std::variant`? Would a recursive `using` declaration work? I wish it did. 130 | I really, really wish it did. 131 | 132 | ```cpp 133 | // LIES!!! This code doesn't work 134 | // error: use of undeclared identifier 'json_value' 135 | using json_value = std::variant< 136 | std::nullptr_t, // json null 137 | bool, // json boolean 138 | double, // json number 139 | std::string, // json string 140 | std::map, // json object 141 | std::vector>; // json array 142 | ``` 143 | 144 | ## The Recursive Variant Authority provides a complete solution! 145 | 146 | `rva::variant` allows you to write recursive variants _without any boilerplate_ 147 | by passing `rva::self_t` in the places where you want your recursive type to go! 148 | When you write a statement like this, `rva::variant` will replace instances of 149 | `rva::self_t` with a properly specified instance of it's type, and it does this 150 | as a paper-thin wrapper over `std::variant` that provides all your boilerplate 151 | code for you. And there was a _lot_ of boilerplate code to write. 152 | 153 | ```cpp 154 | using json_value = rva::variant< 155 | std::nullptr_t, // json null 156 | bool, // json boolean 157 | double, // json number 158 | std::string, // json string 159 | std::map, // json object, type is std::map 160 | std::vector>; // json array, type is std::vector 161 | ``` 162 | 163 | `rva::variant` provides the full set of functionality given by `std::variant`, 164 | including: 165 | 166 | - `visit`, 167 | - `get`, 168 | - `get_if`, 169 | - `holds_alternative`, 170 | - `std::variant_size`, 171 | - `std::variant_alternative`, 172 | - `std::hash`) 173 | 174 | And it does so as a drop-in replacement (or as close to one as the standard 175 | would allow)! 176 | 177 | The Recursive Variant Authority hopes that you enjoy your time using 178 | `rva::variant`, and we encourage you to make full use of it's capabilities when 179 | writing your code. 180 | 181 | ## Usage & Installation 182 | 183 | The most straight-forward way to use `rva::variant` is by using 184 | [CMake's FetchContent interface](https://cmake.org/cmake/help/v3.21/module/FetchContent.html) 185 | to find and fetch the library: 186 | 187 | ```cmake 188 | FetchContent_Declare( 189 | rva 190 | GIT_REPOSITORY https://github.com/codeinred/recursive-variant.git 191 | GIT_TAG main 192 | ) 193 | FetchContent_MakeAvailable(rva) 194 | ``` 195 | 196 | Alternatively, you can install it as a CMake package like so. _Please note that 197 | it's not necessary to build it in release mode, as it's a header-only library._ 198 | 199 | ```bash 200 | git clone https://github.com/codeinred/recursive-variant.git rva 201 | cd rva 202 | cmake -B build -DBUILD_TESTING=OFF 203 | cmake --build build 204 | sudo cmake --install build 205 | ``` 206 | 207 | Once installed, the library can then be discovered as a CMake package: 208 | 209 | ```cmake 210 | find_package(rva REQUIRED) 211 | ``` 212 | 213 | In either case, whether obtained via `FetchContent`, or installed as a package, 214 | you can use it via `target_link_libraries`. The library is header-only, but this 215 | will ensure that it's added to the include path for that target. 216 | 217 | ```cmake 218 | target_link_libraries( PRIVATE rva::rva) 219 | ``` 220 | 221 | ### Running tests 222 | 223 | You may have noticed that the installation process "built" the library with 224 | testing off. Installing the library doesn't require running tests, however if 225 | you wish to run them, you may do so by ommitting the flag disabling testing, and 226 | then running `build/test_rva -s`. Testing is done via the 227 | [Catch2 Testing framework](https://github.com/catchorg/Catch2). You will see a 228 | lot of `{?}` in the test output, but that's just because Catch2 doesn't know how 229 | to print a variant. 230 | 231 | Tests may be found in the `test/` directory. 232 | 233 | ```bash 234 | git clone https://github.com/codeinred/recursive-variant.git rva 235 | cd rva 236 | cmake -B build 237 | cmake --build build -j 8 238 | build/test_rva -s 239 | ``` 240 | -------------------------------------------------------------------------------- /cmake/config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 4 | check_required_components("@PROJECT_NAME@") 5 | -------------------------------------------------------------------------------- /cmake/helper.cmake: -------------------------------------------------------------------------------- 1 | ################################################ 2 | ## Defaults, Definitions and helper functions ## 3 | ################################################ 4 | 5 | # PROJECT_IS_TOP_LEVEL is a variable added in CMake 3.21 that checks if the 6 | # current project is the top-level project. This checks if it's built in, 7 | # and if not, adds a definition for it. 8 | if("${CMAKE_VERSION}" VERSION_LESS "3.21.0") 9 | if("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}") 10 | set(PROJECT_IS_TOP_LEVEL ON) 11 | else() 12 | set(PROJECT_IS_TOP_LEVEL OFF) 13 | endif() 14 | endif() 15 | 16 | # Defines some useful constants representing terminal codes to print things 17 | # in color. 18 | if(NOT WIN32) 19 | string(ASCII 27 Esc) 20 | set(ColorReset "${Esc}[m") 21 | set(ColorBold "${Esc}[1m") 22 | set(Red "${Esc}[31m") 23 | set(Green "${Esc}[32m") 24 | set(Yellow "${Esc}[33m") 25 | set(Blue "${Esc}[34m") 26 | set(Magenta "${Esc}[35m") 27 | set(Cyan "${Esc}[36m") 28 | set(White "${Esc}[37m") 29 | set(BoldRed "${Esc}[1;31m") 30 | set(BoldGreen "${Esc}[1;32m") 31 | set(BoldYellow "${Esc}[1;33m") 32 | set(BoldBlue "${Esc}[1;34m") 33 | set(BoldMagenta "${Esc}[1;35m") 34 | set(BoldCyan "${Esc}[1;36m") 35 | set(BoldWhite "${Esc}[1;37m") 36 | endif() 37 | 38 | # Define a function 'note' that prints a message in bold cyan 39 | function(note msg) 40 | message("🐈 ${BoldCyan}says: ${msg}${ColorReset}") 41 | endfunction() 42 | 43 | #################################################### 44 | ## Sec. 2: Dependency Management via FetchContent ## 45 | #################################################### 46 | 47 | set(remote_dependencies "") 48 | 49 | # If ALWAYS_FETCH is ON, then find_or_fetch will always fetch any remote 50 | # dependencies rather than using the ones provided by the system. This is 51 | # useful for creating a static executable. 52 | option( 53 | ALWAYS_FETCH 54 | "Tells find_or_fetch to always fetch packages" 55 | OFF) 56 | 57 | 58 | include(FetchContent) 59 | # find_or_fetch will search for a system installation of ${package} via 60 | # find_package. If it fails to find one, it'll use FetchContent to download and 61 | # build it locally. 62 | function(find_or_fetch package repo tag) 63 | if (NOT ALWAYS_FETCH) 64 | find_package(${package} ${ARGN} QUIET) 65 | endif() 66 | 67 | if (ALWAYS_FETCH OR NOT ${${package}_FOUND}) 68 | note("Fetching dependency '${package}' from ${repo}") 69 | include(FetchContent) 70 | FetchContent_Declare( 71 | "${package}" 72 | GIT_REPOSITORY "${repo}" 73 | GIT_TAG "${tag}" 74 | ) 75 | list(APPEND remote_dependencies "${package}") 76 | set (remote_dependencies ${remote_dependencies} PARENT_SCOPE) 77 | else() 78 | note("Using system cmake package for dependency '${package}'") 79 | endif() 80 | endfunction() 81 | 82 | function(always_fetch package repo tag) 83 | note("Fetching dependency '${package}' from ${repo}") 84 | include(FetchContent) 85 | FetchContent_Declare( 86 | "${package}" 87 | GIT_REPOSITORY "${repo}" 88 | GIT_TAG "${tag}" 89 | ) 90 | list(APPEND remote_dependencies "${package}") 91 | set (remote_dependencies ${remote_dependencies} PARENT_SCOPE) 92 | endfunction() 93 | 94 | ##################################################################### 95 | ## Sec. 3: Convinience Functions to add targets more automatically ## 96 | ##################################################################### 97 | 98 | 99 | # Adds every top-level .cpp file in the given directory as an executable. Arguments 100 | # provided after the directory name are interpreted as libraries, and it'll link 101 | # targets in that directory against those libraries. 102 | function(add_source_dir dir) 103 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") 104 | file(GLOB all_targets "${dir}/*.cpp") 105 | string(REPLACE ";" ", " library_list "${ARGN}") 106 | foreach(filename ${all_targets}) 107 | get_filename_component(target ${filename} NAME_WLE) 108 | note("Adding '${target}' from ${dir}/${target}.cpp with libraries ${library_list}") 109 | add_executable("${target}" "${filename}") 110 | target_link_libraries("${target}" PRIVATE ${ARGN}) 111 | endforeach() 112 | else() 113 | note("add_source_dir: Skipping ${dir}. Directory not found.") 114 | endif() 115 | endfunction() 116 | 117 | # Adds every top-level .cpp file in the given directory as an executable. Arguments 118 | # provided after the directory name are interpreted as libraries, and it'll link 119 | # targets in that directory against those libraries. Each target will also be 120 | # registered as a test via CTest 121 | function(add_test_dir dir) 122 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") 123 | file(GLOB all_targets "${dir}/*.cpp") 124 | string(REPLACE ";" ", " library_list "${ARGN}") 125 | foreach(filename ${all_targets}) 126 | get_filename_component(target ${filename} NAME_WLE) 127 | # Tests are named test_{name of test} 128 | set(target test_${target}) 129 | note("Adding test '${target}' from ${dir}/${target}.cpp with libraries ${library_list}") 130 | add_executable("${target}" "${filename}") 131 | target_link_libraries("${target}" PRIVATE ${ARGN}) 132 | add_test(NAME "${target}" COMMAND "${target}") 133 | endforeach() 134 | else() 135 | note("add_test_dir: Skipping ${dir}. Directory not found.") 136 | endif() 137 | endfunction() 138 | 139 | 140 | # Targets C++20 for a given target. also adds additional compiler options 141 | # in order to ensure greater levels of compatibility. 142 | function(target_cpp_20 target_name) 143 | target_compile_features(${target_name} INTERFACE cxx_std_20) 144 | 145 | # The /EHa flag enables standard C++ stack unwinding 146 | # See: https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=msvc-160 147 | if (MSVC) 148 | target_compile_options(${target_name} INTERFACE "/EHa") 149 | endif() 150 | 151 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 152 | # This definition is needed b/c the header needs it in order 153 | # to work on clang 154 | target_compile_definitions(${target_name} INTERFACE __cpp_impl_coroutine=1) 155 | endif() 156 | 157 | # Enables GCC support for coroutines (these are standard C++ now but GCC still 158 | # requires a flag for them) 159 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 160 | target_compile_options(${target_name} INTERFACE "-fcoroutines") 161 | endif() 162 | endfunction() 163 | 164 | 165 | function(add_submodules libname dir) 166 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") 167 | file(GLOB all_modules "${dir}/*") 168 | foreach(module_dir ${all_modules}) 169 | get_filename_component(module ${module_dir} NAME) 170 | note("Linked ${module} @ ${dir}/${module}") 171 | add_subdirectory("${dir}/${module}") 172 | target_link_libraries(${libname} INTERFACE ${module}) 173 | endforeach() 174 | endif() 175 | endfunction() 176 | -------------------------------------------------------------------------------- /include/rva/variant.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RECURSIVE_VARIANT_AUTHORITY_VARIANT_HPP 2 | #define RECURSIVE_VARIANT_AUTHORITY_VARIANT_HPP 3 | #include 4 | #include 5 | 6 | namespace rva { 7 | /** 8 | * @brief replace is a template type used to implement replace_t. It provides 9 | * member, a using declaration named `type`. 10 | * 11 | * @tparam T the type to transform. 12 | * @tparam Find the type to find 13 | * @tparam Replace the type to replace it with. 14 | */ 15 | template 16 | struct replace; 17 | /** 18 | * @brief replace is a template that takes a type T (which itself might be a 19 | * template), and replaces all instances of *Find* with *Replace*. For example: 20 | * 21 | * - `replace_t` -> `int` 22 | * - `replace_t, char, int>` -> `std::vector` 23 | * 24 | * @tparam T the type to transform. 25 | * @tparam Find the type to find 26 | * @tparam Replace the type to replace it with. 27 | */ 28 | template 29 | using replace_t = typename replace::type; 30 | 31 | struct self_t {}; 32 | 33 | // See: https://en.cppreference.com/w/cpp/utility/variant 34 | template 35 | class variant : public std::variant>...> { 36 | public: 37 | using base_type = std::variant>...>; 38 | constexpr static bool 39 | nothrow_swappable = std::is_nothrow_swappable_v; 40 | 41 | using base_type::base_type; 42 | 43 | // Observers 44 | using base_type::index; 45 | using base_type::valueless_by_exception; 46 | 47 | // Modifiers 48 | using base_type::operator=; 49 | using base_type::emplace; 50 | using base_type::swap; 51 | 52 | variant() = default; 53 | variant(variant const&) = default; 54 | variant(variant&&) = default; 55 | variant& operator=(variant const&) = default; 56 | variant& operator=(variant&&) = default; 57 | 58 | constexpr void swap(variant& other) noexcept(nothrow_swappable) { 59 | base_type::swap(other); 60 | } 61 | constexpr base_type& get_base() & noexcept { return *this; } 62 | constexpr base_type const& get_base() const& noexcept { return *this; } 63 | constexpr base_type&& get_base() && noexcept { return *this; } 64 | constexpr base_type const&& get_base() const&& noexcept { return *this; } 65 | 66 | constexpr base_type* get_pointer_to_base() noexcept { return this; } 67 | constexpr base_type const* get_pointer_to_base() const noexcept { 68 | return this; 69 | } 70 | 71 | auto operator<=>(variant const&) const = default; 72 | bool operator==(variant const&) const = default; 73 | }; 74 | 75 | // See: https://en.cppreference.com/w/cpp/utility/variant/visit 76 | template 77 | constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants) { 78 | return std::visit( 79 | std::forward(visitor), 80 | std::forward(variants).get_base()...); 81 | } 82 | template 83 | constexpr R visit(Visitor&& visitor, Variants&&... variants) { 84 | return std::visit( 85 | std::forward(visitor), 86 | std::forward(variants).get_base()...); 87 | } 88 | 89 | // See: https://en.cppreference.com/w/cpp/utility/variant/get 90 | template 91 | constexpr decltype(auto) get(rva::variant& v) { 92 | return std::get(std::forward(v).get_base()); 93 | } 94 | template 95 | constexpr decltype(auto) get(rva::variant&& v) { 96 | return std::get(std::forward(v).get_base()); 97 | } 98 | template 99 | constexpr decltype(auto) get(const rva::variant& v) { 100 | return std::get(std::forward(v).get_base()); 101 | } 102 | template 103 | constexpr decltype(auto) get(const rva::variant&& v) { 104 | return std::get(std::forward(v).get_base()); 105 | } 106 | template 107 | constexpr T& get(rva::variant& v) { 108 | return std::get(std::forward(v).get_base()); 109 | } 110 | template 111 | constexpr T&& get(rva::variant&& v) { 112 | return std::get(std::forward(v).get_base()); 113 | } 114 | template 115 | constexpr const T& get(const rva::variant& v) { 116 | return std::get(std::forward(v).get_base()); 117 | } 118 | template 119 | constexpr const T&& get(const rva::variant&& v) { 120 | return std::get(std::forward(v).get_base()); 121 | } 122 | 123 | // See: https://en.cppreference.com/w/cpp/utility/variant/get_if 124 | template 125 | constexpr auto* get_if(rva::variant* pv) noexcept { 126 | return std::get_if(pv->get_pointer_to_base()); 127 | } 128 | template 129 | constexpr auto const* get_if(const rva::variant* pv) noexcept { 130 | return std::get_if(pv->get_pointer_to_base()); 131 | } 132 | template 133 | constexpr auto* get_if(rva::variant* pv) noexcept { 134 | return std::get_if(pv->get_pointer_to_base()); 135 | } 136 | template 137 | constexpr auto const* get_if(const rva::variant* pv) noexcept { 138 | return std::get_if(pv->get_pointer_to_base()); 139 | } 140 | 141 | template 142 | constexpr bool holds_alternative(const rva::variant& v) noexcept { 143 | return std::holds_alternative(v.get_base()); 144 | } 145 | } // namespace rva 146 | 147 | template 148 | struct std::hash> : std::hash> { 149 | using base_type = std::hash>; 150 | using base_type::base_type; 151 | hash() = default; 152 | hash(hash const&) = default; 153 | hash(hash&&) = default; 154 | size_t operator()(rva::variant const& v) const { 155 | return base_type::operator()(v.get_base()); 156 | } 157 | }; 158 | 159 | template 160 | struct std::variant_size> 161 | : std::integral_constant {}; 162 | template 163 | struct std::variant_size> 164 | : std::integral_constant {}; 165 | 166 | template 167 | struct std::variant_alternative> 168 | : std::variant_alternative::base_type> {}; 169 | template 170 | struct std::variant_alternative> 171 | : std::variant_alternative::base_type> {}; 172 | 173 | // Implementation for replace 174 | namespace rva { 175 | template 176 | struct replace { 177 | using type = T; 178 | }; 179 | template 180 | struct replace { 181 | using type = Replace; 182 | }; 183 | template 184 | struct replace { 185 | using type = Replace*; 186 | }; 187 | template 188 | struct replace { 189 | using type = Replace&; 190 | }; 191 | template 192 | struct replace { 193 | using type = Replace&&; 194 | }; 195 | template 196 | struct replace { 197 | using type = Replace[]; 198 | }; 199 | template 200 | struct replace { 201 | using type = Replace[N]; 202 | }; 203 | template 204 | struct replace { 205 | using type = const Replace; 206 | }; 207 | template 208 | struct replace { 209 | using type = const Replace*; 210 | }; 211 | template 212 | struct replace { 213 | using type = const Replace&; 214 | }; 215 | template 216 | struct replace { 217 | using type = const Replace[]; 218 | }; 219 | template 220 | struct replace { 221 | using type = const Replace[N]; 222 | }; 223 | template 224 | struct replace { 225 | using type = replace_t*; 226 | }; 227 | template 228 | struct replace { 229 | using type = replace_t&; 230 | }; 231 | template 232 | struct replace { 233 | using type = replace_t&&; 234 | }; 235 | template 236 | struct replace { 237 | using type = replace_t[]; 238 | }; 239 | template 240 | struct replace { 241 | using type = replace_t[N]; 242 | }; 243 | template 244 | struct replace { 245 | using type = replace_t const; 246 | }; 247 | template 248 | struct replace { 249 | using type = replace_t const*; 250 | }; 251 | template 252 | struct replace { 253 | using type = replace_t const&; 254 | }; 255 | template 256 | struct replace { 257 | using type = replace_t const[]; 258 | }; 259 | template 260 | struct replace { 261 | using type = replace_t const[N]; 262 | }; 263 | 264 | template