clang编译程序在std :: any_cast期间抛出std :: bad_any_cast

时间:2018-08-05 10:49:25

标签: c++ gcc casting clang c++17

我正在使用std::any的应用程序上工作。

最近我发现,当我用clang编译它时,bad_any_cast之一上出现了std::any_cast异常。

我确定我的类型正确。我在typeid(T).name()中添加了一些cout转储,以确保插入到std::any中的类型和我尝试转换为的类型之间没有区别。

我试图编写简单的程序来演示它,但是我无法复制它。

值得一提的是: 我要传递一包std::any(每个包装内部包含不同类型),只有一个包装有问题(其std::map<ENUM, int>)。

当我切换到boost::any时(或者如果我使用gcc构建应用程序),问题消失了。

我已经深入研究std::any_cast的实现,但失败了:

  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;
    }

第二个if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage)语句未通过。

在我看来,同一个any::_Manager<decay_t<_Tp>>::_S_manage_Tp的一些实例(可能是由我的应用程序由几个模块组成的),但是我无法在一个简单的示例中重现它。 / p>

你们对我有什么建议或提示吗?

编辑

受评论和答案的启发,我创建了一个示例,弄乱了默认可见性(这也是我在我的应用程序中所做的事情),这似乎是问题的根源。

文件:

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;
}

制作文件

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

因此,我有2个共享库,这些共享库链接了负责std::any创建和转换的公共静态库。在静态lib的一个站点中创建std::any并在另一个静态目录中强制转换时,会出现异常。

1 个答案:

答案 0 :(得分:3)

如果您使用的是Linux或OS X这样的Unix,则C ++类型ID是由指向类型信息的指针处理的,而不是名称字符串匹配项。

这意味着,如果您拥有共享库或具有“相同”类型的静态库,但是它们具有自己的单独副本,则类型信息将不匹配。

这可能取决于您的情况。

我相信您必须在any中定义一个使用默认可见性声明的公共共享库中的类型。那应该使所有其他共享库都使用该副本,而不是其静态库中的隐藏副本。