如何使用源代码中的执行策略检测C++ 17的扩展内存管理算法的可用性?

Fra*_*kHB 6 c++ c++17

P0040R3(采用2016-06,另见N4603)介绍了一些扩展内存管理算法,如std::uninitialized_move_n草案,最后它成为ISO C++ 17的一部分.其中一些有一个额外的过载,带有一个ExecutionPolicy参数,可能支持并行性.

但是,截至目前(2018年8月),我发现这些重载的实现并未附带任何标准库实现.我检查过的实现文档并没有很好地阐明它.具体来说,(目前)它们是:

  • libstdc ++表明它不支持主干中的P0040R3,但实际上至少std::destroy_atstd::uninitialized_move_n不支持ExecutionPolicyGCC 8.2.
  • 自4.0以来,libc ++对P0040R3的"完全"支持,但ExecutionPolicy实际上缺少重载.
  • Microsoft VC++支持P0040R3,因为VS 2017 15.3带/std:c++17/std:c++latest,但ExecutionPolicy实际上缺少重载.

ExecutionPolicy我知道的唯一具有重载的实现是在HPX中,但这不是标准库的完整实现.如果我想以便携式方式使用这些功能,我必须同样适应自定义实现,而不是直接使用std名称.但我仍然希望std将来使用实现作为首选项(除非他们已知错误).(原因是实现定义的执行策略与具体实现紧密结合,因此外部实现以及它们的客户端代码通常可能没有机会利用各种执行策略;尽管对于客户端代码来说不一定如此.在符合标准的意义上不保证便携性.)因此,我希望在我的便携式自适应层中有条件包含的东西用于实现 - using std::...在标准库提供时获得指定的功能,并用我的实现补充它仅在必要时作为标准库实现中缺少部分的后备.

众所周知,SD-6功能测试宏以及P0941R2显示__cpp_lib_raw_memory_algorithms对于P0040R3中的功能已足够.另一方面,__cpp_lib_parallel_algorithm似乎根本没有关系<memory>.所以没有办法像当前的libc ++和MSVC实现一样表达状态 - std名称来自P0040R3但没有ExecutionPolicy重载.而且我不确定__has_include<execution>会不会工作.现实可能更奇怪,例如,libc ++甚至不支持P0336R1.

那么,如果在某些较新版本的标准库实现中(有希望)可以使用这些功能,除了检查每个版本的源代码,或者完全重新发明整个P0040R3的轮子之外,如何在我的代码中使其完全可移植?

编辑:

我知道功能测试宏的用途,我认为libstdc ++已经做了正确的事情.但是,还有改进的余地.更具体地说,我的可移植层代码将扮演实现的角色(如HPX),但在标准库实现已经提供轮子时,不再重新发明轮子的意义上更"轻量级":

namespace my
{
#if ???
//#if __cpp_lib_raw_memory_algorithms
using std::uninitialized_move_n;
// XXX: Is this sufficient???
#else
// ... wheels here ... not expected to be more efficient to std counterparts in general
#endif
}
Run Code Online (Sandbox Code Playgroud)

所以我的客户端代码可以是:

my::uninitialized_move_n(???::par, iter, size, d_iter);
Run Code Online (Sandbox Code Playgroud)

而不是(复制自Barry的回答):

#if __cpp_lib_raw_memory_algorithms
std::uninitialized_move_n(std::execution::par, iter, size, d_iter);
#else
// ???
#endif
Run Code Online (Sandbox Code Playgroud)

这两段代码都可以工作,但显然__cpp_lib_raw_memory_algorithms直接在客户端代码中的任何地方进行检查都会更加昂贵.

理想情况下,我应该有一些完整的最新标准库实现,但我并不总能保证(特别是在标准库作为系统库的一部分安装的环境中工作).无论如何,我需要适应以减轻客户的工作.

后备显而易见:using std::uninitialized_move_n;完全避开路径.我担心这将是一个悲观的实现,所以我想尽可能避免这种方法.

第二次更新:

因为" 完全便携"听起来不清楚,所以我在上面的编辑中说明了一些代码.虽然这个问题没有改变,仍然被标题所涵盖,但我将在这里更具体.

我想在问题中"完全可移植"的方式被限制为,考虑到上面编辑的代码,填写标记的任何部分???,而不依赖于任何特定版本的语言实现(例如,没有像宏名称那样依赖于实现应该用于此目的).

请参阅此处此处,以查找代码示例无法满足条件.(当然,这些版本是通过提交日志...肯定不完善的检查想通了,而且还是越野车在某些情况下).请注意,这是不相关的重载用ExecutionPolicy,但因为它们在提及的标准库实现失踪,我的下一步行动取决于这个问题的解决方案.(但名称的未来std应该是明确的.)

一个完美的(足够的)解决方案可以是,例如,添加一个新的功能测试宏,使过载独立__cpp_lib_raw_memory_algorithms于未来,我可以添加我的重载实现,ExecutionPolicy当它们没有被独立的新功能测试检测到宏,没有搞乱#if再次的状况.但我当然不能保证这种方式是可行的; 它最终取决于委员会和供应商的决定.

我不确定是否还有其他方向.

Bar*_*rry 4

P0941的初始版本包含一个表格,清楚地表明P0040R3具有相应的功能测试宏__cpp_lib_raw_memory_algorithms。这意味着编写代码以有条件地使用此功能的正确、可移植的方法是:

#if __cpp_lib_raw_memory_algorithms
std::uninitialized_move_n(std::execution::par, iter, size, d_iter);
#else
// ???
#endif
Run Code Online (Sandbox Code Playgroud)

强加的要求是,如果定义了该宏,则该函数存在并执行标准规定的操作。但那个没有被定义的宏并没有真正说明什么。正如您所指出的,P0040R3 的某些部分是在 libstdc++ 中实现的 - 部分,但不是全部,这就是未定义功能测试宏的原因。

目前正在共同努力在 libstdc++ 中实现并行算法。

至于在那里的分行做什么#else,嗯……你就得靠自己了。