9

我正在将我的旧代码转换为可作为 c++ 模块导入的东西。问题是我希望它仍然可以工作并且很容易维护为旧的标头/源版本。我该怎么做(如果可能)。

是否可以创建一个导出标题内容的模块?(也接受任何其他允许您维护旧的 .cpp/.h 文件和模块文件的解决方案)

玩具示例:

// In vector.h
template <typename T>
struct Vector {
    T x, y;
}

// In .cppm
export module vector;
// #include "vector.h"
// Export struct/class Vector from header

我尝试过的只是export Vector不同的版本,有和没有模板等。

额外的问题:你能为标准库做到这一点吗?(例如 iostream 或字符串)

4

3 回答 3

2

最近,我创建了两个答案,第一个是关于如何从任何旧式标题中创建模块(在 CLang 中)。第二个关于如何从 STD 标头创建模块。

现在使用这两个答案,您可以像以前一样使用.hpp基于旧的样式进行编码。然后在使用代码中,您可以执行以下操作:

#if USE_MODULES
    import std_mod; // Module created out of all STD headers.
    import my_lib; // Module created out of my_lib.h.
#else
    #include <iostream>
    #include <vector>
    // ... All other tons of STD headers ...

    #include "my_lib.h"
#endif

int main() { use_my_lib(); }

使用头文件作为模块或作为包含是完全可以互换的,在这两种情况下你甚至会有宏,因为 CLang 的头文件模块也导出宏。

此外,您不需要任何特殊语法来创建my_lib库。您根本不需要使用exportormodule关键字,您只需像在没有模块的旧式 C++ 中一样编写代码,然后您的标头就可以轻松转换为模块。

我在我相当庞大的 C++ 项目中做了这样的事情。它完全没有模块编码,然后在我的构建系统中自动将所有头文件转换为模块,这大大提高了编译速度。当然,我实现了自己的构建系统,它知道包含和模块,并在飞行时自动进行转换而不更改原始文件。

于 2021-04-29T19:20:14.697 回答
1

a)您希望(大部分)保持原样,但将其包装在模块中。

这将导致类似于 PCH 的东西,但在语言层面上是标准化的。在不需要更改代码的假设下,这具有以下优点:

  • 用户可以将您的库用作头文件和模块(如果他们自己更新到 C++20)
  • 如果库作为模块使用,您可能会看到编译时间的好处(尽管取决于项目)

实现这一点的最简单方法是将所有公共标头放在一个模块中:

// your_lib.cppm
module;
// global module fragment here
#include "your_header1.hpp"
#include "your_header2.hpp"
// ...

export module your_lib;

您应该阅读有关可导入标头的信息(注释中已显示的语法,例如import <iostream>)。据我所知,什么是可导入标头是实现定义的。所以一般来说,您不必使用全局模块片段。

b) 你想模块化你的代码库,但允许其他人仍然使用头文件。

想要这样的东西的唯一原因(这是一个很好的理由)是支持仍然使用旧标准 C++20 的用户使用您的库。

您希望的解决方案实际上是不可能的。对不起。没有办法从模块返回到标题。图书馆内部的一切都可以完全模块化。然后你必须做出决定。将公共接口保留为标题,或将其模块化。在第一种情况下,请参阅 a) 关于如何在内部模块中使用自己的公共标头。在第二种情况下,您必须提供接口的第二种实现作为头文件。正如我所说,您只选择 b) 来支持较旧的标准版本。因此,您还必须确保不在(基于标头的)公共接口中使用任何 C++20 功能。

于 2021-03-28T20:03:00.087 回答
0

我将从我自己的一个库中举一些例子,我已经转换为支持旧样式的标题和模块。

这个例子是一个只有头文件的库,我将添加一个只有当我有一个时才不是头文件的库的例子。

仅头文件/模块库

使导出成为可选。在我的情况下,我定义了一个带有宏的文件,如果项目与模块一起使用,则添加导出。

导出.h

#pragma once

// Macros for handling compatability with/without modules

#ifdef matmath_use_modules

#ifndef matmath_export
#define matmath_export export
#endif

#else

#ifndef matmath_export
#define matmath_export
#endif

#endif

然后几乎像往常一样定义库的标头,但稍作调整,使用导出宏而不是您想要的符号的导出关键字,并且所有包含都被删除。

vec.h

// vec.h
#pragma once

#include "export.h"

#ifndef matmath_use_modules

#include <cmath>
#include <ostream>
#if __cplusplus >= 201103L
#include <tuple>
#endif

#include "pi.h"

#endif

matmath_export template <typename T>
class VecT {
public:
    T x = 0, y = 0, z = 0;

    constexpr VecT() = default;

   ...
};

然后,您可以在选择的模块中使用您的标题并激活正确的宏。

向量.cppm

module;

#include <cmath>
#include <ostream>

import matmath.pi;

export module matmath.vec;

#define matmath_use_modules
#include "matmath/vec.h"

// If your file has a regular cpp-file you could include that
// here in the same fashion
// #include "path/to/vec.cpp"

最后:使用类时,您可以选择通过模块文件或头文件来使用项目(尽管将头文件和模块组合为 lib 会产生很多痛苦)。

使一个小项目可以使用和不使用模块来构建

如果你只是想构建一个有模块和没有模块的小项目,你可以删除所有导出相关的语句并将导入语句转换为包含。这种方法要求您对代码进行更改,或者至少有一个单独的步骤来创建没有模块相关代码的代码副本。https://github.com/mls-m5/rym/blob/master/non-module-build.sh

于 2021-03-29T07:20:26.380 回答