Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← C++ 编程 / C++ Programming

插件系统 / Plugin Systems

1. 插件工程:可演进的模块边界 / Plugin Engineering and Evolvable Module Boundaries

2. 运行时动态加载:LoadLibrary 与 dlopen —— 打开插件的大门 / Runtime Dynamic Loading with LoadLibrary and Dlopen

3. 插件接口设计:C ABI、函数表与受控 C++ 接口 / Plugin Interface Design with C ABI, Function Tables, and Controlled C++ Interfaces

4. CMake 构建完整插件项目 / Building a Complete Plugin Project with CMake

5. 插件架构设计模式:发现、生命周期、依赖与通信 / Plugin Architecture Patterns for Discovery, Lifecycle, Dependencies, and Communication

6. 跨平台插件开发:Windows / Linux / macOS 统一策略

7. 插件热加载与版本管理 / Plugin Hot Reloading and Version Management

8. 插件安全:沙箱、进程隔离与权限控制 / Plugin Security with Sandboxing, Process Isolation, and Permissions

9. 工业软件插件系统案例分析 / Industrial Software Plugin System Case Study

10. 综合实战:版本化 C ABI 插件系统 / Full Project: Versioned C ABI Plugin System

本页目录

CMake 构建完整插件项目 / Building a Complete Plugin Project with CMake ​

📅 创建时间:2026-07-13 🏷️ 标签:#CMake #插件构建 #项目结构 #RPATH 📚 前置知识:project structure thirdparty cmake, plugin interface design


📋 本章目标 ​

  • 掌握插件项目的标准 CMake 目录结构设计
  • 学会用 INTERFACE 库管理插件公共接口
  • 掌握插件的 CMake 配置:SHARED 库 / 输出目录 / RPATH 设置
  • 实现插件自动发现:构建后插件 .so 统一输出到 plugins/ 目录
  • 理解 install 规则:安装后宿主、接口头文件、插件的目录布局

第1部分:插件项目的标准目录结构 ​

plugin_system/
├── CMakeLists.txt                    # 顶层 CMake
├── host/                             # 宿主程序
│   ├── CMakeLists.txt
│   └── main.cpp
├── plugin_interface/                 # 公共接口(最关键!)
│   ├── CMakeLists.txt               # INTERFACE 库
│   ├── iplugin.h                    # 所有插件的基础接口
│   ├── icalculator.h                # 计算器插件专用接口
│   └── plugin_loader.h              # 跨平台加载工具(第06篇的封装)
├── plugins/                          # 编译好的插件 .so/.dll 放这里
│   ├── CMakeLists.txt               # 只是组织子目录
│   ├── plugin_math/
│   │   ├── CMakeLists.txt
│   │   └── math_plugin.cpp
│   └── plugin_text/
│       ├── CMakeLists.txt
│       └── text_plugin.cpp
└── cmake/                            # CMake 工具模块
    └── PluginUtils.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────────────────────────┐
│              各目录的编译角色                                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  plugin_interface/  →  INTERFACE 库                                        │
│    不生成二进制,只提供头文件和编译选项                                       │
│    宿主和插件都依赖它                                                        │
│                                                                             │
│  host/              →  EXECUTABLE                                          │
│    链接到 plugin_interface(用头文件)                                       │
│    不链接到具体插件(插件是运行时加载的)                                     │
│                                                                             │
│  plugins/*/         →  SHARED 库                                           │
│    每个插件编译为 .so/.dll                                                   │
│    依赖 plugin_interface(用头文件)                                         │
│    输出到统一的 plugins/ 目录                                                │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第2部分:CMake 实现详解 ​

2.1 顶层 CMakeLists.txt ​

cmake
cmake_minimum_required(VERSION 3.20)
project(PluginSystem VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)       # Linux 默认隐藏符号
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)

# 公共接口
add_subdirectory(plugin_interface)

# 宿主程序
add_subdirectory(host)

# 所有插件
add_subdirectory(plugins)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

2.2 plugin_interface/ —— INTERFACE 库 ​

cmake
# plugin_interface/CMakeLists.txt
add_library(plugin_interface INTERFACE)

target_include_directories(plugin_interface
    INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
        $<INSTALL_INTERFACE:include/plugin_system>
)

# 插件接口需要的公共编译选项
target_compile_definitions(plugin_interface
    INTERFACE
        PLUGIN_API_VERSION=1
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

INTERFACE 库不编译任何 .cpp 文件——它只传递头文件路径和编译选项给任何依赖它的 target。

2.3 plugins/plugin_math/ —— 一个具体插件 ​

cmake
# plugins/plugin_math/CMakeLists.txt
add_library(plugin_math SHARED
    math_plugin.cpp
)

# 依赖公共接口(获取头文件 + 编译选项)
target_link_libraries(plugin_math PRIVATE plugin_interface)

# 设置输出目录 —— 所有插件统一到 plugins/ 目录
set_target_properties(plugin_math PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins"
)

# Linux: 设置 soname
set_target_properties(plugin_math PROPERTIES
    VERSION     1.0.0
    SOVERSION   1
)

# Windows: 编译 DLL 时定义导出宏(如果需要对外导出更多符号)
target_compile_definitions(plugin_math PRIVATE MATH_PLUGIN_EXPORTS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

2.4 host/ —— 宿主程序 ​

cmake
# host/CMakeLists.txt
add_executable(plugin_host
    main.cpp
)

# 只需要公共接口头文件,不需要链接任何具体插件!
target_link_libraries(plugin_host PRIVATE plugin_interface)

# Linux: 需要 -ldl
if(UNIX)
    target_link_libraries(plugin_host PRIVATE ${CMAKE_DL_LIBS})
endif()

# RPATH: 让可执行文件能从 ../plugins 找到 .so
set_target_properties(plugin_host PROPERTIES
    BUILD_RPATH   "${CMAKE_BINARY_DIR}/plugins"
    INSTALL_RPATH "$ORIGIN/../lib/plugin_system/plugins"
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第3部分:插件自动收集 —— add_custom_command ​

构建完成后,所有插件 .so/.dll 已经在 build/plugins/ 下了。如果想要构建后自动生成一个插件列表文件或拷贝到统一位置:

cmake
# 在顶层 CMakeLists.txt 末尾
add_custom_target(collect_plugins ALL
    COMMAND ${CMAKE_COMMAND} -E echo "Plugins built:"
    COMMAND ${CMAKE_COMMAND} -E echo "  ${CMAKE_BINARY_DIR}/plugins/"
    COMMENT "All plugins collected in plugins/ directory"
)

# 如果有外部预编译的插件需要拷贝进来:
# add_custom_command(TARGET collect_plugins POST_BUILD
#     COMMAND ${CMAKE_COMMAND} -E copy_if_different
#         ${CMAKE_SOURCE_DIR}/external/prebuilt_plugin.so
#         ${CMAKE_BINARY_DIR}/plugins/
# )
1
2
3
4
5
6
7
8
9
10
11
12
13

3.1 Debug vs Release 插件子目录 ​

cmake
# 更专业的做法:Debug 和 Release 插件分开放
set(PLUGIN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/plugins/$<CONFIG>")

set_target_properties(plugin_math PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIR}"
    RUNTIME_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIR}"
)
# 结果:build/plugins/Debug/plugin_math.so
#       build/plugins/Release/plugin_math.so
1
2
3
4
5
6
7
8
9

第4部分:Install 规则 —— 给别人用的时候 ​

cmake
# 顶层 CMakeLists.txt 末尾的 install 规则

# 1. 宿主程序
install(TARGETS plugin_host
    RUNTIME DESTINATION bin
)

# 2. 公共接口头文件
install(TARGETS plugin_interface
    INCLUDES DESTINATION include
)
install(DIRECTORY plugin_interface/
    DESTINATION include/plugin_system
    FILES_MATCHING PATTERN "*.h"
)

# 3. 插件
install(TARGETS plugin_math plugin_text
    LIBRARY DESTINATION lib/plugin_system/plugins
    RUNTIME DESTINATION bin/plugin_system/plugins
)

# 安装后的目录结构:
# /usr/local/
# ├── bin/
# │   └── plugin_host
# ├── include/
# │   └── plugin_system/
# │       └── *.h
# └── lib/
#     └── plugin_system/
#         └── plugins/
#             ├── libplugin_math.so
#             └── libplugin_text.so
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

第5部分:处理第三方插件的构建 ​

当甲方给你一个预编译好的 .so/.dll(不是你的 CMake 子项目),如何融入构建流程?

cmake
# 方法1:创建 IMPORTED target
add_library(external_plugin SHARED IMPORTED)
set_target_properties(external_plugin PROPERTIES
    IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/external/prebuilt_plugin.so"
)

# 方法2:复制到构建输出目录
add_custom_command(TARGET collect_plugins POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
        "${CMAKE_SOURCE_DIR}/external/prebuilt_plugin.so"
        "${CMAKE_BINARY_DIR}/plugins/"
)

# 方法3:如果是 Windows 并需要 .lib + .dll
add_library(ext_win64_plugin SHARED IMPORTED)
set_target_properties(ext_win64_plugin PROPERTIES
    IMPORTED_LOCATION   "${CMAKE_SOURCE_DIR}/external/win64/plugin.dll"
    IMPORTED_IMPLIB     "${CMAKE_SOURCE_DIR}/external/win64/plugin.lib"
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第6部分:一个完整的构建和运行流程 ​

bash
# 1. 构建
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)

# 2. 查看产物
ls build/plugins/             # 所有 .so 插件
ls build/host/plugin_host     # 宿主程序

# 3. 运行(build RPATH 已经设好,直接运行)
./build/host/plugin_host

# 4. 安装
cmake --install build --prefix /usr/local

# 5. 运行安装后的版本
/usr/local/bin/plugin_host    # INSTALL_RPATH = $ORIGIN/../lib/plugin_system/plugins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

核心总结 ​

┌─────────────────────────────────────────────────────────────────────────────┐
│              CMake 插件项目核心模式                                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  INTERFACE 库 = 插件接口的载体                                               │
│    • 不编译代码,只传递头文件和编译选项                                       │
│    • 宿主和插件都 PRIVATE 依赖它                                              │
│                                                                             │
│  插件 target 配置:                                                          │
│    • add_library(xxx SHARED ...)                                            │
│    • LIBRARY_OUTPUT_DIRECTORY → 统一的 plugins/ 目录                         │
│    • VERSION + SOVERSION → soname 正确设置                                   │
│                                                                             │
│  RPATH 配置:                                                                │
│    • BUILD_RPATH → 构建后能直接在 build 目录运行                             │
│    • INSTALL_RPATH → 安装后能找到插件                                        │
│                                                                             │
│  宿主不链接具体插件 → 依赖关系通过运行时 dlopen 管理                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

章节测试 ​

测试1:INTERFACE 库 ​

为什么插件公共接口要用 INTERFACE 库而不是 STATIC 库?INTERFACE 库传递了什么?

测试2:插件输出 ​

如何让所有插件编译后自动放在同一个 plugins/ 目录下?用到了 CMake 的什么属性?

测试3:RPATH ​

BUILD_RPATH 和 INSTALL_RPATH 分别解决什么问题?为什么需要分别设置?

测试4:宿主依赖 ​

宿主程序为什么不需要 target_link_libraries 到任何具体插件?

测试5:构建流程 ​

从零开始构建这个插件项目,写出完整的命令序列(configure → build → run)。


参考答案 ​

测试1答案 ​

答案:INTERFACE 库不生成任何二进制文件,只传递:(1) include 路径(target_include_directories);(2) 编译选项(target_compile_definitions / target_compile_options);(3) 链接依赖。使用 INTERFACE 库的理由是插件接口是纯头文件的——只需要 #include,不需要链接任何 .lib/.a。如果用 STATIC 库,编译链接时会试图找 .cpp 文件来生成二进制。

测试2答案 ​

答案:通过 set_target_properties 设置 LIBRARY_OUTPUT_DIRECTORY(Linux .so)、RUNTIME_OUTPUT_DIRECTORY(Windows .dll)、ARCHIVE_OUTPUT_DIRECTORY(Windows .lib 静态/导入库)。将所有插件的这些属性设为同一个 ${CMAKE_BINARY_DIR}/plugins 目录即可。

测试3答案 ​

答案:

  • BUILD_RPATH:在 build 目录中运行时用的搜索路径。构建后 ./build/host/plugin_host 能直接从 ./build/plugins/ 找到 .so。
  • INSTALL_RPATH:安装后运行时用的搜索路径。cmake --install 后程序被拷贝到系统路径(如 /usr/local/bin),需要用 $ORIGIN/../lib/... 指明 .so 的相对位置。
  • 分别设置是因为 build 和 install 的目录结构不同。

测试4答案 ​

答案:因为运行时动态加载(dlopen/LoadLibrary)完全绕过了编译时的链接检查。宿主通过 dlopen 打开 .so,通过 dlsym 获取函数指针,通过函数指针调用。这些操作对链接器完全不可见——链接器不知道宿主"将要"加载哪些 .so。宿主只需要链接提供 dlopen 的 libdl(Unix)和插件接口头文件。

测试5答案 ​

答案:

bash
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)
./build/host/plugin_host
1
2
3

相关笔记 ​

  • project structure thirdparty cmake - CMake 基础与第三方库
  • plugin interface design - 插件接口设计
  • plugin architecture patterns - 插件架构设计模式

下一步学习 ​

  • [ ] 阅读 09 - 插件架构设计模式

学习状态:🟡 开始学习

工程深化:把本篇知识落到项目里 ​

这一节不是为了凑篇幅,而是把《CMake 构建完整插件项目 / Building a Complete Plugin Project with CMake》从“知道概念”推进到“能在项目里稳定使用”。 阅读时可以把每个知识点都追问成四件事:它保护什么边界,失败时有什么现象,怎样最小复现,怎样写进团队流程。

text
concept -> boundary -> failure signal -> minimal proof -> project rule
1

工程切片 1:最小可复现样例 ​

**场景。**围绕 插件发现 建一个很小的案例,不要一开始就放进完整业务系统。把问题压缩到一个可以提交给同事的目录,保留源码、构建命令、版本输出和预期现象。

**边界。**先判断这里讨论的是 插件发现、加载器 还是 契约 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 插件发现 -> 加载器 -> 契约 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 2:接口边界 ​

**场景。**围绕 加载器 建一个很小的案例,不要一开始就放进完整业务系统。写清调用方能依赖什么、不能依赖什么,并把隐式假设转换成命名函数、配置项或测试。

**边界。**先判断这里讨论的是 加载器、契约 还是 生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 加载器 -> 契约 -> 生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 3:失败注入 ​

**场景。**围绕 契约 建一个很小的案例,不要一开始就放进完整业务系统。故意制造一个常见错误,让日志、断言或测试先失败,再用修复后的证据说明规则生效。

**边界。**先判断这里讨论的是 契约、生命周期 还是 隔离 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 契约 -> 生命周期 -> 隔离 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 4:跨平台差异 ​

**场景。**围绕 生命周期 建一个很小的案例,不要一开始就放进完整业务系统。至少比较 Windows、Linux 或不同编译器下的一个差异,记录差异属于标准、实现还是平台约定。

**边界。**先判断这里讨论的是 生命周期、隔离 还是 诊断 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 生命周期 -> 隔离 -> 诊断 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 5:性能观察 ​

**场景。**围绕 隔离 建一个很小的案例,不要一开始就放进完整业务系统。用小规模和压力规模各跑一次,区分算法成本、同步成本、I/O 成本和工具链配置成本。

**边界。**先判断这里讨论的是 隔离、诊断 还是 插件发现 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 隔离 -> 诊断 -> 插件发现 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 6:生命周期 ​

**场景。**围绕 诊断 建一个很小的案例,不要一开始就放进完整业务系统。标出资源创建、转移、共享、停止和销毁的顺序,特别关注错误返回和提前退出路径。

**边界。**先判断这里讨论的是 诊断、插件发现 还是 加载器 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 诊断 -> 插件发现 -> 加载器 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 7:诊断证据 ​

**场景。**围绕 插件发现 建一个很小的案例,不要一开始就放进完整业务系统。保留命令、日志、符号、栈、测试输出或截图,让结论可以被另一个环境重新验证。

**边界。**先判断这里讨论的是 插件发现、加载器 还是 契约 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 插件发现 -> 加载器 -> 契约 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 8:维护策略 ​

**场景。**围绕 加载器 建一个很小的案例,不要一开始就放进完整业务系统。把一次性经验沉淀到 README、CI、脚本、示例工程或检查表里,避免只存在个人记忆中。

**边界。**先判断这里讨论的是 加载器、契约 还是 生命周期 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 加载器 -> 契约 -> 生命周期 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 9:版本演进 ​

**场景。**围绕 契约 建一个很小的案例,不要一开始就放进完整业务系统。为未来变更预留兼容策略:版本号、特性位、弃用窗口、迁移脚本和回滚入口。

**边界。**先判断这里讨论的是 契约、生命周期 还是 隔离 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 契约 -> 生命周期 -> 隔离 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

工程切片 10:最小消费者 ​

**场景。**围绕 生命周期 建一个很小的案例,不要一开始就放进完整业务系统。从使用者角度写一个小程序或小工程,只依赖公开接口,验证安装包、头文件和运行时行为。

**边界。**先判断这里讨论的是 生命周期、隔离 还是 诊断 的责任;如果三者混在一起,先把输入、输出和所有权拆开。

text
input -> 生命周期 -> 隔离 -> 诊断 -> observable result
1

**验证。**记录一条能重复运行的命令或测试,并写明成功输出和失败输出。只说“本机能跑”不够,至少要记录工具版本、构建类型和关键参数。

**常见错误。**把偶然通过当成规则、把私有实现当成公开契约、或者让错误路径绕开清理逻辑,都会让本篇主题在真实项目里变得不可维护。

**复盘问题。**如果明天换一个编译器、Qt 版本、插件版本、输入规模或发布目录,这个结论是否仍成立?不成立时,应该由文档、测试还是 CI 给出提示?

本篇收束 ​

掌握《CMake 构建完整插件项目 / Building a Complete Plugin Project with CMake》的标志,不是记住所有名词,而是能把 插件发现、加载器、契约、生命周期、隔离、诊断 放进一条可验证的工程链路。 先用最小样例建立判断,再用测试和脚本固定判断,最后把失败证据留给未来的自己和团队。

最后更新于:

Pager
上一篇3. 插件接口设计:C ABI、函数表与受控 C++ 接口 / Plugin Interface Design with C ABI, Function Tables, and Controlled C++ Interfaces
下一篇5. 插件架构设计模式:发现、生命周期、依赖与通信 / Plugin Architecture Patterns for Discovery, Lifecycle, Dependencies, and Communication

持续记录,持续成长

Copyright © Tidenflow