在c++中,如何用回退包装默认标头

In c++, how to wrap default headers with fallback

本文关键字:包装 默认 回退 何用 c++      更新时间:2023-10-16

假设我的代码使用std::array,我想做:

文件:阵列

#pragma once
#ifdef MY_TOOLSET_HAS_STD_ARRAY
#include <array> //recursive include here?
#else
#include <boost/array.hpp>
namespace std
{
   using boost::array;
}
#endif

这样我的项目就可以使用std::array而不必关心编译器/平台。一个问题(至少)是,当std::array可用时,include将是递归的,而我真正想要的是(从语义上)"包含如果这个include不存在就会包含的头"。

有什么想法吗?我知道将boost::array拉入std可能也被认为是不好的做法,所以我也对这方面的想法感兴趣。

解决这个"问题"的正确方法是首先不要引入它。

如果您的一些构建环境支持C++11,但其他构建环境不支持,那么请找到一个在所有构建环境下都支持的公共子集并使用它。在这种情况下,该公共子集似乎是Boost。所以你应该使用boost::array

还要考虑,如果你发展&使用std::array进行测试,那么整个代码分支都没有经过测试——使用boost::array的分支。

我完全支持懒惰编程——但聪明懒惰编程。懒惰编程并不意味着笨拙或笨拙的编程,聪明的懒惰编程也不会像将boost::array添加到std命名空间那样调用Undefined Behavior。说"我不想浏览我所有的代码并将std::array更改为boost::array"并不是引入黑客和未定义行为的好理由。它可以像调用sed来进行所有这些更改一样简单,而且可能只需要5分钟。

您可以使用C++11之前的"template-typedef变通方法",它不涉及#定义类型名称,但会使使用类型的语法变得更丑陋:

#ifdef MY_TOOLSET_HAS_STD_ARRAY
    #include <array>
#else
    #include <boost/array.hpp>
#endif
template <typename T, size_t N>
struct fixed_array
{
    #ifdef MY_TOOLSET_HAS_STD_ARRAY
        typedef std::array<T, N> type;
    #else
        typedef boost::array<T, N> type;
    #endif
};

然后您对该类型的使用变为:

typename fixed_array<char, 4>::type some_chars;

然而,仅仅使用boost::array会简单得多。这意味着需要测试的排列更少,从而降低了代码库的维护成本。

这绝对是宏的用例之一:

// in "my_fixed_array.h"
#ifdef MY_TOOLEST_HAS_STD_ARRAY
    #include <array>
    #define FIX_ARRAY std::array
#else
    #include <boost/array.hpp>
    #define FIX_ARRAY boost::array
#fi

// anywhere else
#include "my_fixed_array.h"
FIX_ARRAY<char, 4> some_chars;

这样你就不必到处做一些顽皮的事情,比如把东西放进namespace std

根据编译器实现的健全程度(在这方面大多数都很好),您可以简单地依赖include路径搜索顺序。默认系统包含路径通常在用户指定的"附加"包含路径之前进行搜索。

因此,如果您有一个名为array的标头位于非标准包含路径中,则可以假设如果标准<array>标头不存在,则它将仅包含,因为否则将首先找到系统标头。请注意,您甚至不需要使用此技术的特征检测goop。

(我没有说它很漂亮——这有点滥用构建环境,尽管它相当安全。)