Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clang compiled program throws std::bad_any_cast during std::any_cast

I'm working on an application where I use std::any.

Recently I've discovered that when I compile it with clang I'm getting bad_any_cast exception on one of the std::any_casts.

I'm sure I'm casting to right type. I've added some dumps of typeid(T).name() to cout to be sure there is no difference between type inserted into std::any and type i'm trying to cast to.

I was trying to write simple program to demonstrate it but I cannot reproduce it.

What's worth mentioning: I'm passing a pack of std::any (each contains different type inside) and only one has problems (its a std::map<ENUM, int>).

Problem disappears when I switch to boost::any (or if I build my app with gcc).

I've dived into std::any_cast implementation and it fails on:

  template<typename _Tp>
    void* __any_caster(const any* __any)
    {
      if constexpr (is_copy_constructible_v<decay_t<_Tp>>)
      {
          if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage)
          {
              any::_Arg __arg;
               __any->_M_manager(any::_Op_access, __any, &__arg);
              return __arg._M_obj;
          }
        }
      return nullptr;
    }

Second if statement with (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage) doesn't pass.

It seems to me like there are a few instances of any::_Manager<decay_t<_Tp>>::_S_manage for the same _Tp (possibly caused by my application consisting of a few modules) but I cannot reproduce it on a simpler example.

Do you guys have any suggestions or hints how can I deal with it?

EDIT

Inspired by comments and answers, I've created example where I mess up with default visibility (which is also something I do in my app) and that seems to be the root of the problem.

Files:

lib.cpp

#include <any>
#include <map>

enum class D
{
    a,
    b,
    c,
    d,
    e,
};

int read(const std::any& a)
{
    auto map = std::any_cast<std::map<D, int>>(a);
    return map.begin()->second;
}

std::any create()
{
    std::map<D, int> b = { {D::c, 5} };

    std::any a(b);

    return a;
}

lib2.cpp

#include <any>
#include <map>

std::any create();
int read(const std::any& a);

__attribute__ ((visibility("default"))) std::any build_bar2()
{
    return create();
}

__attribute__ ((visibility("default"))) int read_foo2(const std::any& a)
{
    return read(a);
}

lib3.cpp

#include <any>
#include <map>

int read(const std::any &);
std::any create();

__attribute__ ((visibility("default"))) int read_foo3(const std::any& a)
{
    return read(a);
}

__attribute__ ((visibility("default"))) std::any build_bar3()
{
    return create();
}

main.cpp

#include <any>

int read_foo2(const std::any& a);
std::any build_bar2();

int read_foo3(const std::any& a);
std::any build_bar3();


int main()
{
    const std::any& a = build_bar3();
    int av = read_foo2(a);

    const std::any& b = build_bar2();
    int bv = read_foo3(a);

    return av == bv? 1: 0;
}

Makefile

CPP=clang++

all: main

lib.o: lib.cpp
    $(CPP) -std=c++17 -fvisibility=hidden -g -O0 -fPIC -c lib.cpp -o lib.o

lib2.so: lib2.cpp lib.o
    $(CPP) -std=c++17 -fvisibility=hidden -g -O0 -fPIC -shared lib2.cpp lib.o -o lib2.so

lib3.so: lib3.cpp lib.o
    $(CPP) -std=c++17 -fvisibility=hidden -g -O0 -fPIC -shared lib3.cpp lib.o -o lib3.so


main: main.cpp lib3.so lib2.so
    $(CPP) -std=c++17 -fvisibility=hidden -g -O0 main.cpp ./lib2.so ./lib3.so -o main

clean:
    rm -f ./lib.o ./lib2.so ./lib3.so ./main

So I have 2 shared libraries which are linking common static lib responsible for std::any creation and casting. When std::any is created in one instation of static lib and casted in another I get an exception.

like image 789
Michał Walenciak Avatar asked Aug 05 '18 10:08

Michał Walenciak


1 Answers

If you're on a Unix like Linux or OS X, the C++ type ID is handled by a pointer to type information, not a name string match.

That means that if you have shared libraries or static libraries that have the "same" type, but they have their own individual copies of it, the type info will not match up.

This may or may not be your situation.

I believe you'll have to define your types used in your any in a common shared library, declared with default visibility. That should make all of your other shared libraries use that copy instead of a hidden copy from their static library.

like image 109
Zan Lynx Avatar answered Oct 17 '22 15:10

Zan Lynx