写C++的时候,我们要调用其他文件中的函数或者类这些,一般情况下,就是include一个头文件,或者extern。但在C++20中,有了一个更好的调用方式:使用模块化方式进行调用。
如果使用过javascrtip的话,对这种语法看上去就比较熟悉,在javascript中
//a.js
export let val= 'va';
//.b.js
import { val} from 'a.js';
然后就可以在b.js中使用val了
C++使用模块,也差不多,可能比在JS中使用还要简便一点,举例如下
以下代码在cygwin gcc11.3 cmake3.23.2中测试
模块基本使用
//a.cpp
export module mymoduletesta;
export {
char* funca1() {
return "funca11";
}
char* funca2() {
return "funca22";
}
}
//main.cpp
import mymoduletesta;
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <locale.h>
int main()
{
printf(funca1());
printf(funca2());
}
在a.cpp中
export module mymoduletesta;
表示定义了一个导出模块,模块名为mymoduletesta
export {
char* funca1() {
return "funca11";
}
char* funca2() {
return "funca22";
}
}
表示导出本模块的两个函数funca1,funca2
在main.cpp
import mymoduletesta;
表示导入了名为mymoduletesta的模块,然后就可以像以前写include方式那样调用mymoduletesta模块中导出的函数
我使用了cmake进行编译,cmake的定义如下
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(cx)
AUX_SOURCE_DIRECTORY(. DIR_SOURCE)
SET(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmodules-ts")
ADD_EXECUTABLE(app ${DIR_SOURCE})
主要是这两句重要,没有的话编译会报错。-fmodules-ts表示要使用模块功能
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmodules-ts")
另外gcc的版本要在11以上才行
上面就是c++模块的基本使用方式
全局模块
在模块功能中,是不能使用#include语法的,例如上例中,如果改为
#include<stdio.h>
export module mymoduletesta;
export {
char* funca1() {
return "funca11";
}
char* funca2() {
return "funca22";
}
}
编译会产生错误
error: module-declaration only permitted as first declaration, or ending a global module fragment
但是,如果这个模块作为全局模块处理的话,就可以了
module;
#include<stdio.h>
export module mymoduletesta;
export {
char* funca1() {
return "funca11";
}
char* funca2() {
return "funca22";
}
}
第一行的
module;
表示为这是个全局模块,这种模块是可以使用include引入其他文件
模块的导出
事实上,exprot不局限于导出一般函数,类也可以导出
export module mymoduletesta;
export class abc
{
public:
char* m1() {
return "m1";
}
char* m2() {
return "m2";
}
};
命名空间也可以
export namespace abc
{
char* m1() {
return "m1";
}
char* m2() {
return "m2";
}
};
一般的变量也可以
export module mymoduletesta;
export int x = 123;
如果想在模块中导出模块,例如main.cpp中要调用a.cpp模块中的一个函数,但是mian.cpp并没有导入a.cpp模块,只导入了b.cpp,但b.cpp中导入了a.cpp模块,代码如下
//a.cpp
export module mymoduletesta;
export {
char* funca1() {
return "funca11";
}
char* funca2() {
return "funca22";
}
}
//b.cpp
export module mymoduletestb;
import mymoduletesta;
export char* funcb() {
return funca1();
}
//main.cpp
import mymoduletestb;
#include <stdio.h>
int main()
{
printf(funcb());
printf(funca1());
}
这时候在main.cpp中要调用a.cpp中funca1(),那就不能只在b.cpp模块中仅
使用import导入
import mymoduletesta
这是不够的,要把上一句改成
export import mymoduletesta;
这样,在b.cpp中也可以调用a.cpp中的函数,在main.cpp虽然没有导入a.cpp的模块,但是在b.cpp中导出了a.cpp模块,所以,在main.cpp中就也可以使用了
模块分区
一个模块可以有模块分区单元。分区的意思就是把一个模块分成几个独立的模块,但是使用的时候却和使用一个完整的模块一样
分区语法如下
module testmodule:bm;
module testmodule:am;
导入语法如下
import :bm
import :am
以上语法就是把testmodule模块,分为bm,am两个模块分区,但是模块分区只能被相同具名模块的模块单元导入
例如上头两个模块,只能被testmodule模块导入
举例如下
//a-a.cpp
export module mymoduletesta:am;
export {
char* funca1() {
return "funca1";
}
}
//a-b.cpp
module mymoduletesta:bm;
char* funca2() {
return "funca2";
}
//a-c.cpp
export module mymoduletesta;
import :bm;
export import :am;
export char* test()
{
return funca2();
}
//main.cpp
import mymoduletesta;
#include <stdio.h>
int main()
{
printf(funca1());
printf(test());
}
在上面实例中,mymoduletesta有两个分区,am,bm,在a-c.cpp中,
import :bm;
export import :am;
导入了bm,am分区,但是因为bm分区中,没有导出模块,所以,在a-c.cpp中,可以导入bm,也可以使用funca2函数,但是在main.cpp中不能使用funca2,但是am中的funca1,和mymoduletesta中的test因为导出,在main.cpp中可以使用
以上就是C++20中模块的基本使用方式了,还有一点很重要,编译顺序。如果在b.cpp中要使用a.cpp模块,就必须要先让a.cpp先编译