【问题标题】:How to load function with dlsym() without reinterpret_cast?如何在没有 reinterpret_cast 的情况下使用 dlsym() 加载函数?
【发布时间】:2019-08-17 04:14:24
【问题描述】:

我正在尝试使用 clang-tidy 来执行 C++ 核心指南。虽然它确实有很多有效点,但有一件事我无法真正解决:dlsym 返回一个void*,我需要以某种方式将其转换为正确的函数指针。为此,我使用reinterpret_cast。由于指南禁止这样做,我对此有警告。 当然,我可以将//NOLINT cmets 放在任何地方,但我正在寻找不使用reinterpret_cast 的解决方案,因此警告消失了。

这个问题有解决办法吗?

【问题讨论】:

  • 没办法。这是一个众所周知的问题。
  • 从语言的角度来看,仅仅从void* 转换为函数指针类型是粗略的。对象指针和函数指针不是兼容的类型,甚至不能保证具有相同的大小。尽管任何提供dlsym 的平台编译器都必须以某种方式与之抗衡。事实上,通过void* 返回函数指针的函数确实存在问题。
  • @FrançoisAndrieux 从对象指针转换为函数指针,反之亦然,有条件地支持。如果一个平台提供dlopen,它可能支持这样的转换。
  • dlsym 创建一个包装器,因此您只需在包装器中使用一次//NOLINT
  • @FrançoisAndrieux 该标准要求如果一个实现支持有条件支持的功能那么它必须遵守某些规则。是的“应该产生原始指针值”就是这样的规则。

标签: c++ clang-tidy


【解决方案1】:

除了reinterpret_cast 之外,语言中没有其他方法可以将函数指针类型转换为对象指针类型。这样做是实现定义的行为[expr.reinterpret.cast]/8

有条件地支持将函数指针转换为对象指针类型或反之亦然。这种转换的含义是实现定义的,除非实现支持双向转换,将一种类型的纯右值转换为另一种类型并返回,可能具有不同的 cv 限定,应产生原始指针值。

这意味着一个符合标准的 C++ 编译器必须记录如果它确实支持这个特性。而且,如果它确实支持它,它必须记录它的行为方式。您可以依赖它在该编译器上以记录的方式工作(或不可用)。

关于核心指南 linting:如果您必须将 //NOLINT 放在“任何地方”,那么这似乎意味着您在很多地方都在调用裸 dlsym()。例如考虑包装它

template <typename T>
inline T* lookupSymbol(void* module, const char* name)
{
    auto symbol = reinterpret_cast<T*>(dlsym(module, name));  // NOLINT

    if (!symbol)
        throw std::runtime_error("failed to find symbol '"s + name + '\'');

    return symbol;
}

【讨论】:

  • @KonradRudolph 上面引用的段落似乎是在 C++11 中引入的,所以在我看来,至少这些扭曲应该不再需要了……
  • 我在函数中添加了using pT = T*;,然后我可以使用像pT(dlsym(module, name)) 这样的函数样式转换,而clang-tidy 不会为此而烦恼。
  • @Calmarius 好吧,这只是做同样事情的一种非常迂回的方式。在这种情况下,您的函数样式转换被定义为等同于 C 样式转换 [expr.type.conv/2],在这种情况下,它被定义为等同于 reinterpret_cast [expr.cast/4]...
猜你喜欢
  • 2011-01-22
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 2018-04-12
  • 2012-05-22
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多