CLion同一目錄下多個main函式
轉載自:https://zhuanlan.zhihu.com/p/157646534
============================================
使用CLion 刷題解決多個main函式問題的終極方法
在寫 C++ 的題目的時候經常會遇到這樣的問題,寫了多個 cpp 檔案,在 clion 中編譯報錯不能同時存在多 main 函式。
下面列舉幾種方法:
方法1:重定義Main
在每個檔案中通過重定義的方法來解決,在寫某道演算法時,對main進行重定義,
執行完後再修改回去,這樣就能接下去就能再重定義為main函式接著運行了。
優點:不需要修改配置檔案
缺點:會讓原始碼檔案中多出一些奇奇怪怪的程式碼,降低程式碼可閱讀性!
方法2:手動修改CmakeList.txt
通過手動新增add_executable(編譯檔名 原始碼檔案地址)
cmake_minimum_required(VERSION 3.16) project(C_AND_C_Plus_Plus_Practise) set(CMAKE_CXX_STANDARD 14) add_executable(MAIN main.cpp) add_executable(CHelloWord ./C_Practise/HelloWord.c) add_executable(C3test ./C_Practise/3_變數/test.c) add_executable(DataStructureLove DataStructure/1Introduction/Love.cpp) add_executable(DataStructureSqList DataStructure/2LinearList/SqList.cpp) add_executable(DataStructureSqList1 DataStructure/2LinearList/SqList1.cpp) add_executable(DataStructureLinkList DataStructure/2LinearList/LinkList.cpp) add_executable(DataStructureLinkList1 DataStructure/2LinearList/LinkList1.cpp)
優點:只修改配置檔案,不會影響原始碼的可讀性
缺點:每新建一個檔案,就得修改配置檔案,較為繁瑣!
方法3:在CMake檔案中編寫自動生成程式
在Cmake檔案中編寫程式,自動生成編譯後的檔名!
基礎版:
# 遍歷專案根目錄下所有的 .cpp 檔案 file (GLOB files *.cpp) foreach (file ${files}) string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file}) add_executable (${exe} ${file}) message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe}) endforeach ()
上邊兒這段程式碼,只會遍歷根目錄下的cpp檔案,不會遍歷根目錄下的二級目錄。
進階版:
# 遍歷專案根目錄及二級目錄下所有的 .cpp 檔案
file (GLOB files *.cpp */*cpp)
foreach (file ${files})
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
add_executable (${exe} ${file})
message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
endforeach ()
進階版就是手動新增訪問二級目錄的規則,同樣要訪問三級目錄就是再新增一個*/*/*.cpp
最優版:
官方文件提供一個解決方法GLOB_RECURSE
,它會自動遍歷工程檔案根目錄下的所有檔案目錄。
# 遍歷專案根目錄下所有的 .cpp 檔案
file (GLOB_RECURSE files *.cpp)
foreach (file ${files})
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
add_executable (${exe} ${file})
message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
endforeach ()
優點:方便省時
缺點:這種方法要求所有cpp檔案命名不重複,不能含有中文,不能含有‘/’等字元!因為它就是直接Copy你的原始碼檔名的。
參考:https://zhuanlan.zhihu.com/p/442889385
===========================================
cmake:string(REGEX REPLACE ...)
1. 需求
專案要求,下位機傳給上位機的版本號為數字格式,並且其中要包含軟體釋出時的日期(年份最低兩位)。比如,軟體版本號為 4,釋出日期為 2021 年 3 月 2 日。那麼傳給上位機的資料為 “1a 03 02 04”。
2. 方案
可以基於 cmake 中的 configure_file 實現:
- CMakeLists.txt 中獲取時間;
- 將時間相關變數寫入 http://config.h.in 檔案中;
- 使用 configure_file() 將 http://config.h.in 中的變數轉換為 C 中可識別的巨集定義,存入 config.h 檔案中;
- 原始碼中使用巨集定義獲取對應時間。
3. 實現
3.1 獲取系統時間
cmake 中使用 string(TIMESTAMP <output_variable> [<format_string>] [UTC])
獲取系統時間。比如:
cmake_minimum_required(VERSION 3.20)
project(cmake-string)
#獲取年月日時分秒
string(TIMESTAMP COMPILE_TIME %Y%m%d%H%M%S)
#獲取年份後兩位,使用 %Y 獲取完整年份
string(TIMESTAMP TIME_YEAR %y)
#獲取月份
string(TIMESTAMP TIME_MONTH %m)
#獲取日期
string(TIMESTAMP TIME_DAY %d)
message(STATUS "compile time:${COMPILE_TIME}")
configure_file (
"${CMAKE_CURRENT_LIST_DIR}/config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/config.h"
)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(${PROJECT_NAME} main.c)
3.2 編寫http://config.h.in 檔案
將上述變數寫入 http://config.h.in 檔案中,然後使用 configure_file()
將其轉為 config.h 檔案後,即可再 .c 檔案中使用。
#ifndef VERSION_CONFIG_H
#define VERSION_CONFIG_H
#cmakedefine TIME_YEAR @TIME_YEAR@
#cmakedefine TIME_MONTH @TIME_MONTH@
#cmakedefine TIME_DAY @TIME_DAY@
#endif
3.3 C 原始碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
int main(int argc, char *argv[])
{
printf("Build time:%d-%d-%d\n", TIME_YEAR, TIME_MONTH, TIME_DAY);
return 0;
}
3.4 執行結果
Easy?NO!
4. 問題
4.1 出現問題
今天,2021 年 12 月 8 日,我要釋出軟體。
編譯:
config.h:
#ifndef VERSION_CONFIG_H
#define VERSION_CONFIG_H
#define TIME_YEAR 21
#define TIME_MONTH 12
#define TIME_DAY 08
#endif
問題就出在 #define TIME_DAY 08 上面。
C 語言中,以 '0' 開頭的數值表示八進位制數,八進位制數的有效值為 0~7。所以,上面的巨集定義肯定是錯誤的。
如何解決?
4.2 解決問題
我想到的辦法是在 CMakeLists.txt 中將變數 TIME_DAY 的值前面的 ‘0’ 去掉。這裡,我使用 string(REGEX REPLACE ...) 語句達到此目的。
在 CMakeLists.txt 中增加以下語句,同時將 http://config.h.in 中的 @TIME_DAY@ 改為 @TIME_DAY_NUM@:
string(REGEX REPLACE "(^[0])([1-9]*)" "\\2" TIME_DAY_NUM ${TIME_DAY})
這語句的意思是:如果變數 TIME_DAY 的值以 '0' 開頭,那麼就將 '0' 去掉,只保留 '0' 以後的數值,並將數值儲存在變數 TIME_DAY_NUM 中。
"(^[0])([1-9]*)" 和 "\\2" 說明:
上述正則表示式使用了子表示式。子表示式使用小括號——'()' 進行分隔,使用時用 '\1'、'\2'... 進行引用。上述語句中,'(^[0])' 為子表示式 1,'([1-9]*)' 為子表示式 2。'"\\2"' 表示只保留子表示式 2 的內容(這種用法我在 source insight 中也用過)。
比如,TIME_DAY 的值為 '08','(^[0])' 會匹配 '0', '([1-9]*)' 匹配 '8','"\\2"' 會將 '8' 儲存到變數 TIME_DAY_NUM 中。因此,在 http://config.h.in 中,需要將 @TIME_DAY@ 改為 @TIME_DAY_NUM@。
cmake 中 string 的更多功能可檢視官方文件:https://cmake.org/cmake/help/latest/command/string.html#string
執行結果:
5. 總結
這篇文章中包含了 3 個知識點:
- CMakeLists.txt 中獲取系統時間;
- configure_file() 用法;
- string(REGEX REPLACE ...) 正則表示式中子表示式的用法。