【问题标题】:What is a good way of handling ABI-differences between libc++ and the older libstdc++?处理 libc++ 和较旧的 libstdc++ 之间的 ABI 差异的好方法是什么?
【发布时间】:2013-07-01 06:27:51
【问题描述】:

在 Mac 上处理 libc++ 和 stdlibc++ 之间的 ABI 不一致的好方法是什么(如果有)?

问题:许多 c++11 功能需要 C++ 标准库的新 libc++ 实现。但是 libc++ 与旧的 libstdc++ 不兼容 ABI,而目前大多数软件通常链接到后者。例如,系统编译器仍然使用 stdlibc++,这意味着我所有使用 macports 安装的库对于像字符串这样的标准类都有不同的 ABI,并且无法与大量使用 c++11 的项目链接。

我目前的解决方案:保留两个版本的库,这通常会导致问题(boost、opencv 等)并链接到适当的版本。

我想有人可能会建议,如果我真的想使用 libc++,我应该使用 stdlibc++ 清除我的系统中的任何内容,并确保来自 macports(或其他任何地方)的任何内容仅与 libc++ 链接。您可以看到这项任务看起来是多么艰巨。

有没有人想出一个很好的方法来与我们生活的这个“between-stdlib-limbo”相关联? :)

编辑:我正在更明确地提出一个隐含的后续问题:Apple 在他们的系统中同时提供了 libc++ 和 libstdc++。假设一个人攻击了根本问题并尝试切换到 libc++-only。鉴于当前安装在系统上的 100% 库(一些随系统附带,其中大部分通过 macports,少数通过手动编译)链接到 libstdc++(如果有的话),从 libstdc++ 切换到 libc++ 的推荐方法是什么)?有没有人这样做并幸存下来?

【问题讨论】:

  • 所有链接在一起的代码应该使用相同的标准库实现和版本。其他一切都被称为不兼容,并且从未打算工作。
  • 是的,当然。这就是整个问题——我们同时有两个不兼容的标准库实现在流通。我的问题是人们采取了哪些策略来处理这个问题。
  • 这就是策略。还有更多的标准库实现,它们彼此不兼容,并且将永远保持这种状态。

标签: c++ macos c++11 macports


【解决方案1】:

你不会喜欢这个的。

您根本无法拥有使用其定义在不同系统上更改的类型的 ABI。当您有一个使用 std::string(例如)的 ABI,然后使用不同的编译器或在不同的平台上编译它时,您就是这样做的。

在解决这个问题时,我的首选方法(如果可能的话)是从 ABI 中消除所有编译器特定的内容,并将其替换为本机类型或您的库定义的类型。反过来,这些库定义的类型必须只在 ABI 中公开非编译器的内容。

有时您可以通过简单的替换来解决问题。其他时候你需要采取更激烈的步骤。一种方法可能是围绕std::string 之类的东西提供一种桥接器或包装器类。也许是这样的:

class MyString
{
public:
  virtual const char* c_str() const = 0;
  static MyString* Make();
  virtual MyString* Clone() const = 0;
protected:
  MyString();
};

在图书馆本身:

class MyStringImpl
:
  public MyString
{
public:
  const char* c_str() const
  { 
    return mStr.c_str();
  }

  MyString* Clone() const
  {
    return new MyStringImpl (*this);
  }

  MyStringImpl ()
  {
  }

  MyStringImple (const MyString& rhs)
  :
    mStr (rhs.c_str())
  {
  }
private:
  std::string mStr;
};

MyString* MyString::Make()
{
  return new MyStringImpl;
}

毛。可能效率低下。很多代码要写。但这是你自己画的角落。

【讨论】:

  • “会从 ABI 中消除所有编译器特定的东西,并将其替换为任一本机类型”请注意,即使是 int 和 long 之类的东西的大小也会因编译器而异,以及偶数 POD 的实际布局。
  • @PlasmaHH:公平点。在实践中,我会使用固定大小的类型,例如uint32_t 等。
  • 来自 *nix 方面我有点被宠坏了,因为自 2004 年以来(对于 gcc)和其他编译器(例如 intel 和 oracle 通常提供 gnu-abi 模式)拥有相同的编译器 ABI ,而 MSVC 每隔一年就会打破其 ABI,迫使开发人员探索 COM 天堂 :)。即便如此,我认为 POD 在内存中是线性布局的,并且初始填充为零。 POD 的 ABI 不兼容性是来自未定义的成员间填充,还是存在一些松散定义(或未定义)的附加点?
  • @kamjagin:实际上 MSVC 打破 ABI 并不是为了强迫你使用 COM,而是强迫你不要依赖实现细节。 GCC 采用的方法看似合理,但存在一个固有缺陷:GCC 项目不控制标准,因此不能保证他们的 ABI 不会被新标准(例如 std::list: :size 要求在 C++11 中发生了变化)
【解决方案2】:

除非“隐藏”使用标准库类型,在一个程序中混合使用 libc++ 和 libstdc++ 是完全安全的,其中一些 TU(或库或模块)使用 libc++,而另一些则使用 libstdc++。只要 TU 之间的接口不使用不兼容的类型,就不会有任何问题。

libc++ 使用内联命名空间来帮助确保 ABI 不兼容的类型不会被误认为是另一个;如果接口直接使用 libc++ std::string,则期望 libstdc++ std::string 的库将不会链接到该接口,因为实际符号不同:std::stringstd::__1::string

libc++ 还确保异常和动态内存分配等低级功能与 ABI 兼容(假设您使用相同的 abi 库构建 libstdc++ 和 libc++),因此在使用 libc++ 的 TU 中释放内存是安全的。内存是使用 libstdc++ 在 TU 中分配的,或者从基于 libc++ 构建的代码中抛出异常并使用 libstdc++ 在代码中捕获它。

当接口中的类型隐藏标准库类型时可能会出现问题;给定一个使用struct S { std::string s; }; 的接口,S 类型的定义将根据 TU 认为的 std::string 是不同的,因此违反了单一定义规则。


不过,您的真正问题似乎在于那些确实在接口中使用标准库类型的库。

我想有人可能会建议,如果我真的想使用 libc++,我应该使用 stdlibc++ 清除我的系统中的任何内容,并确保来自 macports(或其他任何地方)的任何内容仅与 libc++ 链接。您可以看到这项任务看起来是多么艰巨。

您只需要确保在接口中使用标准库的 TU 使用 libc++。您不需要完全清除 libstdc++。在其接口中不使用标准库的库仍然可以继续与 libstdc++ 链接。

编辑:我正在更明确地提出一个隐含的后续问题:Apple 在他们的系统中同时提供 libc++ 和 libstdc++。假设一个人攻击了根本问题并尝试切换到 libc++-only。鉴于当前安装在系统上的 100% 库(一些随系统附带,其中大部分通过 macports,少数通过手动编译)链接到 libstdc++(如果有的话),从 libstdc++ 切换到 libc++ 的推荐方法是什么)?有没有人这样做并幸存下来?

请记住,只有在接口中使用标准库时才重要。希望这些库在为 OS X 构建时已经自行切换到 libc++。如果没有,也许他们会接受补丁来这样做。

使用适当的库构建自己的二进制文件不是“黑客”;如果没有上游项目为您做这件事,这是正确的做法。如果您始终在自己的代码中使用 libc++,那么您将不需要构建任何库的多个版本;只有 libc++ 版本。


libc++的abi兼容性参考:http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-September/024594.html

【讨论】:

  • 谢谢 - 你完全正确,我高估了从 libstdc++ 切换到 libc++ 的问题。鉴于受人尊敬的提升(文件系统)是罪魁祸首之一,我认为许多已建立的库也在其接口中使用标准库,但情况似乎并非如此(除了其中许多仅 c 语言))。
  • “希望这些库在为 OS X 构建时已经自行切换到 libc++。如果没有,也许他们会接受补丁来这样做。”我怀疑在 Apple 将 Xcode 中 C++ std 库选项的编译器默认值从 libstdc++ 切换到 libc++ 之前会发生这种情况,因为这些库会引入与默认 std lib 相同的问题,它们为非默认值修复。
  • 我在这里只看到 3rd 方库提供商的两个选项: - 提供两种风格的所有二进制文件,一种针对 libstdc++,另一种针对 libc++ - 在他们的 API 中摆脱所有 stdlib 的使用(这将是一个破坏性的 API 更改)
  • @Kaiserludi xcode 项目的默认设置已经有一段时间了。
  • 新项目的模板将项目设置设置为显式覆盖该默认值以使用自 Xcode 4.5 以来的 libc++,但编译器默认值仍为 libstdc++,即使使用 Xcode 6.3,也不会破坏现有项目。跨度>
猜你喜欢
  • 2012-02-18
  • 1970-01-01
  • 2017-04-07
  • 2013-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-12
相关资源
最近更新 更多