C ++ / Mavericks最佳实践

本指南大多要解决如何在Biocumons包中调整C / C ++代码,以构建Mac OS X 10.9(Mavericks)。如果您的包不使用C或C ++代码,则可以假设此文档与您无关。

表的内容

除最后一节外,本文档的编写是为了让读者从头读到尾。

你可以直接跳到教训了检查您的问题是否已探讨的部分。

方向

注意:为简单起见,本指南使用“GCC”(GNU编译器集合)和“clang”引用每个相应的工具集合(包括C ++编译器),而不是简单地简单地“GCC C编译器”或“Clang C编译器”。“mavericks环境”是指默认为Mavericks提供的Clang和Xcode版本的组合。

随着r的Mavericks的释放,Cran和Biocomonder已经采用Apple的首选工具链,为Mavericks平台建造包装。使用OS X默认组合,在Mavericks平台上构建了Biocumons包铿锵声Xcode.

Mavericks环境的引入已经揭示了建筑包的许多问题,其中大多数是由于C / C ++代码依赖于GCC依赖于GCC的方式。过渡到小牛的大多数问题都是由C ++编码实践引起的,这些规范普遍认为是有问题的,并且通过遵守建立而解决最佳实践

以下是一些常见的问题来源,作为Mavericks环境如何与GCC形成对比:

在小牛队的环境下进行开发有什么不同?

最大的变化是铿cl的引入libc ++c++ 11标准库和与Xcode相关的库头的实现。clang获得市场份额的原因有几个,其中一个原因是clang只是作为基于c语言的编译器。“叮当”的拥护者认为:

有关编译器标志的指导,在开发时使用,请参阅相关计划指南页面的相关部分

未指定行为的差异,内存寻址策略

在转换过程中遇到的一些错误似乎是由于依赖于不可移植的未指定的行为。请参阅本指南的有关章节未指定的行为想要查询更多的信息。

例如,C / C ++存储器寻址的许多方面是依赖于实现的,这意味着C / C ++标准的预期行为(“未指定的”)不规定,因此可以编译编译器来决定。

关于内存的最重要的区别是clang似乎对越界内存寻址更有限制。

如何查找bug

使用GCC首选调试器是GDB.,但许多人更喜欢LLDB.用于调试Mavericks环境。支持LLDB在其他平台上此时有限。

由于包中的许多错误与内存寻址或布局错误有关,因此单独依赖于调试器可能不足以跟踪内存错误。valgrind.是检测内存错误的首要工具。

请参阅生物导体指南调试C / C ++代码有关使用调试器和Valgrind的示例。

如果我无法访问mavericks机器怎么办?

使用Mavericks Machine无法替代Mavericks Machine来解决Mavericks失败的软件包。许多所看到的错误只能以Clang,Xcode和OS x 10.9的组合可重复。

但是有几种选择缺乏采购小牛机:

  1. 作为一个探索性措施,安装一个最新版本的GCC并编译你的包化c++ 11或者-std = gnu ++ 11编译器参数(见套餐指南有关开发编译器标志的信息);作为版本4.8.1.,GCC实现了2011年ISO C ++标准的所有主要功能。最近的GCC版本,错误和警告的诊断也大大提高。用一个c++ 11实现可能揭示警告或错误,指向在Mavericks遇到的相同问题。

  2. 安装clang;这是有限的价值,因为许多错误对Mavericks环境是独一无二的。

  3. 使用Valgrind获取内存解决问题;由于Mavericks平台上的许多错误与内存解决问题有关,因此在Linux上使用Valgrind同样可发现许多错误。

  4. 如果您无法使用组合诊断您的问题构建系统输出和valgrind,随意联系BioC-Devel邮件列表

c++ 11

虽然Mavericks上的rang默认版本包括对所有C ++ 11个功能的支持,但C ++ 11的Biocumonductions支持依赖于具有最古老的工具链的平台。因为目前的雪豹(Mac OS X 10.6.8)工具链不支持任何C ++ 11功能,因此生物导体包一般不应使用C ++ 11个功能。最终,当Mavericks更广泛采用时,将丢弃对雪豹的支持。

C ++ 11是不是完全兼容较旧的标准。

可以告诉Clang使用标准库的旧版本(默认值libc ++),但是依赖于特定OS版本的编译设置并不是一个可行的长期解决方案。这种方法极大地增加了包作者的维护负担,并限制了Bioconductor团队提供支持的能力。

代码应适用于避免倒退或转发的构造。看看前进不相容问题部分例子。

Mavericks环境特有的问题

C连锁

C ++用途extern“c”要授予声明C链接,因此使C代码可访问的声明。一些R.标题时#包括d中应该包含的c++系统头文件不是C链接。据有关人士称书写R扩展手册部分R.标题文件应该是不是被包括在extern“c”块。

不良连锁的典型症状是在包装中加载时间(不是编译或链接时间)错误表示找不到特定符号。

解决方案:所有R.头应该是#包括D.外部extern“c”块。

正确的例子#包括R头:

#include  extern“c”{void foo();//函数'foo'和此块中的其他代码有c链接...} extern“c”void bar();//函数'bar'有c链接

openmp.

从这个写作,Mavericks环境不支持OpenMP,如果Apple发布的工具才会出现。

代码不应依赖OpenMP的可用性。独立于OpenMP支持的关注,应从开始中写入代码,以便在单线程环境中劣化。

看看书写R扩展手册部分有关R软件包中的OpenMP代码的信息,并检测支持。

解决方案:如果OpenMP支持不可用,则使用预处理器如其他指令所以如果不可用:

#ifdef support_openmp //多线程openmp版本代码#else //单线程版本的代码#endif

看看缩短包围绕支持OpenMP的良好实践的示例。

C ++最佳实践

这些c++实践适用于大多数c++项目,但已经被Bioconductor社区确定为特别有助于避免Mavericks环境中的问题。

使用RCPP.

rcpp.CRAN包允许与c++无缝集成R.,并且是跨平台。该套餐为此提供了许多相同的福利R.C界面使C ++如此吸引人的语言,同时消除了许多编程的陷阱R.界面。

该包装有很好的记录,并且具有广泛的工作示例存储库,可以获得许多任务:RCPP画廊

避免名称解析错误

A name resolution error occurs when the compiler encounters an identifier (e.g., variable or function name) that is ambiguous (that is, there is a “collision” between two or more identifiers), or name lookup rules lead the compiler to resolve a name incorrectly. clang is more exacting about identifiers.

名称解析错误的典型症状是编译器抱怨函数GROT的类型或数量与它所期望的不同,并且编译器指向标准库中的C ++头文件。

编写R包时有两个名称解析的主要问题:

从R头重新映射标识符

为了方便,R.别名常见标识符来自R.标题。例如。,rf_length(sexp)就变成了长度(性别).虽然这可能方便C,在Mavericks环境中的标题组织似乎导致更多的碰撞而不是GCC。见相关部分写作r扩展手册

解决方案:防止重新映射R.通过定义R_NO_REMAP符号来为C ++代码的标识符。这可以在包级别与-dr_no_remap预处理器标志或文件逐个文件完成#define r_no_remap..使用完全合格的版本R.标识符,通常通过前置rf_

示例摘录从预防重新映射的头文件中摘录:

file CxxCode.h ------------------ #ifndef CXX_CODE_H #define CXX_CODE_H #ifdef __cplusplus #define R_NO_REMAP #endif…void foo(SEXP s) {if(Rf_length(s) > 1) //完全限定:'Rf_length'…} # endif

笔记rf_length.只是许多人的一个例子R.可能与C ++标准库标头中的名称冲突的标识符。

命名空间卫生

在C ++中介绍了名称空间,以限制名称冲突的发生率。然而,许多作者新增C ++,但是使用using引用(特别是'使用命名空间std.“指令”不必要地,从而重新引入问题名称空间意味着解决。

所指出的是CPPreference Notes部分使用使用命名空间std.指令介绍名称解析的整个STD命名空间。在标准库中的所有标题中都有很高的可能性,存在与包中的标识符冲突的标识符。

解决方案:避免'使用命名空间std.'完全指导如果可能的话,尤其在头文件中。更喜欢使用-宣言过度使用 - 指令或简单地使用完全合格版本的标准库标识符。新的C ++作者高估了包括范围决议运营商(即')的多少STD ::')影响可读性。

介绍STD命名空间标识符的示例:

#include  #include  //假设我们要访问std :: map和std :: make_pair标识符

许多新的C ++作者将使用使用指令来介绍所需的标识符。避免如果可能的话:

使用命名空间std;//为分辨率引入整个STD命名空间

一个替代方案使用 - 声明(例如,'使用std ::地图;',允许手工挑选标识符引入(与整个STD命名空间相反);这里我们只是想std ::地图std :: make_pair..即使我们想要的标识符列表相当长,我们只需要一个单一的使用声明:

使用std ::地图;//“Map”和“make_pair”在声明范围中使用std :: make_pair引入;

使用 - 声明也可以是块范围。这是在全球范围的使用声明中使用的,因为它可以防止在全球范围内不必要地介绍良好命名空间卫生的宗旨:

void foo(){使用std :: map;使用std :: make_pair;地图 m;M.Insert(make_pair(5,7));...}

一个完全良好的替代方案是简单地将标准的库标识符与“STD ::,大多数c++程序员都习惯阅读:

void foo(){std :: map  m;M.Insert(STD :: Make_Pair(5,7));}

避免未定义的行为和非便携式未指定的行为

C或C ++标准没有规定的两种主要类别:

有问题的未定义或未指定行为的典型症状是赛格虚虎库,只会出现在Mavericks环境中。此前未发现问题的原因可能是GCC默默地允许代码执行代码而不是崩溃程序。

解决方案:防守代码以避免有问题的构造和使用调试器找到导致错误的代码。

来自外部源代码的问题

有些软件包需要使用贡献者直接写的代码。最常见的方案是包括由第三方编写的库的源代码。某些包装还使用代码生成工具产生的代码,例如,SWIG..首先,看相关套餐指南部分有关外部源代码的指导。

看看经验教训本指南的一部分有关特定代码来源的建议。

生成的代码

某些包使用由第三方工具生成的代码,即机器编写的代码。Swig是一个常见的例子。

由机器编写的代码的问题是代码意味着由机器。例如,SWIG生成的每个代码文件的顶部都声明,不打算手动阅读或编辑代码。

由于许多代码生成工具对Mavericks环境的假设进行了无效,因此代码需要人们注意解决错误;但由于机器书面代码的不可思议性质,很难隔离错误。

解决方案:如果可能,重新生成问题代码,否则用手修复。用手修理非常沮丧

第三方代码

某些软件包包括未以编译器的方式写入的第三方库,因此不要在Mavericks环境中建立开箱即用。

偏好近似顺序的解决方案:

  1. 检查是否存在cr或Bioconductor软件包提供相同的功能,同时满足您的用例的性能需求。从您的包中消除第三方代码可以大大减少维护负担。

  2. 检查库是否已更新。具有活动用户社区的某些库会激发为更多编译器/环境添加支持的更新。

  3. 检查维护者是否知道库不适用于Mavericks环境,并找出支持即将到来。通常很容易直接联系由个人或小组维护的图书馆作者。

  4. 使用积极维护的备用库,提供等价的功能。有时,如果一个库不再被维护,那是因为这个库已经被提供相同功能的可选项目抛弃了。

  5. 用手更新包中包含的库代码。非常沮丧.维护者假设与主线源项目保持对代码保持责任。如果进行,记录所需的更改的描述,以便更新代码库时,可以轻易再现更改。

从具体问题中吸取的教训

本节用作有关特定问题及其解决方案的知识的松散组织的存储库。预计不会全面。随着知识库的增长,将添加物品。Biocometions渴望建议;请写这件事BioC-Devel邮件列表如果你有任何!

如果您不知道从哪里开始诊断损坏的包裹,那么它可能值得浏览所有这些部分。

在相关的位置,该问题被标记为在编译时或运行时可发现。

如果适用,提供了与实时代码演示的链接。

C ++ 11的前向不兼容问题

C ++ 11并不完全向后兼容。特别是,标准库的某些部分的API在微妙的方式中略微发生变化。大多数问题需要最小的调整来修复。

容器迭代器Const-ness

类型:编译时间

标准库容器上的许多操作现在需要迭代器const.两个准备的例子是擦除采用迭代器参数的方法。

迭代标准库容器

类型:运行时

一般来说,使用特殊的过去的除了平等检查之外的迭代器值(即,==或者!=)导致未定义的行为。特别是,在Mavericks环境中:

对于递增超越的演练示例过去的,看看诊断 - 崩溃示例在C/ c++调试页面。

外部代码源

外部代码源的常见示例是SWIG.促进,以及许多文件格式库。来自外部源代码的代码有时以非编译器独立的方式编写。检查文档,看看是否支持Mavericks环境。

促进

促进是一个免费的、经过同行评审的c++库的来源,它增强了c++语言。Boost的很多部分都是“头文件”,这意味着它们不需要单独编译,头文件只需要出现在搜索路径中,以便客户机代码使用它们。

许多Boost库是平台独立的,但不是所有的。一些Boost库或者正在添加Mavericks环境支持,或者库的作者已经宣布将增加Mavericks环境支持不是被添加。

偏好近似顺序的解决方案:

  1. 使用BH.如果可能的话,在Cran上包装。BH包提供了几个Boost标头库。使用BH包意​​味着在包装中使用Boost的维护成本几乎没有。

  2. 更新您包含包装的Boost库。Boost库有时包含错误,或者稍后更新以增加对其他平台的支持。它是Biocuconducts包维护者有责任让更新包中的所有代码保持更新。

  3. 联系特定Boost库的作者。如果您无法找到关于支持MAVERICKS环境的公告,可能值得联系图书馆作者询问。

SWIG.

SWIG在C / C ++和其他语言编写的代码之间生成代码。在这种写作时,对Clang的SWIG支持是有限的,并且SWIG特别有克朗的LIBC ++版本(C ++ 11)标准库的问题。一些问题仅限于通过调整功能签名来解决的问题。其他问题在SWIG生产代码的方式深深嵌入。

写这篇文章时,在SWIG-DEVEL邮件列表上的此线程似乎最深入地讨论了在Mavericks上使用Swig。

偏好近似顺序的解决方案:

  1. 如果可能,请消除SWIG代码。这可能是减少维护负担的最多。

  2. 使用最新版本的SWIG重新生成SWIG代码。在这篇文章时,最近更新了SWIG,包括对C ++ 11的部分支持,这可能会缓解铿cl的问题libc ++.看看SWIG文档关于C ++ 11支持.新版本可能不会产生问题代码。注意代码必须适用于所有支持的编译器。

  3. 手动排除故障并修复错误。非常沮丧.如果进行,记录所需要的变更的描述,这样如果代码被重新生成,变更就可以很容易地重新生成。

    SWIG文件找到关于故障排除的指导。(例如,SWIG-e.在预处理器运行后,切换输出结果。)或许首先删除所有SWIG功能并逐渐添加功能。查找有关如何修复包裹错误的信息。

f2c

f2c是将Fortran77代码转换为C / C ++代码的工具。制作F2C代码跨平台所需的维护负担很大。由于需要Fortran编译器(或仿真器)来安装R.,F2C通常是不必要的。几个包使用本机Fortran代码而不会出现问题。

解决方案:如果可能,请删除F2C的需求。唯一的追索者是Finesse Makefiles到每个支持的平台或多或少的指数都有一个有针对性的makefile。请写这件事BioC-Devel邮件列表如果你有麻烦。