【问题标题】:How should I write my C++ to be prepared for C++ modules?我应该如何编写我的 C++ 来为 C++ 模块做好准备?
【发布时间】:2016-04-11 15:49:16
【问题描述】:

已经有两个编译器支持 C++ 模块:

现在开始一个新项目时,我应该注意什么才能最终在我的编译器中发布模块功能?

是否可以在使用模块的同时保持与不支持它的旧编译器的兼容性?

【问题讨论】:

  • 我有点害怕给一个声望点超过2400的人回答... ^^ 我问自己每次发布新的c++升级时都写真的很重要吗将我的所有代码更改为新功能?这会导致很多副作用或架构变化......如果我是你,我会编写我自己的元语言,它会保持不变(或者我有更多控制权),我会编写一个函数来转移我的语言在我选择的任何其他本地编程语言中。这就像硬件虚拟化的想法。
  • @NECIPS SO 是一个知识库,您不是在回答我,而是在“回答世界”。如果有人亲自问我这个问题,我会说“去写一个测试项目,玩弄它并自己弄清楚”。几年前,有人会这样做并写一篇关于它的博客文章。现在我希望有人会写一个 SO 答案。
  • 也许“Waldo”可以回答你的问题(theres-waldo.ca/2014/07/17/…
  • 这是一个包装问题。最后,一个模块只是资源的一个视图。您可以将“代码资源”用作源、bin+.h 或模块。如果您熟悉打包系统(例如,用于 microsft VSxxx 的 CoApp),则不必太在意这一点。无论如何,关键始终是您区分系统概念的能力...
  • 使用模块意味着使用import 指令导入它们。较旧的编译器不知道 import 指令。因此,不可能使用模块并保持向后兼容。一些预处理器诡计可能会提供出路,但 IMO 不值得。

标签: c++ backwards-compatibility future-proof c++20 c++-modules


【解决方案1】:

已经有两个编译器支持 C++ 模块

叮当声:http://clang.llvm.org/docs/Modules.html MS VS 2015:http://blogs.msdn.com/b/vcblog/archive/2015/12/03/c-modules-in-vs-2015-update-1.aspx

微软的方法似乎是最受关注的方法,主要是因为微软在其实施中投入的资源比目前任何 clang 人都多。请参阅https://llvm.org/bugs/buglist.cgi?list_id=100798&query_format=advanced&component=Modules&product=clang 了解我的意思,C++ 的模块中有一些大的引人注目的错误,而 C 或特别是 Objective C 的模块在现实世界的代码中看起来更有用。 Visual Studio 最大和最重要的客户 Microsoft 正在大力推动 Modules,因为它解决了大量内部构建可扩展性问题,而且 Microsoft 的内部代码是现有的任何地方最难编译的 C++ 代码,因此您不能扔任何编译器除了 MSVC 之外(例如,祝你好运,让 clang 或 GCC 编译 40k 行函数)。因此,Google 等使用的 clang 构建技巧不适用于 Microsoft,他们迫切需要尽快修复它。

这并不是说 Microsoft 提案在实际应用于大型现实世界代码库时没有严重的设计缺陷。然而 Gaby 认为你应该重构你的模块代码,虽然我不同意,但我可以看到他来自哪里。

现在开始一个新项目时,我应该注意什么才能最终在我的编译器中发布模块功能?

就目前微软的编译器预期实现模块而言,您应该确保您的库可用于所有这些形式:

  1. 动态库
  2. 静态库
  3. 仅标题库

令很多人感到惊讶的是,目前预期实现的 C++ 模块保留了这些区别,所以现在您获得了上述所有三个的 C++ 模块变体,其中第一个看起来最就像人们期望的 C++ 模块一样,最后一个看起来最像一个更有用的预编译头文件。您应该支持这些变体的原因是因为您可以重用大多数相同的预处理器机器,也可以通过很少的额外工作来支持 C++ 模块。

以后的 Visual Studio 将允许将模块定义文件(.ifc 文件)作为资源链接到 DLL 中。这最终将消除 MSVC 上对 .lib 和 .dll 区别的需要,您只需向编译器提供一个 DLL,它在模块导入时“正常工作”,不需要头文件或其他任何东西。这当然有点像 COM,但没有 COM 的大部分优点。

是否可以在单个代码库中使用模块,并且仍然保持与不支持它的旧编译器的兼容性?

我假设您的意思是上面插入的粗体文本。

答案通常是肯定的,而且预处理器宏更有趣。 #include <someheader> 可以在标头中变成import someheader,因为预处理器仍然照常工作。因此,您可以使用 C++ 模块支持标记单个库头文件,如下所示:

// someheader.hpp

#if MODULES_ENABLED
#  ifndef EXPORTING_MODULE
import someheader;  // Bring in the precompiled module from the database
// Do NOT set NEED_DEFINE so this include exits out doing nothing more
#  else
// We are at the generating the module stage, so mark up the namespace for export
#    define SOMEHEADER_DECL export
#    define NEED_DEFINE
#  endif
#else
// Modules are not turned on, so declare everything inline as per the old way
#  define SOMEHEADER_DECL
#  define NEED_DEFINE
#endif

#ifdef NEED_DEFINE
SOMEHEADER_DECL namespace someheader
{
  // usual classes and decls here
}
#endif

现在在您的 main.cpp 或其他文件中,您只需执行以下操作:

#include "someheader.hpp"

...如果编译器有 /experimental:modules /DMODULES_ENABLED 那么你的应用程序会自动使用你的库的 C++ 模块版本。如果没有,您将像我们一直做的那样获得内联包含。

我认为这些是对您的源代码进行的最小可能更改,以使您的代码现在可以准备好模块。你会注意到我对构建系统只字未提,这是因为我仍在调试我编写的 cmake 工具,以使所有这些东西都能无缝地“正常工作”,而且我预计还要调试几个月。期待在明年或后年的 C++ 会议上看到它:)

【讨论】:

    【解决方案2】:

    是否可以在使用模块的同时保持与不支持它的旧编译器的兼容性?

    不,这是不可能的。可以使用一些 #ifdef 这样的魔法:

    #ifdef CXX17_MODULES
        ...
    #else
        #pragma once, #include "..." etc.
    #endif
    

    但这意味着您仍然需要提供.h 支持,因此失去了所有好处,而且您的代码库现在看起来很丑。

    如果您确实想遵循这种方法,检测我刚刚编写的“CXX17_MODULES”的最简单方法是编译一个小型测试程序,该程序使用带有您选择的构建系统的模块,并为大家看看编译成功与否。

    现在开始一个新项目时,我应该注意什么才能最终在我的编译器中发布模块功能?

    这取决于。如果您的项目是企业项目,并且可以为您提供食物,那么我会等待几年,一旦它在马厩中发布,以便它得到广泛的适应。另一方面,如果您的项目能够负担得起最前沿的技术,请务必使用模块。

    基本上,这与 Python3 和 Python2 或 PHP7 和 PHP5 的情况相同。您需要在成为一名优秀的最新程序员和不惹恼 Debian 的人之间找到平衡;-)

    【讨论】:

    • 为什么投反对票?无论如何,C++ 模块不是另一个疯狂的超级酷特性,就像用户定义的文字,而是一个经过深思熟虑的解决实际问题的解决方案(在 C++ 代码中表达模块化)。如果我的代码设计得很好,它就已经是模块化的了。我只需要将它传达给工具,以便它可以利用它,
    • 我同意我们必须等待一段时间才能使用它。我说 2025 年它将成为标准使用。 Oracle、IBM 和英特尔开发缓慢的编译器将是主要问题。 Oracle 仍处于 C++11
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-06
    • 1970-01-01
    • 2016-12-02
    • 2023-03-15
    • 2010-09-09
    相关资源
    最近更新 更多