处理libc ++和旧libstdc ++之间ABI差异的好方法是什么?

时间:2013-07-03 14:03:08

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

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

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

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

我想有人可能会建议,如果我真的想要使用libc ++,我应该使用stdlibc ++清除我的系统,并确保macports(或其他任何地方)的任何内容只与libc ++链接。你可以看到这个任务看起来多么令人生畏。

有没有人找到一个很好的方式来解决我们生活在这个“stdlib-limbo”之间的问题? :)

编辑:我更明确地提出了一个隐含的后续问题:Apple将libc ++和libstdc ++与他们的系统一起发布。假设有人攻击了潜在的问题并尝试转换到libc ++ - 仅。从libstdc ++切换到libc ++的推荐方法是什么,因为系统上当前安装的100%的库(一些随系统一起提供,大多数通过macport,一些通过手动编译)链接到libstdc ++(如果有的话) )?有没有人这样做并活了下来?

2 个答案:

答案 0 :(得分:13)

除了标准库类型的“隐藏”使用之外,将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 ++ - 仅。从libstdc ++切换到libc ++的推荐方法是什么,因为系统上当前安装的100%的库(一些随系统一起提供,大多数通过macport,一些通过手动编译)链接到libstdc ++(如果有的话) )?有没有人这样做并活了下来?

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

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


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

答案 1 :(得分: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;
}

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