Variant - C++17 `std::variant` for C++11/14/17

MPark.Variant

C++17 std::variant for C++11/14/17

release header travis appveyor license godbolt wandbox

Introduction

MPark.Variant is an implementation of C++17 std::variant for C++11/14/17.

Documentation

Integration

Single Header

The single-header branch provides a standalone variant.hpp file for each release. Copy it and #include away!

Submodule

You can add mpark/variant as a submodule to your project.

git submodule add https://github.com/mpark/variant.git 3rdparty/variant

Add the include directory to your include path with -I3rdparty/variant/include then #include the variant.hpp header with #include <mpark/variant.hpp>.

If you use CMake, you can simply use add_subdirectory(3rdparty/variant):

cmake_minimum_required(VERSION 3.6.3)

project(HelloWorld CXX)

add_subdirectory(3rdparty/variant)

add_executable(hello-world hello_world.cpp)
target_link_libraries(hello-world mpark_variant)

Installation / CMake find_package

git clone https://github.com/mpark/variant.git
mkdir variant/build && cd variant/build
cmake ..
cmake --build . --target install

This will install mpark/variant to the default install-directory for your platform (/usr/local for Unix, C:\Program Files for Windows). You can also install at a custom location via the CMAKE_INSTALL_PREFIX variable, (e.g., cmake .. -DCMAKE_INSTALL_PREFIX=/opt).

The installed mpark/variant can then be found by CMake via find_package:

cmake_minimum_required(VERSION 3.6.3)

project(HelloWorld CXX)

find_package(mpark_variant 1.3.0 REQUIRED)

add_executable(hello-world hello_world.cpp)
target_link_libraries(hello-world mpark_variant)

CMake will search for mpark/variant in its default set of installation prefixes. If mpark/variant is installed in a custom location via the CMAKE_INSTALL_PREFIX variable, you'll likely need to use the CMAKE_PREFIX_PATH to specify the location (e.g., cmake .. -DCMAKE_PREFIX_PATH=/opt).

Requirements

This library requires a standard conformant C++11 compiler. The following compilers are continously tested:

Compiler Operating System Version String
GCC 4.8.5 Ubuntu 16.04.6 LTS g++-4.8 (Ubuntu 4.8.5-4ubuntu8~16.04.1) 4.8.5
GCC 4.9.4 Ubuntu 16.04.6 LTS g++-4.9 (Ubuntu 4.9.4-2ubuntu1~16.04) 4.9.4
GCC 5.5.0 Ubuntu 16.04.6 LTS g++-5 (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010
GCC 6.5.0 Ubuntu 16.04.6 LTS g++-6 (Ubuntu 6.5.0-2ubuntu1~16.04) 6.5.0 20181026
GCC 7.4.0 Ubuntu 16.04.6 LTS g++-7 (Ubuntu 7.4.0-1ubuntu116.04ppa1) 7.4.0
GCC 8.3.0 Ubuntu 16.04.6 LTS g++-8 (Ubuntu 8.3.0-16ubuntu3~16.04) 8.3.0
GCC 9.2.1 Ubuntu 16.04.6 LTS g++-9 (Ubuntu 9.2.1-17ubuntu1~16.04) 9.2.1 20191102
Clang 3.6.2 Ubuntu 16.04.6 LTS Ubuntu clang version 3.6.2-3ubuntu2 (tags/RELEASE_362/final) (based on LLVM 3.6.2)
Clang 3.7.1 Ubuntu 16.04.6 LTS Ubuntu clang version 3.7.1-2ubuntu2 (tags/RELEASE_371/final) (based on LLVM 3.7.1)
Clang 3.8.0 Ubuntu 16.04.6 LTS clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
Clang 3.9.1 Ubuntu 16.04.6 LTS clang version 3.9.1-4ubuntu3~16.04.2 (tags/RELEASE_391/rc2)
Clang 4.0.0 Ubuntu 16.04.6 LTS clang version 4.0.0-1ubuntu1~16.04.2 (tags/RELEASE_400/rc1)
Clang 5.0.0 Ubuntu 16.04.6 LTS clang version 5.0.0-3~16.04.1 (tags/RELEASE_500/final)
Clang 6.0.0 Ubuntu 16.04.6 LTS clang version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final)
Clang 7.1.0 Ubuntu 16.04.6 LTS clang version 7.1.0-svn353565-1exp120190408084827.60 (branches/release_70)
Clang 8.0.1 Ubuntu 16.04.6 LTS clang version 8.0.1-svn369350-1exp120190820122438.78 (branches/release_80)
Clang Xcode 8.3 Darwin Kernel Version 16.7.0 (OS X 10.12.6) Apple LLVM version 8.1.0 (clang-802.0.42)
Clang Xcode 9.4 Darwin Kernel Version 17.4.0 (OS X 10.13.3) Apple LLVM version 9.1.0 (clang-902.0.39.2)
Clang Xcode 10.1 Darwin Kernel Version 17.7.0 (OS X 10.13.6) Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Visual Studio 14 2015 Visual Studio 2015 with Update 3 MSVC 19.0.24241.7
Visual Studio 15 2017 Visual Studio 2017 with Update 8 MSVC 19.15.26732.1
Visual Studio 15 2017 Visual Studio 2017 with Update 9 MSVC 19.16.27025.1
Visual Studio 15 2017 (Clang/LLVM) Visual Studio 2017 Clang 7.0.0

NOTES

  • GCC 4.8/4.9: constexpr support is not available for visit and relational operators.
  • Enabling libc++ std::variant tests require -std=c++17 support.

CMake Variables

  • MPARK_VARIANT_INCLUDE_TESTS:STRING (default: "")

    Semicolon-separated list of tests to build. Possible values are mpark, and libc++.

    NOTE: The libc++ std::variant tests are built with -std=c++17.

Unit Tests

Refer to test/README.md.

License

Distributed under the Boost Software License, Version 1.0.

Owner
Michael Park
@facebook engineer, @cplusplus member, @llvm committer, @mesos committer
Michael Park
Comments
  • WIP:  use switch case to improve codegen

    WIP: use switch case to improve codegen

    This PR is just to provide a convenient interface for initial code review.

    For now I only implemented for variants of up to 2 types. It seems to compile and pass unit tests. I tried to insert the implementation branch (to switch case) at the lowest point in the call stack just before it heads to the function pointer table. It took me a bit of trial and error to understand what to use to get the reference to the ith type from the variant at that point in the callstack so I'm hopefully using the right thing. Hopefully, access::base::get_alt is both correct and also unchecked, the latter seems to be important in getting optimal assembly (e.g. when I wrote my top level switch case code with get instead of get_if it didn't generate as good code).

    Anyhow, let me know what you think, we can finalize the max number of types for the optimization later once we have the details nailed down (it's very easy to change from how it's written).

  • Add support for MSVC 2015 cl compiler

    Add support for MSVC 2015 cl compiler

    MSVC 2015 Update 3 cl.exe cant compile example from https://wandbox.org/permlink/b4NDy4VupqPWkjva https://pastebin.com/iwe2sVKy

    from slack cpplang: k-ballo:

    the workaround is rather trivial, just move the noexcept(...) call within a class body, but that's something that only the library can do msvc uses different name lookup implementations depending on the context in which an expression appears.. it has at least 3 different ones, each with its own different bugs

  • now compiles in nvcc 10.2

    now compiles in nvcc 10.2

    So, using some xtensor stuff which uses variant.hpp, I was trying to unable to compile stuff with NVCC 10.2 (latest version), and encountered some parsing error in nvcc that is described in this issue and this PR. It seems that part of the issue lies in xtensor-stack's xtl library and also this variant.hpp header file.

    This PR just slightly changes an expression in variant.hpp so that the nvcc parser doesn't screw up. I think the code written here is valid beforehand, it's just that nvcc chokes on the parameter pack expansion somewhere.

    The error which is encountered before this change, when including variant.hpp is:

    [email protected]:~/scratch/variant/include/mpark$ nvcc nothing.cu 
    variant.hpp: In function ‘constexpr decltype(auto) mpark::visit(Visitor&&, Vs&& ...)’:
    variant.hpp:1967:96: error: parameter packs not expanded with ‘...’:
         return (detail::all({!vs.valueless_by_exception()...})
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                     ^                                                                                                      
    variant.hpp:1967:96: note:         ‘vs’
    

    No clue why this happens, but the changes here fix it.

  • Visit + Lambda

    Visit + Lambda

    Hi,

    I'm trying to use lambda instead of functor to visit a variant element. But is it possible to get compilation error when lambda do not handle all variant type ? Same behavior as a functor ?

    Ex :

    typedef mpark::variant<
        int8_t, uint8_t,
        double,
        std::string
    > VariantValue;
    
    VariantValue v{ (double)2.0 };
    
    mpark::visit([](auto&& arg)
    {
        using T = std::decay_t<decltype(arg)>;
        if (std::is_same_v<T, int8_t>)
            std::cout << "int8_t with value " << arg << '\n';
        else if (std::is_same_v<T, std::string>)
            std::cout << "std::string with value " << arg << '\n';
        else
            // static_assert(always_false<T>::value, "non-exhaustive visitor!");
            // static_assert(mpark::lib::is_invocable<T, arg>::value, "non-exhaustive visitor!");
    
            // ----------------------------------------------------------------------
            // I tried many thing but I'm always getting compilation error or
            // I can't achieve the behavior I'm look for.
    }, v);
    

    Important : I'm using Visual Studio 2015 Update 3 and GCC 4.9.1

    Ref: http://en.cppreference.com/w/cpp/utility/variant/visit

  • Fails with Intel compiler in C++11 mode

    Fails with Intel compiler in C++11 mode

    The following simplest example:

    #include "variant.hpp"
    
    int main()
    {
        using MyVariant = mpark::variant<int, float>;
        MyVariant x = 5;
    }
    

    fails to compile with the Intel compiler if I pass it -std=c++11, but compiles fine, if I give it -std=c++14. It compiles in C++11 mode with GCC and Clang.

    The compiler version is:

    icpc (ICC) 18.0.1 20171018
    Copyright (C) 1985-2017 Intel Corporation.  All rights reserved.
    

    The error it reports:

    In file included from example.cpp(1):
    variant.hpp(995): warning #3801: deduced return types are a C++14 feature
              inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
                                      ^
    
    In file included from example.cpp(1):
    variant.hpp(1000): warning #3801: deduced return types are a C++14 feature
              inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
                                      ^
    
    In file included from example.cpp(1):
    variant.hpp(1026): warning #3801: deduced return types are a C++14 feature
              inline static constexpr AUTO_REFREF get_alt(V &&v)
                                      ^
    
    In file included from example.cpp(1):
    variant.hpp(1033): warning #3801: deduced return types are a C++14 feature
              inline static constexpr AUTO_REFREF get_alt(V &&v)
                                      ^
    
    In file included from example.cpp(1):
    variant.hpp(1076): error: expected an identifier
                  inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
                                          ^
    
    In file included from example.cpp(1):
    variant.hpp(1076): error: "auto" is not allowed here
                  inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
                                          ^
    
    In file included from example.cpp(1):
    variant.hpp(1076): error #303: explicit type is missing ("int" assumed)
                  inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
                                          ^
    
    In file included from example.cpp(1):
    variant.hpp(1076): error: expected a ";"
                  inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
                                                        ^
    
    In file included from example.cpp(1):
    variant.hpp(1080): warning #12: parsing restarts here after previous syntax error
                };
                ^
    
    In file included from example.cpp(1):
    variant.hpp(1084): warning #3801: deduced return types are a C++14 feature
              inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
                                      ^
    
    In file included from example.cpp(1):
    variant.hpp(1088): warning #3801: deduced return types are a C++14 feature
              inline static constexpr AUTO make_fdiagonal_impl()
    
    
    [...]
    
    
    In file included from example.cpp(1):
    variant.hpp(2341): error: expected a ";"
        inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
                                       ^
    
    In file included from example.cpp(1):
    variant.hpp(2381): warning #12: parsing restarts here after previous syntax error
          }  // namespace hash
          ^
    
    In file included from example.cpp(1):
    variant.hpp(2383): error: expected a declaration
        }  // namespace detail
        ^
    
    In file included from example.cpp(1):
    variant.hpp(2423): warning #12: parsing restarts here after previous syntax error
                          v);
                            ^
    
    In file included from example.cpp(1):
    variant.hpp(2424): error: expected a declaration
            return hash_combine(result, hash<std::size_t>{}(v.index()));
            ^
    
    In file included from example.cpp(1):
    variant.hpp(2425): error: expected a declaration
          }
          ^
    
    In file included from example.cpp(1):
    variant.hpp(2437): warning #12: parsing restarts here after previous syntax error
          };
           ^
    
    In file included from example.cpp(1):
    variant.hpp(2443): error: expected a declaration
        };
        ^
    
    In file included from example.cpp(1):
    variant.hpp(2446): error: hash is not a template
        struct hash<mpark::monostate> {
               ^
    
    In file included from example.cpp(1):
    variant.hpp(2455): error: expected a declaration
      }  // namespace std
      ^
    
    example.cpp(7): warning #12: parsing restarts here after previous syntax error
    
    compilation aborted for example.cpp (code 2)
    
  • Crash under MSVC (VS 2015 update 3)

    Crash under MSVC (VS 2015 update 3)

    When we have a global object that contains a variant instance that stores a std::unique_ptr the variant creation crashes the program. I tried to illustrate the minimal example and provide a stacktrace of the crash. Am I missing something? I don't think the code does anything illegal?

    Crash occurs on MSVC (GCC does not crash);

    Callstack:

    0000000000000000() (Unknown Source:0)
    tests.exe!mpark::detail::visitation::alt::visit_alt<mpark::detail::dtor,mpark::detail::destructor<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > >,1> & __ptr64>(mpark::detail::dtor && visitor, mpark::detail::destructor<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > >,1> & <vs_0>) Line 1212 (d:\dev\contrib\variant.hpp:1212)
    tests.exe!mpark::detail::destructor<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > >,1>::destroy() Line 1461 (d:\dev\contrib\variant.hpp:1461)
    tests.exe!mpark::detail::assignment<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > > >::emplace<1,std::unique_ptr<int,std::default_delete<int> > >(std::unique_ptr<int,std::default_delete<int> > && <args_0>) Line 1606 (d:\dev\contrib\variant.hpp:1606)
    tests.exe!`mpark::detail::assignment<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > > >::assign_alt<1,std::unique_ptr<int,std::default_delete<int> >,std::unique_ptr<int,std::default_delete<int> > >'::`7'::<unnamed-type-impl>::operator()(std::integral_constant<bool,1> __formal) Line 1639 (d:\dev\contrib\variant.hpp:1639)
    tests.exe!mpark::detail::assignment<mpark::detail::traits<int,std::unique_ptr<int,std::default_delete<int> > > >::assign_alt<1,std::unique_ptr<int,std::default_delete<int> >,std::unique_ptr<int,std::default_delete<int> > >(mpark::detail::alt<1,std::unique_ptr<int,std::default_delete<int> > > & a, std::unique_ptr<int,std::default_delete<int> > && arg) Line 1650 (d:\dev\contrib\variant.hpp:1650)
    tests.exe!mpark::detail::impl<int,std::unique_ptr<int,std::default_delete<int> > >::assign<1,std::unique_ptr<int,std::default_delete<int> > >(std::unique_ptr<int,std::default_delete<int> > && arg) Line 1765 (d:\dev\contrib\variant.hpp:1765)
    tests.exe!mpark::variant<int,std::unique_ptr<int,std::default_delete<int> > >::operator=<std::unique_ptr<int,std::default_delete<int> >,0,1,std::unique_ptr<int,std::default_delete<int> >,0>(std::unique_ptr<int,std::default_delete<int> > && arg) Line 1982 (d:\dev\contrib\variant.hpp:1982)
    tests.exe!Bar::Bar() Line 214 (d:\dev\tests\core\ConfigTests.cpp:214)
    tests.exe!Foo::Foo() Line 221 (d:\dev\tests\core\ConfigTests.cpp:221)
    tests.exe!`dynamic initializer for 'instance''() Line 223 (d:\dev\tests\core\ConfigTests.cpp:223)
    ucrtbased.dll!00007fffdfb20479() (Unknown Source:0)
    tests.exe!__scrt_common_main_seh() Line 223 (f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:223)
    tests.exe!__scrt_common_main() Line 296 (f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:296)
    tests.exe!mainCRTStartup() Line 17 (f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp:17)
    kernel32.dll!00007ff80f091fe4() (Unknown Source:0)
    ntdll.dll!00007ff80f79efb1() (Unknown Source:0)
    

    Repro:

    using UniquePointer = std::unique_ptr<int>;
    using VariantType = mpark::variant<int, UniquePointer>;
    
    struct Bar
    {
        Bar()
        {
            auto ptr = std::make_unique<int>();
            v = std::move( ptr ); // this crashes
        }
        VariantType v;
    };
    
    struct Foo {
        Foo()
        {
        }
        Bar store;
    } instance; // global instance
    
  • NVCC: Fix Compile with C++14 Constexpr

    NVCC: Fix Compile with C++14 Constexpr

    Out of the many C++14 constexpr constructs, one does not compile with NVCC - even when using -std=c++14 --expt-relaxed-constexpr.

    Seen with all versions of NVCC so far (latest tests with 10.1 and 10.2), occurs even if simply included for host-side code.

    Fix #70

    ~~Update: uh, it helps to replace the brackets with an explicit constructor of an initializer list :)~~ does not work

  • store size_t in a variant ?

    store size_t in a variant ?

    Hi,

    I am trying to store size_t as well as int64_t, double, bool, string in a variant. Storing the 4 last types work fine, but I get a bad_variant_access when trying to get/set the size_t with this code :

    #include <iostream>
    #include <cstdint>
    #define MPARK_EXCEPTIONS
    #include <mpark/variant.hpp>
    
    using namespace std;  // i know it's bad, it's just for testing
    
    int main()
    {
        mpark::variant<int64_t, double, bool, string, size_t> v;
        mpakr::get<size_t>(v) = 15;  // right there
        return 0;
    }
    

    edit : updating code to fix compile errors

  • `static_assert` vs SFINAE

    `static_assert` vs SFINAE

    For type-based operations, there must be exactly one instance of T in the list of alternatives. Determine whether this violation should be a static_assert within the function or a SFINAE + = delete;.

  • Support for pointers to incomplete types

    Support for pointers to incomplete types

    First of all, thank you for providing this implementation of std::variant. It's great to have a lightweight alternative to boost::variant available in C++11. I just stumbled over an issue with recursive type definitions, and I'm not sure whether it's a limitation of C++11 or the variant implementation. The following struct definition compiles fine with GCC's and Clang's C++17 support:

    struct S {
        std::variant<
            std::map<int, S>*
        > var;
    };
    

    When replacing std::variant with mpark::variant, I get an error message regarding the incomplete type S of std::pair<int, S>::second. Is it possible to enhance your implementation so that this kind of recursive type definition compiles with mpark::variant under C++11 as well?

  • Codegen issue

    Codegen issue

    Hey, I found some pretty not great codegen for std::visit. I mentioned this to Louis Dionne (a libc++ maintainer), and asked him if there was a reason switch case was not used for the common case (e.g. visit one variant for under ten types). You can see the differences in assembly (std, boost, switch-case) for 2 examples here: https://gcc.godbolt.org/z/Kt8ZNf.

    He suggested opening an issue here, because he said that you were the expert on implementing variant :-). Someone also told me that there was an open bug report on gcc, so I wrote things up fairly neatly there in more detail: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78113. The gist of it is that the constexpr table of function pointer approach just doesn't get inlined even with brand new compilers, and you can see that even relatively easy optimization like simply optimizing out an empty visitor can't be done. Switch case can't really be doen generically, but it seems easy to generate a bit of code such that you can handle visit for a single variant for up to N types (have N equal e.g. 10). This code is actually fairly simple, and gives massive improvements in assembly for the very common case, at no runtime cost to the other cases, and just very minimal compile cost (most likely).

    Let me know your thoughts!

  • Request: Compare and contrast with other variant implementations

    Request: Compare and contrast with other variant implementations

    Thank you for this stand-alone implementation.

    My problem with it is - I don't know whether to use it, or any of the other ones: Boost, eggs-cpp, Martin Moene's or MapBox - because I don't know what their differences are. Would you mind adding a short comparison (e.g. as a table) to your README?

  • Disable exceptions when used in CUDA code

    Disable exceptions when used in CUDA code

    CUDA supports neither exceptions nor std::terminate in device code, but NVCC silently ignores them. Unfortunately, Clang, which can be used as a drop-in replacement for NVCC to compile CUDA code, does not silently ignore these things, raising the following errors:

    variant/include/mpark/variant.hpp:1811:42: error: reference to __host__ function 'throw_bad_variant_access' in __host__ __device__ function
              holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
    
    variant/include/mpark/variant.hpp:242:9: error: reference to __host__ function '~exception' in __host__ __device__ function
      class bad_variant_access : public std::exception {
    

    To observe this issue, use the following minimal piece of sample code:

    #include <mpark/variant.hpp>
    
    using V = mpark::variant<int>;
    
    __global__
    void kernel(V v) {
        mpark::get<0>(v);
    }
    
    int main() {
        kernel<<<1,1>>>(0);
        return 0;
    }
    

    It can be successfully compiled by NVCC (/usr/local/cuda-11.0/bin/nvcc -I variant/include -x cu -ccbin $(which g++-9) -std=c++14 -gencode=arch=compute_52,code=[sm_52,compute_52] --expt-relaxed-constexpr -o test_nvcc test.cpp). It cannot be successfully compiled by Clang (clang++-10 -I variant/include -x cuda --gcc-toolchain=$(dirname $(dirname $(which gcc-9))) -std=c++14 --cuda-path=/usr/local/cuda-10.0 --cuda-gpu-arch=sm_52 -L /usr/local/cuda-10.1/lib -lcudart -o test_clang test.cpp) without my patch. Note that the precise versions of CUDA, Clang or GCC used in the compile command do not matter, the ones I gave here simply are whatever compatible versions I had available on my system.

    My patch disables exceptions inside CUDA device code and uses the __trap intrinsic as an std::terminate-equivalent to deliver consistently correct behavior for both NVCC and Clang.

  • Constructor broken with gcc-4.8.5

    Constructor broken with gcc-4.8.5

    #include <iostream>
    
    #include "variant.hpp"
    
    struct sv {
            sv(const char*) {}
    };
    
    int main() {
            mpark::variant<bool, sv> v{"abc"};
            std::cout << v.index() << "\n";
            return 0;
    }
    

    With gcc-4.8.5, this code incorrectly prints 0.

    It works for later versions of gcc (tested 7.5.0) and prints 1.

    I tested against master 3c7fc8266bb46046b42c2dc2663f9f505f0cec28

  • [Optimization] Don't generate bad_variant_access logic for exhaustive visit using overloaded

    [Optimization] Don't generate bad_variant_access logic for exhaustive visit using overloaded

    Using clang from trunk with -std=c++17 -Oz, the following code uses overloaded with an exhaustive set of lambdas to visit all cases of the variant:

    https://godbolt.org/z/k-r72L

    #include <https://raw.githubusercontent.com/mpark/variant/single-header/master/variant.hpp>
    #include <functional>
    #include <string>
    #include <stdio.h>
    
    namespace {
    template <class... Ts>
    struct overloaded : Ts... {
    using Ts::operator()...;
    };
    template <class... Ts>
    overloaded(Ts...) -> overloaded<Ts...>;
    }
    
    enum class Foo { kValue };
    enum class Bar { kValue };
    using Callback = std::function<std::string(void)>;
    
    using SumType = mpark::variant<Foo, Bar, Callback>;
    
    void DoStuff(SumType s) {
      mpark::visit(overloaded{
          [](Foo f) { printf("Foo!\n");},
          [](Bar b) { printf("Bar!\n");},
          [](const Callback& c) { printf("Callback: %s\n", c().c_str()); },
      },
      s);
    } 
    

    Even though the lambdas exhaustively handle all cases, MPark.Variant still generates error handling logic to throw bad_variant_access exceptions (which as far as I can tell should never happen).

    I think it'd be a nice optimization to drop the error handling entirely if the visitation is exhaustive.

    Here's the current state of the optimized assembly in case this gets fixed:

    DoStuff(mpark::variant<Foo, Bar, std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > ()> >): # @DoStuff(mpark::variant<Foo, Bar, std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > ()> >)
            push    r14
            push    rbx
            sub     rsp, 40
            mov     rbx, rdi
            cmp     byte ptr [rdi + 32], -1
            lea     rdi, [rsp + 7]
            sete    byte ptr [rdi]
            push    1
            pop     rsi
            call    mpark::detail::any(std::initializer_list<bool>)
            test    al, al
            jne     .LBB0_8
            movzx   ecx, byte ptr [rbx + 32]
            cmp     rcx, 255
            push    -1
            pop     rax
            cmovne  rax, rcx
            cmp     rax, 2
            je      .LBB0_6
            cmp     rax, 1
            je      .LBB0_5
            mov     edi, offset .Lstr
            jmp     .LBB0_4
    .LBB0_5:
            mov     edi, offset .Lstr.4
    .LBB0_4:
            call    puts
            jmp     .LBB0_7
    .LBB0_6:
            lea     r14, [rsp + 8]
            mov     rdi, r14
            mov     rsi, rbx
            call    std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > ()>::operator()() const
            mov     rsi, qword ptr [r14]
            mov     edi, offset .L.str.3
            xor     eax, eax
            call    printf
            mov     rdi, r14
            call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [base object destructor]
    .LBB0_7:
            add     rsp, 40
            pop     rbx
            pop     r14
            ret
    .LBB0_8:
            call    mpark::throw_bad_variant_access()
    mpark::detail::any(std::initializer_list<bool>): # @mpark::detail::any(std::initializer_list<bool>)
            xor     eax, eax
    .LBB1_1:                                # =>This Inner Loop Header: Depth=1
            cmp     rsi, rax
            je      .LBB1_4
            cmp     byte ptr [rdi + rax], 0
            lea     rax, [rax + 1]
            je      .LBB1_1
            mov     al, 1
            ret
    .LBB1_4:
            xor     eax, eax
            ret
    mpark::throw_bad_variant_access():  # @mpark::throw_bad_variant_access()
            push    rax
            push    8
            pop     rdi
            call    __cxa_allocate_exception
            mov     qword ptr [rax], offset vtable for mpark::bad_variant_access+16
            mov     esi, offset typeinfo for mpark::bad_variant_access
            mov     edx, offset std::exception::~exception() [base object destructor]
            mov     rdi, rax
            call    __cxa_throw
    mpark::bad_variant_access::~bad_variant_access() [deleting destructor]:      # @mpark::bad_variant_access::~bad_variant_access() [deleting destructor]
            push    rbx
            mov     rbx, rdi
            call    std::exception::~exception() [base object destructor]
            mov     rdi, rbx
            pop     rbx
            jmp     operator delete(void*)                  # TAILCALL
    mpark::bad_variant_access::what() const:  # @mpark::bad_variant_access::what() const
            mov     eax, offset .L.str
            ret
    std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > ()>::operator()() const: # @std::function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > ()>::operator()() const
            push    rbx
            cmp     qword ptr [rsi + 16], 0
            je      .LBB5_2
            mov     rbx, rdi
            call    qword ptr [rsi + 24]
            mov     rax, rbx
            pop     rbx
            ret
    .LBB5_2:
            call    std::__throw_bad_function_call()
    typeinfo name for mpark::bad_variant_access:
            .asciz  "N5mpark18bad_variant_accessE"
    
    typeinfo for mpark::bad_variant_access:
            .quad   vtable for __cxxabiv1::__si_class_type_info+16
            .quad   typeinfo name for mpark::bad_variant_access
            .quad   typeinfo for std::exception
    
    vtable for mpark::bad_variant_access:
            .quad   0
            .quad   typeinfo for mpark::bad_variant_access
            .quad   std::exception::~exception() [base object destructor]
            .quad   mpark::bad_variant_access::~bad_variant_access() [deleting destructor]
            .quad   mpark::bad_variant_access::what() const
    
    .L.str:
            .asciz  "bad_variant_access"
    
    .L.str.3:
            .asciz  "Callback: %s\n"
    
    .Lstr:
            .asciz  "Foo!"
    
    .Lstr.4:
            .asciz  "Bar!"
    
  • Reduce minimum required CMake version

    Reduce minimum required CMake version

    Is there a specific reason that CMake 3.6.3 is the minimum required version or is this just the smallest version against which was tested?

    I'm using this library with CMake 3.5.1 (default on Ubuntu 16.04) via add_subdirectory() without any obvious issues.

  • Clang V7.1.0 warning

    Clang V7.1.0 warning

    We are seeing this warning while #including <variant.hpp> (V1.4.0) when using Clang 7.1.0:

    /hpx/source/libs/datastructures/include/hpx/datastructures/detail/variant.hpp:2243:22: error: constructor accepting a forwarding reference can hide the copy and move constructors [bugprone-forwarding-reference-overload,-warnings-as-errors]
        inline constexpr variant(Arg &&arg) noexcept(
                         ^
    /hpx/source/libs/datastructures/include/hpx/datastructures/detail/variant.hpp:2231:5: note: copy constructor declared here
        variant(const variant &) = default;
        ^
    /hpx/source/libs/datastructures/include/hpx/datastructures/detail/variant.hpp:2232:5: note: move constructor declared here
        variant(variant &&) = default;
    

    Anything we can do about this?

Eggs.Variant is a C++11/14/17 generic, type-safe, discriminated union.

Eggs.Variant Introduction Eggs.Variant is a C++11/14/17 generic, type-safe, discriminated union. See the documentation at http://eggs-cpp.github.io/va

Sep 9, 2022
A realtime/embedded-friendly C++11 variant type which is never empty and prevents undesirable implicit conversions

strict variant Do you use boost::variant or one of the many open-source C++11 implementations of a "tagged union" or variant type in your C++ projects

Sep 9, 2022
A C++ data container replicating std::queue functionality but with better performance.

A data container replicating std::queue functionality but with better performance than standard library containers in a queue context. C++98/03/11/14/etc-compatible.

Aug 16, 2022
Benchmarking a trivial replacement for std::vector
Benchmarking a trivial replacement for std::vector

std::vector replacement benchmark Dependencies You'll need gnuplot and bash to run ./bench.sh. In addition to that, you'll need to have gcc and clang

Aug 27, 2022
A drop-in replacement for std::list with 293% faster insertion, 57% faster erasure, 17% faster iteration and 77% faster sorting on average. 20-24% speed increase in use-case testing.

plf::list A drop-in replacement for std::list with (on average): 293% faster insertion 57% faster erasure 17% faster iteration 77% faster sorting 70%

Sep 11, 2022
A C++ data container replicating std::stack functionality but with better performance than standard library containers in a stack context.

plf::stack A data container replicating std::stack functionality but with better performance than standard library containers in a stack context. C++9

Sep 11, 2022
variant lite - A C++17-like variant, a type-safe union for C++98, C++11 and later in a single-file header-only library

variant lite: A single-file header-only version of a C++17-like variant, a type-safe union for C++98, C++11 and later Contents Example usage In a nuts

Sep 22, 2022
Recursive Variant: A simple library for Recursive Variant Types
Recursive Variant: A simple library for Recursive Variant Types

rva::variant — Recursive Sum Types for C++ Provided by the Recursive Variant Authority. We stand united in opposition to the TVA. May your variants ne

Sep 5, 2022
high performance C++20 implementation of std::variant

A minimal compile-time overhead, C++20 implementation of std::variant. Fully standard conforming with a couple of documented differences.

Sep 16, 2022
A cleaner and more intuitive std::variant alternative

[WIP] ExtendedVariant This single header library is part of my C++ extended standard stdex libraries. Check our my profile for more. Working with C++

Jun 13, 2021
A wrapper around std::variant with some helper functions

A wrapper around std::variant with some helper functions

Oct 30, 2021
Finite State Machine implementation using std::variant

mp::fsm Implementation of Finite State Machine presented by me on CppCon 2018 in a talk Effective replacement of dynamic polymorphism with std::varian

Aug 3, 2022
Fast C++ container combining the best features of std::vector and std::deque

veque The double-ended vector A very fast C++17 container combining the best features of std::vector and std::deque "In Most Cases, Prefer Using deque

Jul 22, 2022
Invoke.hpp - std::invoke/std::apply analogs for C++11/14

invoke.hpp std::invoke/std::apply analogs for C++11/14 Requirements gcc >= 4.9 clang >= 3.8 msvc >= 2015 Installation invoke.hpp is a header-only libr

Jun 30, 2022
Cpp-std-fwd - forward declarations for C++ std headers

cpp-std-fwd Forward declarations for most useful runtime classes of the C++ 17 standard library. DISCLAIMER: This project is meant as a proof-of-conce

Jul 10, 2022
Structural variant detection and association testing
Structural variant detection and association testing

wham The wham suite consists of two programs, wham and whamg. wham, the original tool, is a very sensitive method with a high false discovery rate. Th

Sep 2, 2022
A modern, C++11-native, single-file header-only, tiny framework for unit-tests, TDD and BDD (includes C++98 variant)

lest – lest errors escape testing This tiny C++11 test framework is based on ideas and examples by Kevlin Henney [1,2] and on ideas found in the CATCH

Sep 19, 2022
Eggs.Variant is a C++11/14/17 generic, type-safe, discriminated union.

Eggs.Variant Introduction Eggs.Variant is a C++11/14/17 generic, type-safe, discriminated union. See the documentation at http://eggs-cpp.github.io/va

Sep 9, 2022
A realtime/embedded-friendly C++11 variant type which is never empty and prevents undesirable implicit conversions

strict variant Do you use boost::variant or one of the many open-source C++11 implementations of a "tagged union" or variant type in your C++ projects

Sep 9, 2022