【问题标题】:std::any across shared library bounding in mingwmingw中跨共享库边界的std :: any
【发布时间】:2017-07-24 21:22:35
【问题描述】:

我在使用 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"});
}

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

这会触发 std::bad_any_cast 尽管传递给 do_stuff_with_any 的 any 确实包含一个字符串。我深入研究了 gcc 的 any 实现,它似乎使用了静态内联成员函数(根据存储对象的类型从模板结构中选择的管理器)地址的比较来检查 any 是否包含请求类型的对象. 而且这个函数的地址似乎跨越了共享库的边界。

std::any 不能保证跨共享库边界工作吗?此代码是否会在某处触发 UB?或者这是 gcc 实现中的错误?我很确定它可以在 linux 上运行,所以这只是 mingw 中的一个错误吗?是已知的还是应该在某处报告?关于(临时)解决方法的任何想法?

【问题讨论】:

标签: c++ shared-libraries mingw std c++17


【解决方案1】:

虽然这确实是关于 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;
}

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

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

【讨论】:

  • 你测试过这个吗?或者为什么 typeid() 没有同样的问题?
  • 我做了,但我找到了一个不需要更改系统标题的解决方法,所以我没有使用它。 typeid 有效,因为它是关键字,而不是模板。
  • @RodrigoHernandez,您能否发布您的解决方法作为答案?
猜你喜欢
  • 2015-09-23
  • 1970-01-01
  • 2016-11-15
  • 2022-08-05
  • 2018-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多