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

Mic*_*iak 6 c++ gcc casting clang c++17

我正在开发一个使用std::any.

最近我发现当我用 clang 编译它时,我bad_any_cast在其中之一上遇到了异常std::any_casts。

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

我试图编写简单的程序来演示它,但我无法重现它。

值得一提的是:我正在传递一包std::any(每个包含不同的类型)并且只有一个有问题(它的 a 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;
    }
Run Code Online (Sandbox Code Playgroud)

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

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

你们有什么建议或提示我该如何处理吗?

编辑

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

文件:

库文件

#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;
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

主程序

#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;
}
Run Code Online (Sandbox Code Playgroud)

生成文件

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
Run Code Online (Sandbox Code Playgroud)

所以我有 2 个共享库,它们链接负责std::any创建和转换的公共静态库。当std::any在静态库的一个站点中创建并在另一个站点中进行转换时,我得到一个异常。

Zan*_*ynx 5

如果你在像 Linux 或 OS X 这样的 Unix 上,C++ 类型 ID 由指向类型信息的指针处理,而不是名称字符串匹配。

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

这可能是也可能不是您的情况。

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