C++ 20 Module

news2024/11/15 17:40:59

头文件包含一直是C/C++的传统,它使代码声明与实现分离,但它有一个非常大的问题就是会被重复编译,拖累编译速度。

通常一个标准头文件iostream展开后可能达几十万甚至上百万行。笔者使用下面的示例进行测试,新建一个main.cc,内容如下:

#include <iostream>

int main(int argc, char* argv[])
{
    return 0;
}

然后分别使用g++和clang++来测试行数:

g++ -E main.cc | wc -c 
1003912
clang++ -E main.cc | wc -c 
999191

随着C++ 20 Module的出现以及各编译器对其的逐步实现,C++也能进行模块化编程,提高编译速度了。

由于历史原因,现有以头文件形式组织的C++代码,不会在短时间内消失,这种情况将持续相当长的时间,也许是几年,十几年,甚至几十年或者更长,所以目前的C++依旧可以在Module中包含头文件,让之与模块(Module)共存,方便使用现有代码。

目前主流的C++编译器有GCC、Clang和MSVC,各个编译器实现的进展不一,使用的命令行参数也不一样,为了简单起见,笔者先以GCC编译器的命令行和Makefile为例来介绍模块的基本写法及编译,再介绍Clang和MVVC使用CMake来编译项目。

一、模块基础

1. 定义模块

libA.cpp:

export module libA;

export int plus(int x, int y)
{
    return x + y;
}

2. 使用模块

main.cpp

import libA;

int main(int argc, char *argv[])
{
	plus(1, 2);
	return 0;
}

3. 编译:

g++ -std=c++20 -fmodules-ts -c libA.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 libA.o main.o -o main

需要先编译libA,再编译main

二、声明与定义分离

1. 模块分区

前面的libA模块代码全部在一个文件中,当代码比较多的时候,清晰度就会下降,可以使用声明与定义分离的形式:

api.cpp:

export module libA;
export
{
int plus(int x, int y);
}

plus.cpp

module libA;

int plus(int x, int y)
{
    return x + y;
}

编译:

g++ -std=c++20 -fmodules-ts -c api.cpp
g++ -std=c++20 -fmodules-ts -c plus.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 api.o plus.o main.o -o main

注意编译顺序,一定是要先编译模块接口(声明)文件api.cc,再编译模块定义(实现)文件plus.cc,否则会报错:

libA: error: failed to read compiled module: No such file or directory
libA: note: compiled module file is 'gcm.cache/libA.gcm'
libA: note: imports must be built before being imported
libA: fatal error: returning to the gate for a mechanical issue
compilation terminated.

在这里插入图片描述

三、Module Partition(模块分区)
当一个模块功能比较多时,C++支持将模块进行分区,每个文件只是模块中的一部分,这样可以将模块的接口与实现进一步分离。

libA/minus.cpp

export module libA:minus;

export int minus(int x, int y)
{
    return x - y;
}

libA/plus.cpp

export module libA:plus;

export int plus(int x, int y)
{
    return x + y;
}

libA/test.cpp

export module libA:test;
import <iostream>;
#include <string.h>

export class CTest
{
  public:
	CTest()
	{
		printf("CTest()\n");
	}
	void foo()
	{
		printf("foo\n");
	}
};

libA/z.cpp

export module libA;

export import :plus;
export import :minus;
export import :test;

接口文件中引用分区模块时可以省略主模块名,也可以指定主模块名:
export import :plus;export import libA:plus;都可以。

编译:

g++ -std=c++20 -fmodules-ts -xc++-system-header iostream
g++ -std=c++20 -fmodules-ts -c libA/minus.cpp
g++ -std=c++20 -fmodules-ts -c libA/plus.cpp
g++ -std=c++20 -fmodules-ts -c libA/test.cpp
g++ -std=c++20 -fmodules-ts -c libA/z.cpp
g++ -std=c++20 main.cpp -c main
g++ -std=c++20 libA/minus.o libA/plus.o libA/test.o libA/z.o main.o -o main

这里同样需要注意编译顺序,先编译系统级头文件,再编译自定义模块。

GCC可以使用import <iostream>;来引用标准库头文件,但是需要先手动编译,第一句即是,引用得的标准库头文件越多,自己手动编译得也越多。注意:有些标准库头文件目前还不能使用import来引用,将头文件编译为模块时会报错。为了最大程度上与其它编译器兼容,目前不建议使用import来引用标准库头文件

自定义模块必须先编译各分区的实现(minus.cpp、plus.cpp、test.cpp),最后编译模块的接口(z.cpp)。

当文件比较多的时候,使用命令行直接一个个文件编译比较慢,也容易出错,所以可以改用Makefile来编译,编写Makefile如下:

CXX := g++
CXXFLAGS := -std=c++20 -fmodules-ts -gdwarf-4
Target := main

SOURCE := $(wildcard libA/*.cpp)
SOURCE += $(wildcard *.cpp)

OBJS := $(addsuffix .o, $(SOURCE))

all: $(Target)

$(Target): std $(OBJS)
	$(CXX) $(CXXFLAGS) $(OBJS) -o $(Target)

std:
	$(CXX) $(CXXFLAGS) -xc++-system-header iostream
	$(CXX) $(CXXFLAGS) -xc++-system-header cmath
	
%.cpp.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

clean:
	rm $(OBJS) gcm.cache $(Target) -rf

此时只需要执行make即可编译项目,make clean清除生成的文件。为了让Makefile遵循前面的编译顺序,笔者在文件命名时即按字母顺序进行了相应的排序,所以$(wildcard libA/*.cpp)得到的文件顺序是符合要求的。

为了让lldb也可以调试生成的程序,添加了-gdwarf-4选项。

当源文件有修改,编译时可能会出现CRC不匹配的错误,如下:
在这里插入图片描述
需要执行make cleanmake即可。

2. 子模块

子模块与分区模块非常像,只是子模块使用冒号:分隔,而子模块使用点号.分隔。

libA/test.cpp

export module libA.test;
import <iostream>;
#include <string.h>

export class CTest
{
  public:
	CTest()
	{
		printf("CTest()\n");
	}
	void foo()
	{
		printf("foo\n");
	}
};

libA/z.cpp

export module libA;

export import libA:plus;
export import :minus;
export import libA.test;

三、Clang与MSVC使用CMake来编译项目

在顶层目录创建CMakeLists.txt

cmake_minimum_required(VERSION 3.28)
project(main)

set(CMAKE_CXX_STANDARD 20)
add_subdirectory(libA)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libA)

在libA目录创建CMakeLists.txt

cmake_minimum_required(VERSION 3.28)
project(libA)

set(CMAKE_CXX_STANDARD 20)

aux_source_directory(. SOURCE)
#这里需要去掉路径中的./
string(REPLACE "./" "" SOURCE "${SOURCE}")

add_library(${PROJECT_NAME})
target_sources(${PROJECT_NAME}
  PUBLIC
    FILE_SET cxx_modules TYPE CXX_MODULES FILES
    ${SOURCE}
)

注意:cmake_minimum_required(VERSION 3.28)一定要是3.28及以上,且需要去掉模块路径中的./

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1403818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动文章生成软件:自用工具推荐与使用技巧

在信息爆炸的时代&#xff0c;内容创作已成为企业和个人提升品牌影响力、增加流量和提升知名度的重要手段。然而&#xff0c;手动撰写大量文章既费时又费力。有没有一种方法可以快速、高效地生成文章呢&#xff1f;答案是肯定的&#xff0c;今天我们就来分享一款自用的AI批量生…

(十一)Head first design patterns状态模式(c++)

状态模式 如何去描述状态机&#xff1f; 假设你需要实例化一台电梯&#xff0c;并模仿出电梯的四个状态&#xff1a;开启、关闭、运行、停止。也许你会这么写 class ILift{ public:virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){} }…

windows用mingw(g++)编译opencv,opencv_contrib,并install安装

windows下用mingw编译opencv貌似不支持cuda&#xff0c;选cuda会报错&#xff0c;我无法解决&#xff0c;所以没选cuda&#xff0c;下面两种编译方式支持。 如要用msvc编译opencv&#xff0c;参考我另外一篇文章 https://blog.csdn.net/weixin_44733606/article/details/1357…

gRPC-gateway使用介绍

gRPC-gateway 参考资料&#xff1a;gRPC-Gateway使用指南 服务中&#xff0c;使用了gRPC gateway&#xff08;代理&#xff09;来将外部的http请求映射为内部rpc调用。 proto文件示例&#xff1a; // 导入google/api/annotations.proto import "google/api/annotations…

《WebKit 技术内幕》学习之九(4): JavaScript引擎

4 实践——高效的JavaScript代码 4.1 编程方式 关于如何使用JavaScript语言来编写高效的代码&#xff0c;有很多铺天盖地的经验分享&#xff0c;以及很多特别好的建议&#xff0c;读者可以搜索相关的词条&#xff0c;就能获得一些你可能需要的结果。同时&#xff0c;本节希望…

在全志H616核桃派上实现USB摄像头的OpenCV颜色检测

在给核桃派开发板用OpenCV读取图像并显示到pyqt5的窗口上并加入颜色检测功能&#xff0c;尝试将图像中所有蓝色的东西都用一个框标记出来。 颜色检测核心api 按照惯例&#xff0c;先要介绍一下opencv中常用的hsv像素格式。颜色还是那个颜色&#xff0c;只是描述颜色用的参数变…

图神经网络X项目|基于图神经网络的电商行为的预测(5%)

文章目录 Jupyter Notebook 学习人工智能的好帮手数据集数据集下载数据集调用数据集应用技巧——获取不重复的编号数据集应用技巧——随机采样数据集应用技巧——抽取前N项进行模拟测试 数据集构建技巧一——查看数据集构建进度 Jupyter Notebook 学习人工智能的好帮手 【Jupy…

opencv010 卷积02(方盒滤波和均值滤波)

今天继续学习滤波器的相关知识&#xff01;这篇比较简单&#xff0c;也短一些&#xff0c;明天写高斯滤波 方盒滤波 boxFilter(scr, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) 方盒滤波的卷积核如下&#xff1a; normalize&#xff08;标准化&#xff0…

从潮汐架构和安第斯大模型,看智能手机的未来演进

好久没聊手机了&#xff0c;今天聊聊手机。 最近这段时间&#xff0c;手机厂商纷纷发布了自家最新的旗舰系列。其中&#xff0c;有一些技术&#xff0c;蛮值得关注的。 大家都知道&#xff0c;手机行业是出了名的“内卷”&#xff0c;厂商之间的竞争非常激烈。但从本质来说&…

STL之unordered_map使用方法

这里写目录标题 STL之unordered_map使用方法1.什么是STL呢2.unordered_map2.1 头文件&#xff1a;2.2 怎么创建&#xff1a;2.3 初始化&#xff1a;2.4 根据key获取对应value值&#xff1a;2.5 遍历&#xff0c;判断key是否存在&#xff1a;2.6 怎么根据迭代器it获取key和value…

浅谈拨测在网络安全中的应用

在当今数字化时代&#xff0c;网络安全成为各个行业和组织关注的焦点。为了保障网络的稳定性和信息的安全&#xff0c;拨测安全性成为一种日益重要的工具。本文将介绍拨测在网络安全中的应用&#xff1a; 1.威胁模拟 通过威胁模拟&#xff0c;拨测安全性可以模拟各种网络攻击&a…

分布式websocket IM聊天系统相关问题问答【第九期】

前言 上期视频讲解了自己关于聊天系统的设计的时候出现了一些不一样的声音。不了解情况的可以看上上期视频。这期主要是讨论。IM聊天系统设计方案多。我的先说明一下自己的技术背景互相之间才能更好的理解。 本期对应视频 目前已经写的文章有。并且有对应视频版本。 git项目地…

小白初探架构模式—常用的设计模式

目录 1.前言 2. 主从架构 2.1 主从架构的优点 2.2 主从架构的应用场景 2.3 主从架构的实现 2.4 主从架构的示例 3. 主从架构设计的延伸 3.1 主备模式 3.2 主从复制 3.3 集群分片 3.4 异地多活 4. 总结 1.前言 作为一个架构设计小白&#xff0c;我们通常用了很多种工具&…

Java和Redis实现一个简单的热搜功能

1. 前言 我们有一个简单的需求&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录。用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该字符被搜索的个数以及当前的时间戳 &#xff08;用…

4_机械臂运动学基础向量空间

在了解机械臂正解推导的过程中&#xff0c;几个问题一直困扰着我&#xff1a; 1、为什么3*3矩阵可以描述姿态&#xff1f;矩阵更进一步的意义是什么&#xff1f;姿态是否有其他的描述方式&#xff0c;如果有是什么&#xff1f; 2、机械臂法兰中心相对于基座的坐标&#xff0c;6…

开始学习vue2基础篇(初体验)

一、什么是VUE&#xff08;官网 &#xff1a;https://cn.vuejs.org/&#xff09; 官方给出的概念 &#xff1a;Vue (读音 /vju ː/ &#xff0c;类似于 view) 是一套用 于构建用户界面的前端框架 渐进式的 JavaScript 框架 二、VUE的特点 易用 &#xff1a;基础只需HTML、CSS、…

[小程序]页面事件

一、下拉刷新 1.开启和配置 小程序中开启下拉刷新的方式有两种&#xff1a; ①全局开启下来刷新 在app.json的window节点中&#xff0c;设置enablePullDownRefresh设为ture。 ②局部开启下来刷新 在页面对应的json文件的的window节点中&#xff0c;设置enablePullDownRefresh设…

yolov5 opencv dnn部署 github代码

yolov5 opencv dnn部署 github代码 源码地址实现推理源码中作者的yolov5s.onnx推理条件python部署(因为python比较简单就直接介绍了)c部署 参考链接 源码地址 yolov5官网还提供的dnn、tensorrt推理链接本人使用的opencv c github代码,代码作者非本人&#xff0c;也是上面作者推…

定向减免!函数计算让轻量 ETL 数据加工更简单,更省钱

作者&#xff1a;澈尔、墨飏 业内较为常见的高频短时 ETL 数据加工场景&#xff0c;即频率高时延短&#xff0c;一般均可归类为调用密集型场景。此场景有着高并发、海量调用的特性&#xff0c;往往会产生高额的计算费用&#xff0c;而业内推荐方案一般为攒批处理&#xff0c;业…

【EI会议征稿通知】2024年第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)

2024年第四届人工智能、自动化与高性能计算国际会议&#xff08;AIAHPC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Automation and High Performance Computing 2024第四届人工智能、自动化与高性能计算国际会议(AIAHPC 2024)将于20…