You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为接口实现分离、支持多实现选择的项目编写CMake配置

CMake实现接口-实现分离的静态库项目规范方案

我们需要构建一个接口与实现分离的C项目,核心要求包括:

  • 接口(interface)仅含函数声明,实现(implementation)分多个版本(A/B)可选
  • 所有模块静态链接,且接口、实现可独立复用(可分属不同Git仓库)
  • 根项目通过编译参数选择具体实现版本

推荐保留原文件夹结构,该结构天然满足接口、实现与主项目的分离需求:

│   CMakeLists.txt
│   main.c
│
├───implementation
│   │   CMakeLists.txt
│   │
│   ├───A
│   │       CMakeLists.txt
│   │       one.c
│   │       two.c
│   │
│   └───B
│           CMakeLists.txt
│           one.c
│           two.c
│
└───interface
        CMakeLists.txt
        one.h
        two.h

1. interface模块CMake配置

虽然INTERFACE库常被用于纯头文件库,但此处使用它完全合理:interface模块仅提供头文件声明,无编译产物,正好匹配INTERFACE库"仅传递编译/链接属性"的特性,可便捷地向依赖目标暴露头文件路径。

interface/CMakeLists.txt内容:

cmake_minimum_required(VERSION 3.10)
project(example_interface)

# 定义INTERFACE库,仅传递头文件搜索路径
add_library(example_interface INTERFACE)
# 向所有依赖该库的目标暴露当前目录作为头文件路径
target_include_directories(example_interface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

2. implementation模块配置

每个实现版本(A/B)都作为独立静态库,依赖interface模块以获取头文件路径。

2.1 implementation/A/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(example_impl_a)

# 构建静态库
add_library(example_impl_a STATIC one.c two.c)
# 依赖interface模块,自动继承头文件搜索路径
target_link_libraries(example_impl_a PRIVATE example_interface)

2.2 implementation/B/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(example_impl_b)

add_library(example_impl_b STATIC one.c two.c)
target_link_libraries(example_impl_b PRIVATE example_interface)

2.3 implementation/CMakeLists.txt

作为子目录入口,不直接构建库,仅按需由根目录控制子目录引入:

cmake_minimum_required(VERSION 3.10)
project(example_implementations)

# 不默认添加子目录,由根CMakeLists.txt根据参数选择

3. 根目录CMake配置

根目录负责实现版本选择、模块引入与主程序构建:

CMakeLists.txt内容:

cmake_minimum_required(VERSION 3.10)
project(example_main)

# 提供编译参数,默认选择A实现
option(SELECT_IMPL "Select implementation (A/B)" "A")

# 引入interface模块
add_subdirectory(interface)

# 根据参数选择对应的实现模块
if(SELECT_IMPL STREQUAL "A")
    add_subdirectory(implementation/A)
    set(SELECTED_IMPL example_impl_a)
elseif(SELECT_IMPL STREQUAL "B")
    add_subdirectory(implementation/B)
    set(SELECTED_IMPL example_impl_b)
else()
    message(FATAL_ERROR "Invalid implementation selected: ${SELECT_IMPL}, choose A or B")
endif()

# 构建主程序
add_executable(example_main main.c)
# 链接接口(获取头文件)和选中的实现(静态库)
target_link_libraries(example_main PRIVATE example_interface ${SELECTED_IMPL})

4. 代码示例

interface/one.h

#ifndef ONE_H
#define ONE_H

void func_one(void);

#endif

implementation/A/one.c

#include "one.h"
#include <stdio.h>

void func_one(void) {
    printf("Implementation A: func_one\n");
}

main.c

#include "one.h"
#include "two.h"

int main() {
    func_one();
    func_two();
    return 0;
}

关键要点说明

  • INTERFACE库的合理性:INTERFACE库的核心作用是传递编译/链接属性,并非仅限纯头文件库使用。此处interface模块仅需传递头文件路径,用INTERFACE库是最简洁规范的方式。
  • 复用性保证:interface和implementation的CMake配置均为自包含结构,根项目只需通过add_subdirectory引入即可,无需修改子模块代码,满足跨项目复用需求(即使分属不同Git仓库,也可通过submodule或直接拷贝引入)。
  • 静态链接实现:所有实现模块均构建为STATIC库,主程序链接时会将静态库代码合并到可执行文件中,完全符合静态链接要求。
  • 版本选择方式:用户可在配置阶段通过cmake -DSELECT_IMPL=B ..指定实现版本,默认使用A实现。

内容的提问来源于stack exchange,提问作者user930473

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:22:38