CMake 多 main() 入口項目搭建(刷題向)


在 ACM 算法競賽 / LeetCode 使用 C++ 進行刷題等場景中,通常:

  • 需要維護很多的 C++ 源代碼
  • 需要有多個 main 函數入口方便執行測試
  • 有一些自己寫的公共函數類庫 (如調試輸出vector等) 在多個文件中引用

本文介紹了通過 CMake 及一些自生成配置文件的工具,通過一個統一的庫,方便管理所有代碼的方法。

CMake 是一個平台無關的可以定制 C++ 編譯流程的工具,可以生成特定平台的 Makefile 文件。默認被 Intellij CLion 支持。


 需要首先通過 CLion 創建一個 C++ 工程:

 

創建出自己需要的目錄結構來:我們把所有的源代碼 .cpp 等文件放在 /src 中, 一些工具類放在 /src/utils 中

 

 

 

 

 

 

CMakeLists.txt 文件就是我們的 CMake 編譯流程配置文件了。為了支持多個 main() 函數入口,我們用  add_executable 命令添加多個 target,這種才能在每個 main() 入口單獨執行:

cmake_minimum_required(VERSION 3.17)
project(lc-cpp)

set(CMAKE_CXX_STANDARD 17)
add_definitions("-DKUN_DEBUG")

add_executable(training_p1 src/normal/cat0/cat00/cat000/p1.cpp)
add_executable(training_p15 src/normal/cat0/cat00/cat001/p15.cpp)
add_executable(biweekly_34_2 src/match/biweekly/biweekly34/p2.cpp)
add_executable(biweekly_34_3 src/match/biweekly/biweekly34/p3.cpp)
add_executable(biweekly_34_4 src/match/biweekly/biweekly34/p4.cpp)

接下來我們處理公共類庫的問題,只需要將 utils 目錄聲明為編譯器的頭文件搜索路徑之下,這樣就能被其他文件引用了。

像是 LeetCode 的 TreeNode, ListNode 等基礎數據結構及其解析、debug 工具可以放在這里面:

include_directories("src/utils")

 

 

 

再來解決最后一個問題。我們每次創建一些 .cpp 代碼后,就需要去 CMakeLists.txt 文件中添加對應的 add_executable 代碼。這部分工作我們可以通過腳本的形式去自動生成,附上 Python3 代碼:

import os

HEAD = '''cmake_minimum_required(VERSION 3.17)
project(lc-cpp)

set(CMAKE_CXX_STANDARD 17)
add_definitions("-DKUN_DEBUG")
include_directories("src/utils")

'''


def update_cmake():
    file_list = []
    for root, dirs, files in os.walk("src"):
        if len(files) == 0:
            continue
        for f in files:
            file_list.append(root + os.sep + f)
    res = HEAD
    for i in sorted(file_list):
        if 'utils' in i:
            continue
        split = i.split(os.sep)
        name_ids = filter(lambda x: x != 'src', split)
        name = "_".join(name_ids).replace(".cpp", "")
        path = "/".join(split)
        code = f'add_executable({name} {path})\n'
        res += code
    with open('CMakeLists.txt', "w") as f:
        f.write(res)


if __name__ == '__main__':
    update_cmake()

 

至此,通過把所以題目按照分類文件夾管理,然后自動生成可執行文件編譯配置,一個比較完成的方便 C++ 刷題的項目環境就創建好了。

 


 

最后附一個 LeetCode 刷題常用的基礎類及解析、輸出工具(common_ds.hpp):

/**
 * LeetCode Common DataStruct.
 */
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct TreeNode {

    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int val, TreeNode *left, TreeNode *right) : val(val), left(left), right(right) {}
};


static TreeNode *parse_tree(string s) {
    const regex re(",");
    vector<string> v(sregex_token_iterator(++s.begin(), --s.end(), re, -1), sregex_token_iterator());
    if (v.empty()) return nullptr;
    vector<TreeNode *> ns;
    ns.push_back(new TreeNode(stoi(v.front())));
    for (int i = 1; i < v.size(); ++i) {
        TreeNode *cur = v[i].find("null") != string::npos ? nullptr : new TreeNode(stoi(v[i]));
        if (i % 2 == 1) ns[(i - 1) / 2]->left = cur;
        else ns[(i - 1) / 2]->right = cur;
        if (cur) ns.push_back(cur);
    }
    return ns.front();
}

static string to_string(TreeNode *root) {
    if (!root) return "null";
    if (!root->left && !root->right) return to_string(root->val);
    return "{" + to_string(root->val) + ", " + to_string(root->left) + ", " + to_string(root->right) + "}";
}

static void print(TreeNode *head, int len = 4, int height = 0, string to = "#") {
    if (!head) return;
    print(head->right, len, height + 1, "v");
    string val = to + to_string(head->val);
    int lenM = val.length(), lenL = (len - lenM) / 2, lenR = len - lenM - lenL;
    val = string(height * len, ' ') + string(lenL, ' ') + val + string(lenR, ' ');
    cout << val << endl;
    print(head->left, len, height + 1, "^");
}

/// =========================================================================

struct ListNode {

    int val;
    ListNode *next;

    ListNode(int x) : val(x), next(nullptr) {}
};

static ListNode *parse_list(string s) {
    const regex re("->");
    vector<string> v(sregex_token_iterator(s.begin(), s.end(), re, -1), sregex_token_iterator());
    ListNode *mock = new ListNode(-1), *p = mock;
    for (auto &i : v) {
        p->next = new ListNode(stoi(i));
        p = p->next;
    }
    return mock->next;
}

static string to_string(ListNode *node) {
    if (!node) return "";
    return to_string(node->val) + (node->next ? "->" + to_string(node->next) : "");
}

static void print(ListNode *node) {
    cout << to_string(node) << endl;
}

/// =========================================================================

struct Interval {

    int start, end;

    Interval() : start(0), end(0) {}

    Interval(int start, int anEnd) : start(start), end(anEnd) {}
};

/// =========================================================================

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM