From b74681a0f521678b40018479a70110e73795695d Mon Sep 17 00:00:00 2001 From: MH Hung Date: Tue, 23 Sep 2025 14:03:16 +0800 Subject: [PATCH] refactor: adopt layered C++ project layout --- Makefile | 18 ++++++++++++------ README.md | 27 ++++++++++++++++++++------- include/project/sample_library.hpp | 22 ++++++++++++++++++++++ src/app/main.cpp | 20 ++++++++++++++++++++ src/lib/sample_library.cpp | 21 +++++++++++++++++++++ src/main.cpp | 14 -------------- 6 files changed, 95 insertions(+), 27 deletions(-) create mode 100644 include/project/sample_library.hpp create mode 100644 src/app/main.cpp create mode 100644 src/lib/sample_library.cpp delete mode 100644 src/main.cpp diff --git a/Makefile b/Makefile index 19e8aeb..df333be 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,28 @@ CXX := g++ CXXFLAGS := -std=c++20 -Wall -Wextra -Wpedantic -O2 +CPPFLAGS := LDFLAGS := SRC_DIR := src +INC_DIR := include BUILD_DIR := build TARGET := app CLANG_FORMAT ?= clang-format CLANG_TIDY ?= clang-tidy -SRCS := $(wildcard $(SRC_DIR)/*.cpp) +SRCS := $(shell find $(SRC_DIR) -name '*.cpp' 2>/dev/null) OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS)) -HDRS := $(wildcard $(SRC_DIR)/*.h) $(wildcard $(SRC_DIR)/*.hpp) -FORMAT_SRCS := $(SRCS) $(HDRS) +SRC_HDRS := $(shell find $(SRC_DIR) \( -name '*.hpp' -o -name '*.h' \) 2>/dev/null) +INCLUDE_HDRS := $(shell [ -d $(INC_DIR) ] && find $(INC_DIR) \( -name '*.hpp' -o -name '*.h' \) 2>/dev/null) +FORMAT_SRCS := $(SRCS) $(SRC_HDRS) $(INCLUDE_HDRS) + +CPPFLAGS += -I$(INC_DIR) $(TARGET): $(OBJS) $(CXX) $(OBJS) $(LDFLAGS) -o $@ -$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) - $(CXX) $(CXXFLAGS) -c $< -o $@ +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR): mkdir -p $(BUILD_DIR) @@ -49,7 +55,7 @@ tidy: @if [ -n "$(strip $(SRCS))" ]; then \ for src in $(SRCS); do \ echo "running clang-tidy on $$src"; \ - $(CLANG_TIDY) $$src -- $(CXXFLAGS); \ + $(CLANG_TIDY) $$src -- $(CPPFLAGS) $(CXXFLAGS); \ done; \ else \ echo "warning: no source files found for clang-tidy"; \ diff --git a/README.md b/README.md index 5176a83..d9bef92 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,22 @@ 這個專案提供一個簡單的 C++ 樣板,包含基本的目錄結構、Makefile 與開發輔助工具,方便快速開始新專案。 ## 目錄結構 -- `src/`: 主要的 C++ 原始碼,目前包含 `main.cpp`。 -- `build/`: 由 Makefile 自動建立,用來存放編譯後的物件檔。 -- `utils/`: 收錄常用腳本,例如編譯、執行、清理、格式化與 clang-tidy。 -- `.clang-format`: 定義 clang-format 的專案格式規則。 -- `.clang-tidy`: 定義 clang-tidy 的檢查設定。 -- `Makefile`: 管理編譯、執行、清理、格式化與靜態分析等命令。 +```text +. +├── include/ +│ └── project/ +│ └── sample_library.hpp # 對外公開的標頭 +├── src/ +│ ├── app/ +│ │ └── main.cpp # 可執行程式進入點 +│ └── lib/ +│ └── sample_library.cpp # 函式庫實作 +├── utils/ # Shell 腳本工具 +├── build/ # make 時產生,存放物件檔 +├── .clang-format +├── .clang-tidy +└── Makefile +``` ## 建置與執行 若系統已安裝 `g++`,可使用以下命令: @@ -65,11 +75,14 @@ CXX=clang++ CXXFLAGS="-std=c++20 -O3" make - 4 空格縮排,最大列寬 100 - 自訂括號換行與 namespace 縮排規則 +## 範例程式碼 +`include/project/sample_library.hpp` 與 `src/lib/sample_library.cpp` 呈現一個簡單的 `project::Greeter` 類別與 `generate_sequence` 函式,`.hpp` 只放宣告、`.cpp` 負責實作,示範「公開標頭放在 include/,實作放在 src/」的常見結構。`src/app/main.cpp` 會引入該標頭並呼叫它們,執行 `make run` 即可看到輸出。如果要建立自己的模組,可依此模式建立額外的標頭與來源檔,並依需求放在 `src/lib/`、`src/app/` 或新的子資料夾。 + ## 靜態分析 `.clang-tidy` 啟用了一些常用的檢查(`bugprone-*`、`clang-analyzer-*`、`modernize-*` 等)。可使用 `make tidy` 或 `CLANG_TIDY=/path/to/clang-tidy make tidy` 對 `src/*.cpp` 逐一檢查;若要檢查特定檔案,可執行: ```bash -clang-tidy src/main.cpp -- +clang-tidy src/app/main.cpp -- -Iinclude ``` 可視需求調整 `Checks`、`HeaderFilterRegex` 或其他設定來符合專案需求。 diff --git a/include/project/sample_library.hpp b/include/project/sample_library.hpp new file mode 100644 index 0000000..0b6027a --- /dev/null +++ b/include/project/sample_library.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace project { + +// Greeter builds a greeting message for a given name. +class Greeter { +public: + explicit Greeter(std::string base = "Hello") : base_message_(std::move(base)) {} + + [[nodiscard]] std::string greet(const std::string &name) const; + +private: + std::string base_message_; +}; + +// generate_sequence returns numbers from 0 up to limit (exclusive). +[[nodiscard]] std::vector generate_sequence(int limit); + +} // namespace project diff --git a/src/app/main.cpp b/src/app/main.cpp new file mode 100644 index 0000000..2de19a5 --- /dev/null +++ b/src/app/main.cpp @@ -0,0 +1,20 @@ +#include + +#include + +int main() { + std::ios::sync_with_stdio(false); + std::cin.tie(nullptr); + + const project::Greeter greeter{"Welcome"}; + std::cout << greeter.greet("Coding World") << '\n'; + + auto sequence = project::generate_sequence(5); + std::cout << "Sample sequence:"; + for (int value : sequence) { + std::cout << ' ' << value; + } + std::cout << '\n'; + + return 0; +} diff --git a/src/lib/sample_library.cpp b/src/lib/sample_library.cpp new file mode 100644 index 0000000..87b6c8f --- /dev/null +++ b/src/lib/sample_library.cpp @@ -0,0 +1,21 @@ +#include + +#include + +namespace project { + +std::string Greeter::greet(const std::string &name) const { + return base_message_ + ", " + name + "!"; +} + +std::vector generate_sequence(int limit) { + if (limit < 0) { + limit = 0; + } + + std::vector result(static_cast(limit)); + std::iota(result.begin(), result.end(), 0); + return result; +} + +} // namespace project diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 0151347..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include -#include - -int main() { - std::ios::sync_with_stdio(false); - std::cin.tie(nullptr); - - // TODO: Add program logic here. - - return 0; -}