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

Overview

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.

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).

    opened by quicknir 29
  • 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

    opened by RedSkotina 13
  • 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.

    opened by gridley 9
  • 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

    opened by erakis 9
  • 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)
    
    opened by mrzv 8
  • 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
    
    opened by twadrianlis 8
  • 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

    opened by ax3l 7
  • 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

    opened by SuperFola 7
  • `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;.

    opened by mpark 7
  • 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?

    opened by mgieseki 6
  • 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!

    opened by quicknir 5
  • Added PlatformIO manifest library.json

    Added PlatformIO manifest library.json

    I have added PlatformIO manifest library.json.

    Currently I published it in registry under my account (hacker-cb): https://registry.platformio.org/libraries/hacker-cb/MPark-Variant but you can make your own account to publish official releases.

    opened by hacker-cb 0
  • 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?

    opened by eyalroz 0
  • 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.

    opened by mkuron 0
  • 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

    opened by rnburn 1
  • [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!"
    
    opened by bhamiltoncx 8
  • 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.

    opened by wrightsg 0
Releases(v1.4.0)
  • v1.4.0(Jan 15, 2019)

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

    The implementation is based on my implementation of std::variant for libc++ and is continuously tested against libc++'s std::variant test suite.


    New Features

    • #56: Replaced the visitation mechanism to be switch-based.
      • Blog post: Variant Visitation V2
      • Thank you @quicknir for the contribution and collaboration on this effort!
    • Improved visitor same return type error message:
    error: static_assert failed due to requirement 'expected<int>::template but_got<double>()'
        "`visit` requires the visitor to have a single return type"
    
    • Improved visitor exhaustiveness error message:
    error: static_assert failed due to requirement
        'visitor<add_ints>::template does not handle<const int &, const int &>()'
        "`visit` requires the visitor to be exhaustive"
    

    Bug Fixes

    • Relational operators must return a type implicitly convertible to bool.
    • #34: Support for pointers to incomplete types
    • #41: Crash under MSVC (VS 2015 update 3)
    • Visual Studio 2017 related issues:
      • #48: Fails to compile with MSVC 19.15
      • #49: Disable MPARK_CPP14_CONSTEXPR for MSVC <= 19.15
      • #55: Compilation failed in Visual Studio 2017
      • #57: Use constexpr compiler bug workaround for VS2017 version 15.9
    Source code(tar.gz)
    Source code(zip)
    variant.hpp(100.17 KB)
  • v1.3.0(Dec 12, 2017)

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

    The implementation is based on my implementation of std::variant for libc++ and is continuously tested against libc++'s std::variant test suite.


    New Features

    • Added support for GCC 4.8
    • Lowered the minimum MSVC version from 190024215 to 190024210
    • #36: Significant reduction in binary size

    Bug Fixes

    • #34: "Support for pointers to incomplete types"
    • #35, #38: Fixed regression in MSVC 19.12.
    Source code(tar.gz)
    Source code(zip)
    variant.hpp(83.64 KB)
  • v1.2.2(Sep 14, 2017)

  • v1.2.1(Sep 6, 2017)

  • v1.2.0(Jun 8, 2017)

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

    The implementation is based on my implementation of std::variant for libc++ and is continuously tested against libc++'s std::variant test suite.


    New Features

    • Support for GCC 4.9 (no constexpr support for visit and relational operators)
    • Support for -fno-exceptions
    • Support for #include of mpark/variant.hpp without the -I flag
    • Single header versions of mpark/variant.hpp hosted on the single-header branch
    • Support for try it | on godbolt
    • Increase warning levels

    Bug Fixes

    • LWG2904: "Make variant move-assignment more exception safe."
    Source code(tar.gz)
    Source code(zip)
    variant.hpp(81.22 KB)
  • v1.1.0(May 12, 2017)

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

    The implementation is based on my implementation of std::variant for libc++ and is continuously tested against libc++'s std::variant test suite.


    New Features

    • Support for C++11
    • Support for MSVC 2015 / 2017

    Bug Fixes

    • LWG2857: "{variant,optional,any}::emplace should return the constructed value"
    • Implement P0513: "Poisoning the Hash"
    • Implement P0599: "noexcept for Hash functions"
    • llvm.org/PR31916: "std::visit does not accept visitors which require non-const lvalue references"
    • llvm.org/PR33000: "std::visit with zero variants is broken"
    Source code(tar.gz)
    Source code(zip)
    variant.hpp(82.27 KB)
  • v1.0.1(May 12, 2017)

    MPark.Variant is an implementation of C++17 std::variant as a C++14 library.

    The implementation is based on my implementation of std::variant for libc++ and is continuously tested against libc++ std::variant test suite.


    Bug Fixes

    • LWG2857: "{variant,optional,any}::emplace should return the constructed value"
    • Implement P0513: "Poisoning the Hash"
    • Implement P0599: "noexcept for Hash functions"
    • llvm.org/PR31916: "std::visit does not accept visitors which require non-const lvalue references"
    • llvm.org/PR33000: "std::visit with zero variants is broken"
    Source code(tar.gz)
    Source code(zip)
    variant.hpp(63.58 KB)
  • v1.0.0(Jan 17, 2017)

Owner
Michael Park
@facebook engineer, @cplusplus member, @llvm committer, @mesos committer
Michael Park
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

null 138 Dec 3, 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

Chris Beck 90 Oct 10, 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.

Matt Bentley 20 Dec 4, 2022
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

Dale Weiler 9 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%

Matt Bentley 117 Nov 8, 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

Matt Bentley 47 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

Martin Moene 225 Dec 29, 2022
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

Alecto Irene Perez 65 Dec 17, 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.

null 37 Nov 22, 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++

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

A wrapper around std::variant with some helper functions

Eyal Amir 1 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

Mateusz Pusz 65 Jan 3, 2023
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

null 102 Nov 26, 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

Matvey Cherevko 33 Dec 1, 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

Philip Trettner 67 Jul 10, 2022
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

Zev Kronenberg 93 Nov 14, 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

Martin Moene 355 Dec 28, 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

null 138 Dec 3, 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

Chris Beck 90 Oct 10, 2022