CMake解析參數用法示例

cmake_parse_arguments 是 CMake 中用于解析函數或宏參數的工具,特別適合處理帶有選項(OPTIONS)、單值參數(SINGLE_ARGS)和多值參數(MULTI_ARGS)的復雜參數列表。以下是用法說明和一個示例:


基本語法

cmake_parse_arguments(<PREFIX>          # 解析后變量的前綴"<OPTIONS>"       # 選項列表(如 --enable-foo)"<SINGLE_ARGS>"   # 單值參數列表(如 --key VALUE)"<MULTI_ARGS>"    # 多值參數列表(如 --files a.txt b.txt)${ARGN}           # 需要解析的參數(通常用 ${ARGN})

解析后,可以通過以下變量訪問參數:

  • 選項: ${<PREFIX>_<OPTION_NAME>}(值為 TRUE/FALSE
  • 單值參數: ${<PREFIX>_<SINGLE_ARG_NAME>}
  • 多值參數: ${<PREFIX>_<MULTI_ARG_NAME>}

示例

假設我們需要定義一個宏 setup_project,支持以下參數:

  • 選項: ENABLE_TEST(是否啟用測試)
  • 單值參數: VERSION(項目版本號)
  • 多值參數: SOURCES(源文件列表)
代碼實現
# 定義宏
macro(setup_project)# 解析參數set(OPTIONS "ENABLE_TEST")        # 選項列表set(SINGLE_ARGS "VERSION")        # 單值參數列表set(MULTI_ARGS "SOURCES")         # 多值參數列表cmake_parse_arguments("ARG"                         # 前綴為 ARG"${OPTIONS}""${SINGLE_ARGS}""${MULTI_ARGS}"${ARGN}                       # 傳入宏的參數)# 檢查是否有未解析的參數(可選)if(ARG_UNPARSED_ARGUMENTS)message(WARNING "Unknown arguments: ${ARG_UNPARSED_ARGUMENTS}")endif()# 處理選項if(ARG_ENABLE_TEST)message(STATUS "Testing enabled")# 添加測試相關邏輯...endif()# 處理單值參數if(ARG_VERSION)message(STATUS "Project version: ${ARG_VERSION}")else()message(FATAL_ERROR "VERSION must be specified!")endif()# 處理多值參數if(ARG_SOURCES)message(STATUS "Source files: ${ARG_SOURCES}")# 添加編譯目標...add_executable(my_app ${ARG_SOURCES})else()message(FATAL_ERROR "SOURCES must be specified!")endif()
endmacro()# 調用示例
setup_project(ENABLE_TESTVERSION 1.0.0SOURCES src/main.cpp src/utils.cpp
)

輸出結果

-- Testing enabled
-- Project version: 1.0.0
-- Source files: src/main.cpp;src/utils.cpp

注意事項

  1. 變量作用域:如果使用 macro 而非 function,解析后的變量會影響父作用域。建議在函數中使用 function + PARENT_SCOPE 控制作用域。
  2. 參數順序:CMake 不關心參數順序,但需要確保參數名和值正確匹配。
  3. 未解析參數:通過 ${<PREFIX>_UNPARSED_ARGUMENTS} 可以捕獲未知參數。

通過 cmake_parse_arguments,你可以更靈活地處理 CMake 函數/宏的復雜參數!


你提到的這一點非常重要!在 CMake 中,宏(macro)函數(function)的行為有本質區別(尤其是作用域規則不同)。以下我會詳細解釋它們的差異,并給出一個函數的例子,同時說明為何在之前的回答中選擇了宏。


為什么先用了宏(macro)的例子?

  1. 簡化示例
    在簡單的示例中,宏的變量作用域規則(直接修改父作用域)可以讓代碼更簡潔,不需要額外處理變量傳遞。對于新手來說,這可能更容易理解。

  2. 歷史原因
    早期 CMake 版本中,cmake_parse_arguments 在函數中的行為需要更謹慎的作用域處理,而宏的行為更“直接”。

但實際項目中,**函數(function)**通常是更安全的選擇,因為它能隔離作用域,避免變量污染。


函數(function)的示例

以下是將之前的 setup_project 從宏改為函數的實現,并修復作用域問題:

代碼實現
# 定義函數
function(setup_project)# 解析參數set(OPTIONS "ENABLE_TEST")        # 選項列表set(SINGLE_ARGS "VERSION")        # 值參數列表set(MULTI_ARGS "SOURCES")         # 多值參數列表cmake_parse_arguments("ARG"                         # 前綴為 ARG"${OPTIONS}""${SINGLE_ARGS}""${MULTI_ARGS}"${ARGN}                       # 傳入函數的參數)# 檢查未解析的參數(可選)if(ARG_UNPARSED_ARGUMENTS)message(WARNING "Unknown arguments: ${ARG_UNPARSED_ARGUMENTS}")endif()# 處理選項if(ARG_ENABLE_TEST)message(STATUS "Testing enabled")# 啟用測試邏輯(例如 include(CTest)endif()# 處理單值參數if(NOT ARG_VERSION)message(FATAL_ERROR "VERSION must be specified!")endif()message(STATUS "Project version: ${ARG_VERSION}")# 處理多值參數if(NOT ARG_SOURCES)message(FATAL_ERROR "SOURCES must be specified!")endif()message(STATUS "Source files: ${ARG_SOURCES}")# 關鍵點:將變量傳遞到父作用域(如果需要影響外部)# 例如,將源文件列表傳遞給父作用域:set(SOURCES ${ARG_SOURCES} PARENT_SCOPE)# 在函數內創建目標(變量直接可見)add_executable(my_app ${ARG_SOURCES})
endfunction()# 調用示例
setup_project(ENABLE_TESTVERSION 1.0.0SOURCES src/main.cpp src/utils.cpp
)

宏(macro) vs 函數(function)的關鍵區別

特性宏(macro)函數(function)
作用域直接修改父作用域的變量默認只在函數內部作用域有效
變量傳遞自動“泄漏”到父作用域需顯式使用 set(... PARENT_SCOPE)
性能類似代碼替換,可能更高效有獨立作用域,稍復雜
安全性容易污染父作用域變量隔離作用域,更安全

為什么推薦使用函數?

  1. 避免變量污染
    函數不會意外覆蓋父作用域的變量(例如,如果父作用域中已經有一個 VERSION 變量,宏可能會直接修改它,而函數不會)。

  2. 邏輯隔離
    函數內部的操作(如 add_executable)默認只在函數作用域內生效,除非顯式傳遞變量到父作用域。

  3. 可維護性
    函數的隔離性使得代碼行為更可預測,適合復雜項目。


何時使用宏?

  1. 需要直接修改父作用域
    例如,在頂層腳本中快速操作變量。
  2. 簡單代碼替換
    當邏輯非常簡單且不需要隔離作用域時。

總結

  • 如果你的目標是封裝一個可復用的邏輯模塊,優先使用函數
  • 如果示例中使用了宏,可以補充一個函數的版本,明確說明作用域處理的差異。這樣用戶可以根據實際需求選擇。

這段代碼的目的是通過兩次調用 cmake_parse_arguments 實現一種嵌套參數解析的機制。它的核心思路是:先解析外層的“元參數”(定義如何解析內層參數),再用這些元參數去解析實際傳遞的參數。以下是對代碼的詳細解釋,以及這種設計的優缺點分析。


代碼解釋

function(nuttx_parse_function_args)cmake_parse_arguments(IN "" "FUNC""OPTIONS;ONE_VALUE;MULTI_VALUE;REQUIRED;ARGN" "${ARGN}")cmake_parse_arguments(OUT "${IN_OPTIONS}" "${IN_ONE_VALUE}""${IN_MULTI_VALUE}" "${IN_ARGN}")if(OUT_UNPARSED_ARGUMENTS)message(FATAL_ERROR "${IN_NAME}: unparsed ${OUT_UNPARSED_ARGUMENTS}")endif()foreach(arg ${IN_OPTIONS} ${IN_ONE_VALUE} ${IN_MULTI_VALUE})set(${arg}${OUT_${arg}}PARENT_SCOPE)endforeach()endfunction()
1. 函數定義
function(nuttx_parse_function_args)

定義了一個名為 nuttx_parse_function_args 的函數,目的是封裝一個通用的參數解析工具。


2. 第一次解析:cmake_parse_arguments(IN ...)
cmake_parse_arguments(IN ""                   # 外層無選項(OPTIONS)"FUNC"               # 外層單值參數(SINGLE_ARGS)"OPTIONS;ONE_VALUE;MULTI_VALUE;REQUIRED;ARGN"  # 外層多值參數(MULTI_ARGS)"${ARGN}"            # 輸入的參數列表
)
  • 目的:解析外層的“元參數”,這些參數定義了如何解析內層的實際參數。
  • 解析結果
    • IN_FUNC: 單值參數,表示實際要解析的函數名(可能用于錯誤提示)。
    • IN_OPTIONS: 多值參數,定義內層參數的選項(OPTIONS)。
    • IN_ONE_VALUE: 多值參數,定義內層參數的單值參數(SINGLE_ARGS)。
    • IN_MULTI_VALUE: 多值參數,定義內層參數的多值參數(MULTI_ARGS)。
    • IN_ARGN: 多值參數,保存實際需要解析的內層參數列表。

3. 第二次解析:cmake_parse_arguments(OUT ...)
cmake_parse_arguments(OUT "${IN_OPTIONS}"      # 內層選項(來自第一次解析的 IN_OPTIONS)"${IN_ONE_VALUE}"    # 內層單值參數(來自第一次解析的 IN_ONE_VALUE)"${IN_MULTI_VALUE}"  # 內層多值參數(來自第一次解析的 IN_MULTI_VALUE)"${IN_ARGN}"         # 實際需要解析的參數(來自第一次解析的 IN_ARGN)
)
  • 目的:用第一次解析得到的元參數(IN_OPTIONS, IN_ONE_VALUE, IN_MULTI_VALUE)去解析實際的內層參數(IN_ARGN)。
  • 解析結果
    • OUT_<OPTION>: 內層選項的值(TRUE/FALSE)。
    • OUT_<SINGLE_ARG>: 內層單值參數的值。
    • OUT_<MULTI_ARG>: 內層多值參數的值。

4. 錯誤檢查
if(OUT_UNPARSED_ARGUMENTS)message(FATAL_ERROR "${IN_NAME}: unparsed ${OUT_UNPARSED_ARGUMENTS}")
endif()
  • 檢查是否有未解析的參數,如果有則報錯。

5. 將解析結果傳遞到父作用域
foreach(arg ${IN_OPTIONS} ${IN_ONE_VALUE} ${IN_MULTI_VALUE})set(${arg}${OUT_${arg}}PARENT_SCOPE)
endforeach()
  • 將內層解析得到的參數值(OUT_<arg>)傳遞到父作用域,使得調用者可以訪問這些參數。

為什么使用兩次解析?

這種設計的關鍵在于動態定義參數解析規則

  1. 外層解析:定義內層參數的解析規則(哪些是選項、單值參數、多值參數)。
  2. 內層解析:根據外層解析得到的規則,解析實際的參數列表。

例如,假設調用代碼如下:

nuttx_parse_function_args(FUNC my_functionOPTIONS ENABLE_FOOONE_VALUE VERSIONMULTI_VALUE SOURCESARGN ENABLE_FOO VERSION 1.0.0 SOURCES a.cpp b.cpp
)
  • 外層解析會提取 OPTIONS ENABLE_FOO, ONE_VALUE VERSION, MULTI_VALUE SOURCES,并將 ENABLE_FOO VERSION 1.0.0 SOURCES a.cpp b.cpp 保存到 IN_ARGN
  • 內層解析會根據外層解析的規則,將 ENABLE_FOO 解析為選項,VERSION 解析為單值參數,SOURCES 解析為多值參數。

優點

  1. 靈活性
    允許動態指定參數解析規則,可以復用同一個函數解析不同結構的參數。
  2. 通用性
    適合需要為多個函數或宏統一封裝參數解析邏輯的場景。
  3. 減少重復代碼
    避免在每個函數中重復編寫 cmake_parse_arguments

缺點

  1. 復雜度高
    嵌套解析邏輯難以理解,尤其是對 CMake 新手。
  2. 依賴外層參數的正確性
    如果外層參數(如 OPTIONS, ONE_VALUE)未正確傳遞,內層解析會失敗。
  3. 錯誤處理困難
    嵌套解析可能導致錯誤信息不直觀(例如,外層參數錯誤和內層參數錯誤混雜)。
  4. 變量作用域問題
    需要顯式傳遞變量到父作用域,容易遺漏或出錯。

改進建議

  1. 添加注釋
    明確說明兩次解析的目的,尤其是外層參數的含義。
  2. 嚴格的錯誤檢查
    在外層解析后檢查必要的元參數(如 FUNC, OPTIONS 是否存在)。
  3. 示例和文檔
    提供調用示例和文檔,說明如何正確傳遞外層和內層參數。
  4. 簡化設計
    如果不需要動態定義解析規則,應優先使用單次 cmake_parse_arguments

總結

這段代碼通過兩次 cmake_parse_arguments 實現了一種動態參數解析機制,雖然靈活但復雜度較高。在實際項目中,應權衡靈活性和可維護性:如果不需要動態指定解析規則,應避免嵌套解析;若必須使用,需確保充分的錯誤處理和文檔支持。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79219.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79219.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79219.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自定義JackSon配置

避免前端&#xff08;JavaScript&#xff09;處理大數&#xff08;如 Long、BigInteger&#xff09;時發生精度丟失問題&#xff0c;所以引入了自定義 Jackson 配置。 先看代碼&#xff1a; /** 根據id修改員工信息*/PutMappingpublic R<String> update(HttpServletRequ…

Qt入門——什么是Qt?

Qt背景介紹 什么是Qt? Qt 是?個 跨平臺的 C 圖形用戶界面應用程序框架 。它為應用程序開發者提供了建立藝術級圖形界面所需的所有功能。它是 完全面向對象 的&#xff0c;很容易擴展。Qt 為開發者提供了 ?種基于組件的開發模式 &#xff0c;開發者可以通過簡單的拖拽和組合…

Linux CentOS 安裝Python 3.8.0

在 CentOS 上升級 Python 3.6.8 到 3.8.0&#xff0c;可以按照以下步驟操作&#xff1a; 1. 安裝依賴 sudo yum groupinstall -y "Development Tools" sudo yum install -y openssl-devel bzip2-devel libffi-devel zlib-devel wget 如果遇到報錯“File "/bin…

Python爬蟲(11)Python數據存儲實戰:深入解析NoSQL數據庫的核心應用與實戰

目錄 引言一、背景&#xff1a;為什么選擇NoSQL存儲爬蟲數據&#xff1f;1.1 爬蟲數據的核心挑戰1.2 NoSQL數據庫的核心優勢 二、NoSQL數據庫在爬蟲中的核心應用2.1 MongoDB&#xff1a;文檔型數據庫的王者2.2 Redis&#xff1a;內存數據庫的極致性能 三、NoSQL選型與性能優化策…

PCB設計工藝規范(一)概述

PCB設計工藝規范&#xff08;一&#xff09; 1.概述2.關鍵詞及引用標準3.PCB板材要求3.1 確定PCB使用板材以及TG值3.2 確定 PCB 的表面處理鍍層 4.熱設計要求5.器件庫選項要求 資料來自網絡&#xff0c;僅供學習使用。 1.概述 規范產品的 PCB 工藝設計&#xff0c;規定 PCB 工…

proxychains4系統代理for linux(加速國內github下載速度,pip安裝)

1.proxychains4代理安裝&#xff1a; sudo apt-get install proxychains42.找到配置文件/etc/proxychains4.conf在[ProxyList]后面添加以下內容&#xff1a; socks5 127.0.0.1 10808 配置如下&#xff1a; 3.使用proxychains4(git clone)&#xff1a; proxychains4 git c…

Seata客戶端@GlobalTransactional核心源碼解析

文章目錄 前言一、GlobalTransactional1.1、wrapIfNecessary1.2、handleGlobalTransaction1.3、invoke 二、總結 前言 Seata是阿里開源的分布式事務解決方案。在Spring傳統的事務中&#xff0c;開啟事務&#xff0c;執行事務&#xff0c;回滾/提交事務&#xff0c;統一由Spring…

pytorch搭建并訓練神經網絡

#從小白開始學習人工智能# #學習筆記# 工具&#xff1a;pytorch 一、基礎概念 1.神經網絡是什么&#xff1f; 神經網絡是人類受到生物神經細胞結構啟發而研究出的算法體系。又稱為人工神經網絡&#xff08;Artificial neural network&#xff09; 最簡版神經網絡結構圖&a…

JavaEE-多線程實戰01

Java 多線程入門&#xff1a;第一個多線程程序 在 Java 中&#xff0c;多線程編程是非常重要的一部分。本篇文章將通過示例&#xff0c;帶你快速了解如何創建第一個多線程程序&#xff0c;并深入分析其運行機制。 1. 創建一個線程類并繼承 Thread 在 Java 中&#xff0c;我們…

Android Compose 無網絡狀態處理全指南:從基礎到高級實踐

Android Compose 無網絡狀態界面處理全方案 引言 在移動應用開發中&#xff0c;網絡連接不穩定是常見場景。優雅地處理無網絡狀態能顯著提升用戶體驗。Jetpack Compose 提供了強大的工具來實現各種網絡狀態下的界面展示。本文將全面介紹在 Compose 中處理無網絡狀態的多種方案…

Arduino項目實戰與編程技術詳解

一、智能避障小車:超聲波傳感器與PWM電機控制 1.1 硬件需求與工作原理 智能避障小車的核心在于超聲波傳感器與電機驅動模塊的協同工作。超聲波傳感器(HC-SR04)通過發射高頻聲波并接收回波來測量距離,而L298N電機驅動模塊則負責控制兩個直流電機的轉向與速度。 1.1.1 超聲…

Java在云計算、大數據、云原生下的應用和優勢 - 面試實戰

Java在云計算、大數據、云原生下的應用和優勢 - 面試實戰 第一輪提問 面試官&#xff1a;馬架構&#xff0c;請簡單介紹一下Java在云計算中的主要應用場景有哪些&#xff1f; 馬架構&#xff1a;Java在云計算中的主要應用場景包括微服務架構設計、容器化部署&#xff08;如D…

數據庫與大數據技術教程資料

概述 無論你是剛入門的技術新人&#xff0c;還是尋求突破的資深工程師&#xff0c;這份精心整理的電子書合輯將為你打開系統性學習的大門&#xff01;所有資源支持多端閱讀&#xff0c;助力技術成長每一步資料已經整理好&#xff0c;喜歡的朋友請自取&#xff1a;https://pan.…

【Spring Boot 注解】@ConfigurationProperties

文章目錄 ConfigurationProperties注解一、簡介二、依賴引入三、基本用法四、主要特性五、激活方式六&#xff0c;優點七、與 Value 對比 ConfigurationProperties注解 一、簡介 ConfigurationProperties 是 Spring Boot 提供的一個強大注解&#xff0c;用于將外部配置&#…

C++(初階)(十六)——set

set setset介紹set的構造和迭代器set的增刪查findlower_boundmultiset和set的差異 題目[349. 兩個數組的交集 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/problems/intersection-of-two-arrays/description/)交集差集[142. 環形鏈表 II - 力扣&#xff08;L…

higress之:讓流量通過gateway

本來想測跨域問題&#xff0c;結果參數配置過去之后一直沒生效&#xff0c;經過了解說是gateway才是設置跨域參數的核心&#xff0c;所以需要讓流量通過gateway&#xff0c;搗鼓了半天記錄一下 第一步&#xff0c;測試服務是否正常 通過get svc、pod等&#xff0c;發現各pod都…

C盤哪些文件刪除之后無影響,可以清理磁盤空間。

C盤是電腦的系統盤,存放了操作系統的重要文件和部分默認安裝的軟件。當C盤空間不足時,系統可能運行緩慢甚至卡頓,這時清理C盤是一個有效的解決方法。由于C盤包含許多關鍵數據,清理時需要格外謹慎,以免誤刪導致系統崩潰。將詳細介紹C盤中可以安全刪除的文件類型及清理方法,…

開源項目實戰學習之YOLO11:ultralytics-cfg-models-fastsam(九)

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 1. __init__.py2. model.py3. predict.py4. utils.py5. val.py FastSAM 是一種目標檢測和圖像分割模型&#xff0c;Ultralytics 是一個在計算機視覺領域廣泛使用的庫&#x…

Windows11安裝Docker

本次安裝環境 Windows11&#xff08;23H2&#xff09;&#xff0c;CPU&#xff08;12代Intel&#xff09; 什么是Docker Docker 是一個軟件平臺&#xff0c;讓您可以快速構建、測試和部署應用程序。Docker 將軟件打包成名為容器的標準化單元&#xff0c;這些單元具有運行軟件所…

C# 在VS2022中開發常用設置

一、基礎環境配置 1. 安裝必要組件 在 VS2022 安裝時確保勾選以下工作負載&#xff1a; ??使用 .NET 的桌面開發??&#xff08;包含 WPF/WinForms&#xff09;??ASP.NET 和 Web 開發????.NET 跨平臺開發????Azure 開發????數據存儲和處理?? 2. 主題與外…