17

什么(如果有)是处理 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++ 的推荐方法是什么)? 有没有人这样做并幸存下来?

4

2 回答 2

13

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

libc++ 使用内联命名空间来帮助确保 ABI 不兼容的类型不会被误认为是另一种;如果接口std::string直接使用 libc++,则期望 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

于 2013-07-03T17:34:40.210 回答
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;
}

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

于 2013-07-03T14:45:19.853 回答