如何使用CMake语言编写函数式接口并实现功能封装
CMake本身没有原生的闭包和结构化返回值语法,但可以通过以下几种方案实现你要的显式输入输出、封装效果,完全替代全局隐式变量的脚本写法:
方案1:函数显式传参 + 自定义前缀输出变量
用function()配合CMake内置的cmake_parse_arguments解析输入参数,输出变量由调用方指定前缀,完全避免全局命名冲突,接近你要的结构化返回效果:
# 封装独立的查找函数 function(find_folly) # 定义输入参数格式:开关项、单值项、多值项 set(options REQUIRED STATIC) set(one_value_args MIN_VERSION OUTPUT_PREFIX ERROR_VAR) set(multi_value_args COMPONENTS) cmake_parse_arguments(ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) # 内部查找逻辑 find_path(FOLLY_INCLUDE_DIR folly/folly.h) find_library(FOLLY_LIBRARY NAMES folly) # 错误处理 if(NOT FOLLY_INCLUDE_DIR OR NOT FOLLY_LIBRARY) if(ARG_REQUIRED) message(FATAL_ERROR "folly library not found") endif() # 回传错误信息 if(ARG_ERROR_VAR) set(${ARG_ERROR_VAR} "folly not found, required min version: ${ARG_MIN_VERSION}" PARENT_SCOPE) endif() set(${ARG_OUTPUT_PREFIX}_FOUND FALSE PARENT_SCOPE) return() endif() # 结构化输出所有结果,全部挂载到调用方指定的前缀下 set(${ARG_OUTPUT_PREFIX}_FOUND TRUE PARENT_SCOPE) set(${ARG_OUTPUT_PREFIX}_INCLUDE_DIRS ${FOLLY_INCLUDE_DIR} PARENT_SCOPE) set(${ARG_OUTPUT_PREFIX}_LIBRARIES ${FOLLY_LIBRARY} PARENT_SCOPE) set(${ARG_OUTPUT_PREFIX}_VERSION "2024.01.01" PARENT_SCOPE) endfunction()
调用示例:
# 调用时自定义输出前缀,同时接收错误信息 find_folly( REQUIRED MIN_VERSION 2023.01.01 OUTPUT_PREFIX MyProj_Folly ERROR_VAR FOLLY_FIND_ERR ) if(FOLLY_FIND_ERR) message(WARNING "Find folly failed: ${FOLLY_FIND_ERR}, use built-in substitute") else() target_include_directories(my_app PRIVATE ${MyProj_Folly_INCLUDE_DIRS}) target_link_libraries(my_app PRIVATE ${MyProj_Folly_LIBRARIES}) endif()
方案2:导入目标封装(CMake官方推荐)
如果不需要暴露一堆配置变量,更干净的做法是封装为命名空间导入目标,所有编译参数、依赖、路径都绑定在目标上,调用方直接链接即可,完全不用处理变量:
# 在查找逻辑末尾加入导入目标定义 if(NOT TARGET Folly::folly) add_library(Folly::folly UNKNOWN IMPORTED) set_target_properties(Folly::folly PROPERTIES IMPORTED_LOCATION ${FOLLY_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${FOLLY_INCLUDE_DIR} INTERFACE_COMPILE_DEFINITIONS "FOLLY_ENABLE_FIBER=1" INTERFACE_LINK_LIBRARIES "pthread;glog::glog" ) endif()
调用方使用方式:
find_package(Folly REQUIRED) # 一行代码自动引入所有头文件、库、编译选项 target_link_libraries(my_app PRIVATE Folly::folly)
方案3:回调函数实现类闭包特性
CMake 3.18及以上版本支持cmake_language(CALL)语法,可以把函数作为参数传递,实现类似闭包的效果,将查找逻辑和业务逻辑分离:
# 封装查找 + 回调逻辑 function(with_folly CALLBACK_FUNC) find_folly(REQUIRED OUTPUT_PREFIX _folly ERROR_VAR _err) if(_err) message(FATAL_ERROR ${_err}) endif() # 调用传入的回调函数,把查找结果作为参数传递 cmake_language(CALL ${CALLBACK_FUNC} ${_folly_INCLUDE_DIRS} ${_folly_LIBRARIES}) endfunction() # 自定义业务回调 function(my_app_folly_config INCLUDE_DIRS LIBRARIES) target_include_directories(my_app PRIVATE ${INCLUDE_DIRS}) target_link_libraries(my_app PRIVATE ${LIBRARIES}) target_compile_definitions(my_app PRIVATE "USE_FOLLY=1") endfunction() # 调用,传入回调即可 with_folly(my_app_folly_config)
封装为独立包
你可以把上述所有函数写到独立的FollyHelpers.cmake脚本中,放到项目的cmake目录下,在主CMakeLists.txt中加入配置:
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
之后任何需要使用的地方直接include(FollyHelpers)即可导入所有封装好的函数,完全替代原来的带隐式变量的脚本。
内容的提问来源于stack exchange,提问作者benedict97
相关产品推荐
相关产品推荐

