diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3476333 --- /dev/null +++ b/.clang-format @@ -0,0 +1,23 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 120 +AlignEscapedNewlines: Left +AlignAfterOpenBracket: AlwaysBreak +# +# Bind * to the type rather than the name. +PointerAlignment: Left +# +# Put function name on separate line from return type. +AlwaysBreakAfterReturnType: All +# +# Put arguments either all on same line or on separate lines. +BinPackArguments: false +# +# Put function parameters on separate lines. +BinPackParameters: false +# +# Open brace goes on new line only when starting a new struct, enum, or func. +BreakBeforeBraces: Mozilla +# +# Don't sort includes in alphabetical order because Windows headers are odd. +SortIncludes: false \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d094ad3..4131adb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,14 @@ jobs: - name: Install build tools run: | sudo apt-get update - sudo apt-get install -y build-essential g++ clang + sudo apt-get install -y build-essential g++ clang clang-format - # 3. Build and run all examples dynamically + # 3. Clang-format check + - name: Clang-format Check + run: | + ./clang-check.sh *.cpp *.hpp *.c *.h + + # 4. Build and run all examples dynamically - name: Build and Run run: | for san in address thread undefined; do diff --git a/README.md b/README.md index 644c8c0..9fddb60 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Examples include (and will expand to): * Smart pointers * [unique-ptr-basics](./unique-ptr-basics/) * Lock‑free / wait‑free data structures +* Views + * [views-zip-enumerate](./views-zip-enumerate/) * Atomics and memory ordering * RAII and ownership patterns * Performance‑oriented C++ idioms @@ -101,7 +103,7 @@ Common compiler settings live in [common.mk](./common.mk): ```make CXX := g++ -CXXFLAGS := -std=c++20 -Wall -Wextra +CXXFLAGS := -std=c++23 -Wall -Wextra ``` Individual examples may extend this, e.g.: @@ -131,6 +133,15 @@ make SANITIZE= --- +## Clang Format +The `clang-format` is used to ensure the code format. + +```bash +./clang-check.sh *.cpp *.hpp +``` + +--- + ## Continuous Integration GitHub Actions automatically builds all examples on: @@ -140,11 +151,16 @@ GitHub Actions automatically builds all examples on: The CI setup requires **no updates** when new example folders are added. +The CI will perform: +1. `./clang-check.sh *.cpp *.hpp` +2. `make SANITIZE=[address, thread, undefined]` +3. `make run` + --- ## Toolchain -* C++20 +* C++23 * GNU Make * GCC / Clang (CI currently uses GCC) * Linux (Ubuntu) diff --git a/clang-check.sh b/clang-check.sh new file mode 100755 index 0000000..c441a3c --- /dev/null +++ b/clang-check.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -euo pipefail + +# Supported extensions +supported_ext=("c" "h" "cpp" "hpp") + +# Check if a file is supported +is_supported_file() { + local file="$1" + local ext="${file##*.}" + for e in "${supported_ext[@]}"; do + [[ "$ext" == "$e" ]] && return 0 + done + return 1 +} + +# Run clang-format in dry-run mode on a single file +format_file() { + local file="$1" + if is_supported_file "$file"; then + echo "Check Formatting $file" + clang-format --dry-run --Werror "$file" + else + echo "Skipping unsupported file: $file" + fi +} + +# Run clang-format on a glob pattern recursively +format_pattern() { + local pattern="$1" + # Find files matching the pattern + find . -type f -iname "$pattern" ! -path "./.git/*" -print0 | while IFS= read -r -d '' file; do + format_file "$file" + done +} + +# Main +if [ "$#" -eq 0 ]; then + # Default: format all supported files + for ext in "${supported_ext[@]}"; do + format_pattern "*.$ext" + done +else + for arg in "$@"; do + if [ -f "$arg" ]; then + format_file "$arg" + else + # Treat as a pattern + format_pattern "$arg" + fi + done +fi diff --git a/common.mk b/common.mk index e1b2520..a126a30 100644 --- a/common.mk +++ b/common.mk @@ -26,7 +26,7 @@ endif # -------------------------- # Compiler / Linker flags # -------------------------- -CXXFLAGS := -std=c++20 -Wall -Wextra $(OPTFLAGS) $(SAN_FLAGS) +CXXFLAGS := -std=c++23 -Wall -Wextra $(OPTFLAGS) $(SAN_FLAGS) LDFLAGS := $(SAN_FLAGS) # -------------------------- diff --git a/thread-safe-queue/Makefile b/thread-safe-queue/Makefile index ce0eba3..ed0679c 100644 --- a/thread-safe-queue/Makefile +++ b/thread-safe-queue/Makefile @@ -17,7 +17,7 @@ $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -c $< -o $@ run: $(TARGET) - ./$(TARGET) + ./$(TARGET) $(ARGS) clean: rm -f $(OBJS) $(TARGET) diff --git a/thread-safe-queue/main.cpp b/thread-safe-queue/main.cpp index a135587..25d26ea 100644 --- a/thread-safe-queue/main.cpp +++ b/thread-safe-queue/main.cpp @@ -5,19 +5,23 @@ #include #include -template -class ThreadSafeQueue { -public: +template class ThreadSafeQueue +{ + public: ThreadSafeQueue() = default; ~ThreadSafeQueue() = default; - void enqueue(T item) { + void + enqueue(T item) + { std::lock_guard lock(mutex_); queue_.push(std::move(item)); cond_var_.notify_one(); } - T dequeue() { + T + dequeue() + { std::unique_lock lock(mutex_); cond_var_.wait(lock, [this]() { return !queue_.empty(); }); T item = queue_.front(); @@ -25,23 +29,29 @@ class ThreadSafeQueue { return item; } - bool isEmpty() const { + bool + isEmpty() const + { std::lock_guard lock(mutex_); return queue_.empty(); } - size_t size() const { + size_t + size() const + { std::lock_guard lock(mutex_); return queue_.size(); } -private: + private: mutable std::mutex mutex_; std::condition_variable cond_var_; std::queue queue_; }; -int main() { +int +main() +{ ThreadSafeQueue tsQueue; // Enqueue some items diff --git a/unique-ptr-basics/Makefile b/unique-ptr-basics/Makefile index c461b52..1b5bad8 100644 --- a/unique-ptr-basics/Makefile +++ b/unique-ptr-basics/Makefile @@ -14,7 +14,7 @@ $(TARGET): $(OBJS) $(CXX) $(CXXFLAGS) -c $< -o $@ run: $(TARGET) - ./$(TARGET) + ./$(TARGET) $(ARGS) clean: rm -f $(OBJS) $(TARGET) diff --git a/unique-ptr-basics/main.cpp b/unique-ptr-basics/main.cpp index 8af0197..db2e851 100644 --- a/unique-ptr-basics/main.cpp +++ b/unique-ptr-basics/main.cpp @@ -2,43 +2,50 @@ #include #include -class Resource { -public: - explicit Resource(int id) : id_(id) { - std::cout << "Resource " << id_ << " acquired\n"; - } +class Resource +{ + public: + explicit Resource(int id) : id_(id) { std::cout << "Resource " << id_ << " acquired\n"; } - ~Resource() { - std::cout << "Resource " << id_ << " released\n"; - } + ~Resource() { std::cout << "Resource " << id_ << " released\n"; } - void use() const { + void + use() const + { std::cout << "Using resource " << id_ << "\n"; } -private: + private: int id_; }; // Factory function: returns ownership -std::unique_ptr make_resource(int id) { +std::unique_ptr +make_resource(int id) +{ return std::make_unique(id); } // Takes ownership explicitly -void consume_resource(std::unique_ptr res) { +void +consume_resource(std::unique_ptr res) +{ std::cout << "Consuming resource\n"; res->use(); // res is destroyed here } // Borrows resource (no ownership transfer) -void inspect_resource(const Resource& res) { +void +inspect_resource(const Resource& res) +{ std::cout << "Inspecting resource\n"; res.use(); } -int main() { +int +main() +{ std::cout << "=== create resource ===\n"; auto r1 = make_resource(1); diff --git a/views-zip-enumerate/Makefile b/views-zip-enumerate/Makefile new file mode 100644 index 0000000..e1b7434 --- /dev/null +++ b/views-zip-enumerate/Makefile @@ -0,0 +1,25 @@ +# pull in shared compiler settings +include ../common.mk + +# per-example flags +CXXFLAGS += -pthread + +TARGET := views-zip-enumerate +SRCS := main.cpp +OBJS := $(SRCS:.cpp=.o) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $^ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +run: $(TARGET) + ./$(TARGET) $(ARGS) + +clean: + rm -f $(OBJS) $(TARGET) + +.PHONY: all clean run diff --git a/views-zip-enumerate/main.cpp b/views-zip-enumerate/main.cpp new file mode 100644 index 0000000..f74dc38 --- /dev/null +++ b/views-zip-enumerate/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +void +enumerate(std::string_view str) +{ + for (const auto& [index, character] : std::views::zip(std::views::iota(0), str)) { + std::cout << index << ": " << character << '\n'; + } +} + +int +main(int argc, char* argv[]) +{ + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " ...\n"; + enumerate("default string"); + // make pipeline happy + return 0; + } + for (int i = 1; i < argc; ++i) { + std::string_view input = argv[i]; + std::cout << "Enumerating string: " << input << '\n'; + enumerate(input); + std::cout << "------------------------\n"; + } + return 0; +}