在mingw中跨共享库的std :: any

nyo*_*ain 5 c++ mingw std shared-libraries c++17

当在共享库边界上使用libstdc ++的std :: any mingw实现时,我偶然发现了一个问题。它产生一个std::bad_any_cast明显不应该(我相信)的地方。

我使用mingw-w64,gcc-7并使用-std = c ++ 1z编译代码。

简化代码:

main.cpp:

#include <any>
#include <string>

// prototype from lib.cpp
void do_stuff_with_any(const std::any& obj);

int main()
{
    do_stuff_with_any(std::string{"Hello World"});
}
Run Code Online (Sandbox Code Playgroud)

lib.cpp:

将被编译到一个共享库中,并与main.cpp中的可执行文件链接。

#include <any>
#include <iostream>

void do_stuff_with_any(const std::any& obj)
{
    std::cout << std::any_cast<const std::string&>(obj) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

尽管任何传递到的do_stuff_with_any都包含字符串,但这会触发std :: bad_any_cast 。我深入研究了gcc的any实现,它似乎使用了静态内联成员函数(根据所存储对象的类型从模板结构中选择的管理器)的地址进行比较,以检查是否包含所请求类型的对象。而且此函数的地址似乎在共享库的边界上发生了变化。

是不是std :: any不能跨共享库边界工作?该代码是否在某处触发UB?还是这是gcc实施中的错误?我很确定它可以在linux上运行,所以这只是mingw中的错误吗?是已知的还是应该在某个地方报告?对(临时)解决方法有什么想法吗?

Rod*_*dez 4

虽然这是一个关于 Windows DLL 如何工作的问题,并且从 GCC 8.2.0 开始,这个问题仍然存在,但可以通过将 any 标头内的 __any_caster 函数更改为以下内容来轻松解决此问题:

template<typename _Tp>
void* __any_caster(const any* __any)
{
  if constexpr (is_copy_constructible_v<decay_t<_Tp>>)
{
#if __cpp_rtti
  if (__any->type().hash_code() == typeid(_Tp).hash_code())
#else
  if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage)
#endif
    {
      any::_Arg __arg;
      __any->_M_manager(any::_Op_access, __any, &__arg);
      return __arg._M_obj;
    }
}
  return nullptr;
}
Run Code Online (Sandbox Code Playgroud)

或者类似的东西,唯一相关的部分是#if 中包含的比较行。

详细来说,管理器函数有 2 个副本,一个在 exe 上,一个在 dll 上,传递的对象包含 exe 的地址,因为那是它的创建位置,但是一旦它到达 dll 端,指针就会与dll 地址空间中的地址永远不会匹配,因此应该比较类型 info hash_codes。