1. 程式人生 > 其它 >CLion同一目錄下多個main函式

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 實現:

  1. CMakeLists.txt 中獲取時間;
  2. 將時間相關變數寫入  檔案中;
  3. 使用 configure_file() 將  中的變數轉換為 C 中可識別的巨集定義,存入 config.h 檔案中;
  4. 原始碼中使用巨集定義獲取對應時間。

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 編寫 檔案

將上述變數寫入  檔案中,然後使用 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 中增加以下語句,同時將  中的 @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 中。因此,在  中,需要將 @TIME_DAY@ 改為 @TIME_DAY_NUM@

cmake 中 string 的更多功能可檢視官方文件:

執行結果:

5. 總結

這篇文章中包含了 3 個知識點:

    1. CMakeLists.txt 中獲取系統時間;
    2. configure_file() 用法;
    3. string(REGEX REPLACE ...) 正則表示式中子表示式的用法。