将C++模块文件编译为动态库(或静态库)

前言

C++20提供了模块功能,将其编译为动态链接库(或静态库)的方式与传统的头文件方式略所不同,本文将带你简要了解。静态库的编译方式与此类似,将在最后提及。

示例代码:点击下载

库项目配置

首先下载实例代码,可以看到一个 lib 文件,里面就是动态库的实例代码。

lib/
├── CMakeLists.txt
├── modules/Hello.cppm # 模块接口文件
└── src/Hello.cpp # 模块实现文件

你可以自行查看源码,已经给出了充分的注释。这里给出几个重点说明:

  • 标准库模块可以不启用,自行删除相关代码。
  • 模块接口文件需要 public 暴露给下层,模块实现文件可以 private 隐藏。
  • 可以仅接口文件(将函数实现也写在接口),但编译速度较慢。
  • 模块接口文件必须使用 target_sources 命令配合 CXX_MODULES 类型加入目标。
  • 作为动态链接库,模块接口文件也需要编译器特定的导出符号(不是C++标准的export),但模块实现文件不需要。
  • Clang/GNU下默认全导出,导出符号可以省略。MSVC的导出符号 __declspec(dllexport) 是必须的,但无需导入符号。

因此可以得到这么一份 CMakeLists.txt,与上面提供的示例代码相同:

cmake_minimum_required(VERSION 4.0.3)

# 标准库模块实验性 UUID ,请自行设置
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")

# 添加动态库
set(lib_name "HelloLib")
add_library(${lib_name} SHARED)

# 获取源文件列表
file(GLOB_RECURSE CXX_MODULE_FILES "modules/*.cppm" "modules/*.ixx")
file(GLOB_RECURSE CXX_CPP_FILES "src/*.cpp")

# 添加源文件与模块接口文件
target_sources(${lib_name} PRIVATE ${CXX_CPP_FILES})
target_sources(${lib_name} PUBLIC
        FILE_SET public_modules # FILE_SET任意取名
        TYPE CXX_MODULES    # 模块接口文件 需要使用 CXX_MODULES 类型
        BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
        FILES ${CXX_MODULE_FILES}
)
# 设置 C++ 标准和标准库模块支持
set_target_properties(${lib_name} PROPERTIES
        CXX_STANDARD 23
        CXX_STANDARD_REQUIRED ON
        CXX_MODULE_STD 1
)

# 设置动态链接库的导出符号,MSVC与Clang/GNU的处理方式不同。
if(MSVC)
    target_compile_definitions(${lib_name} PRIVATE "LIB_EXPORT=__declspec(dllexport)")
else()
    target_compile_options(${lib_name} PRIVATE -fvisibility=hidden)
    target_compile_definitions(${lib_name} PRIVATE "LIB_EXPORT=__attribute__((visibility(\"default\")))")
endif()

使用模块的动态库

模块接口文件构成的动态库,使用起来和普通的动态库并无区别,依然使用 target_link_libraries 命令直接链接库即可,这边直接给出 CMakeLists.txt 代码,与上面的示例文件相同:

cmake_minimum_required(VERSION 4.0.3)

# 标准库模块实验性 UUID ,请自行设置
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
# 保证动态库文件和可执行文件输出到同目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>)

project(module_lib LANGUAGES CXX)

# 导入子目录
add_subdirectory(lib)
# 添加可执行文件目标
add_executable(main src/main.cpp)
# 链接动态库
target_link_libraries(main PRIVATE HelloLib)
# 设置 C++ 标准和标准库模块支持
set_target_properties(main PROPERTIES
        CXX_STANDARD 23
        CXX_STANDARD_REQUIRED ON
        CXX_MODULE_STD 1
)

然后在主项目的 main.cpp 文件中,可以直接使用 import 命令导入动态库中的模块:

import Hello;

int main() {
    Hello::say();
}

静态库编译配置

静态库编译与动态库编译基本类似,只是无需设置编译器特定的导出符号。

参考示例代码,只需修改 CMakeLists.txt ,将库类型设为 STATIC ,并删除最下方的导出符号宏定义即可。

模块接口文件开头的宏配置

#ifndef LIB_EXPORT
    #define LIB_EXPORT
#endif

可以保证 CMake 没有设置此宏时,将此宏定义为空,不影响模块接口文件内容。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇