Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

asserting std::holds_alternative with complex type fails to compile

The following code uses assert to check if a std::variant holds some specific type, i.e. a complex type. However, it fails to compile with gcc-10, clang-7, and clang-14.

#include <cassert>
#include <list>
#include <variant>

template <typename K, typename V>
class Foo {
public:
    struct Item;

    using List = std::list<Item>;

    using Var = std::variant<V, typename List::iterator>;

    struct Item {
        K k;
        Var var;
    };
};

template <typename K, typename V>
class Bar {
public:
    auto func(typename Foo<K, V>::Var &var)
        -> typename Foo<K, V>::List::iterator {

        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));

        return std::get<typename Foo<K, V>::List::iterator>(var);
    }
};

int main() {
    Foo<int, int>::Item item;
    Bar<int, int> bar;
    bar.func(item.var);
    return 0;
}

The compilers complain that assert is not declared:

t.cpp:25:55: error: too many arguments provided to function-like macro invocation
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
                                                      ^
/usr/include/assert.h:92:11: note: macro 'assert' defined here
#  define assert(expr)                                                  \
          ^
t.cpp:25:9: error: use of undeclared identifier 'assert'; did you mean '__assert'?
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
        __assert
/usr/include/assert.h:81:13: note: '__assert' declared here
extern void __assert (const char *__assertion, const char *__file, int __line)
            ^
t.cpp:25:9: warning: expression result unused [-Wunused-value]
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
t.cpp:25:9: warning: expression result unused [-Wunused-value]
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
t.cpp:36:9: note: in instantiation of member function 'Bar<int, int>::func' requested here
    bar.func(item.var);
        ^
2 warnings and 2 errors generated.

However, if I change the assert call to the following:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

using Iter = typename Foo<K, V>::List::iterator;

assert(std::holds_alternative<Iter>(var));

gcc-10 compiles successfully, while clang-7 and clang-14 fails with some strange error messages:

/usr/bin/ld: /tmp/t-ce3ab8.o: in function `__clang_call_terminate':
t.cpp:(.text.__clang_call_terminate[__clang_call_terminate]+0x2): undefined reference to `__cxa_begin_catch'
/usr/bin/ld: t.cpp:(.text.__clang_call_terminate[__clang_call_terminate]+0x7): undefined reference to `std::terminate()'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::__throw_bad_variant_access(char const*)':
t.cpp:(.text._ZSt26__throw_bad_variant_accessPKc[_ZSt26__throw_bad_variant_accessPKc]+0x12): undefined reference to `__cxa_allocate_exception'
/usr/bin/ld: t.cpp:(.text._ZSt26__throw_bad_variant_accessPKc[_ZSt26__throw_bad_variant_accessPKc]+0x39): undefined reference to `__cxa_throw'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::bad_variant_access::~bad_variant_access()':
t.cpp:(.text._ZNSt18bad_variant_accessD2Ev[_ZNSt18bad_variant_accessD2Ev]+0x11): undefined reference to `std::exception::~exception()'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::exception::exception()':
t.cpp:(.text._ZNSt9exceptionC2Ev[_ZNSt9exceptionC2Ev]+0xf): undefined reference to `vtable for std::exception'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::bad_variant_access::~bad_variant_access()':
t.cpp:(.text._ZNSt18bad_variant_accessD0Ev[_ZNSt18bad_variant_accessD0Ev]+0x1e): undefined reference to `operator delete(void*)'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.rel.ro._ZTISt18bad_variant_access[_ZTISt18bad_variant_access]+0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.rel.ro._ZTISt18bad_variant_access[_ZTISt18bad_variant_access]+0x10): undefined reference to `typeinfo for std::exception'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Is there any portable solution to this problem?

>Solution :

Macros have some pitfalls, not only for the writer of the macro, but also for the user. , is special, because it delimits parameters to function like macros, hence can cause issues when it appears in the parameters. Here, adding brackets helps:

assert((std::holds_alternative<typename Foo<K, V>::List::iterator>(var)));
    // ^                                                               ^
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading