diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..c02fdd9 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,9 @@ +set unwind-on-signal on +set pagination off +python +import sys +import os +sys.path.insert(0, os.path.join(os.getcwd(), 'debug')) +import gdbinit +print("GDB pretty-printer for original initialized.") +end \ No newline at end of file diff --git a/.github/workflows/basic-tests.yml b/.github/workflows/basic-tests.yml index cd7de7d..074599e 100644 --- a/.github/workflows/basic-tests.yml +++ b/.github/workflows/basic-tests.yml @@ -3,7 +3,7 @@ name: Test Branch CI - Basic Tests on: - push: + pull_request: branches: [ test ] jobs: diff --git a/.github/workflows/deploy-docs-latest.yml b/.github/workflows/deploy-docs-latest.yml index fbf347a..a136c01 100644 --- a/.github/workflows/deploy-docs-latest.yml +++ b/.github/workflows/deploy-docs-latest.yml @@ -8,6 +8,7 @@ on: jobs: build-and-deploy: + if: github.repository == 'FrozenLemonTee/original' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/deploy-docs-stable.yml b/.github/workflows/deploy-docs-stable.yml index 7c8471f..1b88f99 100644 --- a/.github/workflows/deploy-docs-stable.yml +++ b/.github/workflows/deploy-docs-stable.yml @@ -5,9 +5,12 @@ name: Deploy Documentation - Stable on: push: branches: [ master ] + tags: + - 'v*.*.*' jobs: build-and-deploy: + if: github.repository == 'FrozenLemonTee/original' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/deploy-docs-tags.yml b/.github/workflows/deploy-docs-tags.yml index c56feac..2666f9c 100644 --- a/.github/workflows/deploy-docs-tags.yml +++ b/.github/workflows/deploy-docs-tags.yml @@ -9,6 +9,7 @@ on: jobs: build-and-deploy: + if: github.repository == 'FrozenLemonTee/original' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/full-tests.yml b/.github/workflows/full-tests.yml index 79ca7db..9257f5a 100644 --- a/.github/workflows/full-tests.yml +++ b/.github/workflows/full-tests.yml @@ -56,7 +56,7 @@ jobs: - name: Run tests run: | - cd build && ctest --output-on-failure + cd build && ctest --verbose --output-on-failure windows-tests: runs-on: windows-latest @@ -75,4 +75,32 @@ jobs: - name: Run tests run: | - cd build && ctest -C ${{ matrix.build_type }} --output-on-failure \ No newline at end of file + cd build && ctest --verbose -C ${{ matrix.build_type }} --output-on-failure + + macos-tests: + runs-on: macos-latest + continue-on-error: true + strategy: + matrix: + build_type: [ Debug, Release ] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + brew update + brew install cmake ninja + + - name: Configure and Build + run: | + export CC=clang + export CXX=clang++ + + cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -G Ninja + cmake --build build --config ${{ matrix.build_type }} + + - name: Run tests + run: | + cd build && ctest --verbose --output-on-failure \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5b69209..c90ac67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ -cmake-build-debug/ -.idea/ -test.exe -original.zip -original.rar +cmake-build-*/ +**/.idea/ build/ install/ .vs/ +.vscode/ out/ -original/ \ No newline at end of file +original/ +**/__pycache__/ +.cache/ + +test.exe +original.zip +original.rar +src/.DS_Store \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c8993b..cbe1d66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,53 @@ # CMakeLists.txt Main -cmake_minimum_required(VERSION 3.31) +set(CMAKE_VERSION_MINIMUM 3.31) +cmake_minimum_required(VERSION ${CMAKE_VERSION_MINIMUM}) +message(STATUS "Cmake version: ${CMAKE_VERSION} (>= ${CMAKE_VERSION_MINIMUM} ✓)") +if(CMAKE_GENERATOR MATCHES "Visual Studio" OR CMAKE_GENERATOR MATCHES "Ninja Multi-Config") + set(CMAKE_GENERATOR_PLATFORM x64 CACHE STRING "Platform" FORCE) +endif() project(original LANGUAGES CXX) -set(ORIGINAL_VERSION 0.1.5) +set(ORIGINAL_VERSION 0.1.6) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(Threads REQUIRED) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(COMPILER_GCC_VERSION_MINIMUM 13.0) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${COMPILER_GCC_VERSION_MINIMUM}) + message(FATAL_ERROR "GCC version too old. Minimum required: ${COMPILER_GCC_VERSION_MINIMUM}. Current: ${CMAKE_CXX_COMPILER_VERSION}") + else() + message(STATUS "GCC version: ${CMAKE_CXX_COMPILER_VERSION} (>= ${COMPILER_GCC_VERSION_MINIMUM} ✓)") + endif() +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(COMPILER_CLANG_VERSION_MINIMUM 20.0) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${COMPILER_CLANG_VERSION_MINIMUM}) + message(FATAL_ERROR "Clang version too old. Minimum required: ${COMPILER_CLANG_VERSION_MINIMUM}. Current: ${CMAKE_CXX_COMPILER_VERSION}") + else() + message(STATUS "Clang version: ${CMAKE_CXX_COMPILER_VERSION} (>= ${COMPILER_CLANG_VERSION_MINIMUM} ✓)") + endif() +endif() +if(MSVC) + set(COMPILER_MSVC_VERSION_MINIMUM 1944) + if(MSVC_VERSION LESS ${COMPILER_MSVC_VERSION_MINIMUM}) + message(FATAL_ERROR "MSVC version too old. Minimum required: ${COMPILER_MSVC_VERSION_MINIMUM}. Current: ${MSVC_VERSION}") + else() + message(STATUS "MSVC version: ${MSVC_VERSION} (>= ${COMPILER_MSVC_VERSION_MINIMUM} ✓)") + endif() + set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION "10.0" CACHE STRING "Windows Target Platform Version") +endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-std=c++23" HAS_CXX23_FLAG) + if(NOT HAS_CXX23_FLAG) + message(WARNING "Compiler may not fully support C++23 standard") + endif() +endif() + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (default Release)" FORCE) endif() @@ -20,6 +63,7 @@ file(GLOB ORIGINAL_HEADERS "${ORIGINAL_SRC_DIR}/vibrant/*.h" ) add_library(original STATIC ${ORIGINAL_HEADERS} src/original.cpp) +target_link_libraries(original PUBLIC Threads::Threads) target_include_directories(original PUBLIC $ @@ -69,24 +113,12 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/original ) +add_subdirectory(debug) + option(BUILD_TESTING "Build the testing directories" ON) if (BUILD_TESTING) include(CTest) - if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi") - else() - if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -g") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") - endif() - endif() - - # test cases - add_subdirectory(test/other) - add_subdirectory(test/unit_test) + enable_testing() + add_subdirectory(test) endif () \ No newline at end of file diff --git a/README.md b/README.md index 7cda3b9..2ccc02d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,19 @@ Original是一个C++基础工具库,也是本人的第一个正式项目,用 ## 文档 [文档-Original](https://documents-original.vercel.app/) +## 环境要求 + +为了编译和使用 Original,请确保开发环境满足以下最低版本要求: + +- **C++ 标准**: C++23 +- **CMake**: 3.31 或更高版本 +- **编译器**(任选其一): + - **GCC**: 13.0 或更高版本 + - **Clang**: 20.0 或更高版本 + - **MSVC**(Visual Studio 2022):17.10 (版本 14.44.35207) 或更高版本 + +> 注意:如果使用 GCC 或 Clang,请确保支持 `-std=c++23` 标志。 + ## 安装 这里以项目`hello_original`为例: @@ -21,7 +34,7 @@ Original是一个C++基础工具库,也是本人的第一个正式项目,用 配置`CMakeLists.txt`: ```cmake -cmake_minimum_required(VERSION 3.30) +cmake_minimum_required(VERSION 3.31) project(hello_original) set(CMAKE_CXX_STANDARD 23) @@ -60,7 +73,7 @@ cmake -P install.cmake 配置`CMakeLists.txt`: ```cmake -cmake_minimum_required(VERSION 3.30) +cmake_minimum_required(VERSION 3.31) project(hello_original) set(CMAKE_CXX_STANDARD 23) @@ -103,7 +116,7 @@ array("hello world!") ##### 容器接口: -格式化输出接口 printable,元素比较接口 comparable,堆对象深拷贝接口 cloneable,可迭代接口 iterable +格式化输出接口 printable,元素比较接口 comparable,堆对象深拷贝接口 cloneable,可迭代接口 iterable,数组视图 arrayView ##### 算法库: @@ -166,6 +179,10 @@ array("hello world!") 任务包装类 taskBase/task,任务委派器 taskDelegator +##### 协程: + +生成器 coroutine::generator + #### matrix 计划实现,包含张量,线性代数工具功能。 diff --git a/debug/CMakeLists.txt b/debug/CMakeLists.txt new file mode 100644 index 0000000..2843c8d --- /dev/null +++ b/debug/CMakeLists.txt @@ -0,0 +1,42 @@ +# debug/CMakeLists.txt + +if(CMAKE_BUILD_TYPE MATCHES "Debug") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "MinGW") + message(STATUS "[GDB CONFIG] Detected GNU/MinGW toolchain, setting up GDB auto-load permissions") + + if(WIN32) + file(TO_CMAKE_PATH "$ENV{USERPROFILE}" USER_HOME) + else() + file(TO_CMAKE_PATH "$ENV{HOME}" USER_HOME) + endif() + + set(GDB_GLOBAL_INIT "${USER_HOME}/.config/gdb/gdbinit") + if(NOT EXISTS "${GDB_GLOBAL_INIT}") + set(GDB_GLOBAL_INIT "${USER_HOME}/.gdbinit") + endif() + + set(PROJECT_GDBINIT "${CMAKE_SOURCE_DIR}/.gdbinit") + + set(GDB_CONFIG_LINES + "set auto-load local-gdbinit on +add-auto-load-safe-path ${PROJECT_GDBINIT}\n" + ) + + get_filename_component(GDB_CONFIG_DIR "${GDB_GLOBAL_INIT}" DIRECTORY) + file(MAKE_DIRECTORY "${GDB_CONFIG_DIR}") + + if(EXISTS "${GDB_GLOBAL_INIT}") + file(READ "${GDB_GLOBAL_INIT}" EXISTING_CONTENT) + string(FIND "${EXISTING_CONTENT}" "${PROJECT_GDBINIT}" ALREADY_ADDED) + else() + set(ALREADY_ADDED -1) + endif() + + if(ALREADY_ADDED EQUAL -1) + file(APPEND "${GDB_GLOBAL_INIT}" "\n# Added by Original CMake auto-config\n${GDB_CONFIG_LINES}") + message(STATUS "[GDB CONFIG] Added safe-path for ${PROJECT_GDBINIT} in ${GDB_GLOBAL_INIT}") + else() + message(STATUS "[GDB CONFIG] Safe-path already configured in ${GDB_GLOBAL_INIT}") + endif() + endif() +endif() diff --git a/debug/gdbinit.py b/debug/gdbinit.py new file mode 100644 index 0000000..c9a86d9 --- /dev/null +++ b/debug/gdbinit.py @@ -0,0 +1,58 @@ +# debug/gdbinit.py + +import gdb +import sys +import os +import importlib.util +import inspect + + +def load_printers_from_directory(pretty_printer_collection, directory): + """Load all pretty-printer scripts from the specified directory.""" + if not os.path.isdir(directory): + print(f"[WARN] Skipped non-existent directory: {directory}") + return + + for file_name in os.listdir(directory): + if not file_name.endswith("_printer.py"): + continue + + file_path = os.path.join(directory, file_name) + module_name = os.path.splitext(file_name)[0] + + try: + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + printer_class = None + for name, cls in inspect.getmembers(module, inspect.isclass): + if name == "Printer": + printer_class = cls + break + + if printer_class is None: + print(f"[SKIP] {module_name}: no 'Printer' class found") + continue + type_name = module_name.replace("_printer", "") + type_regex = f"^original::{type_name}(<.*>)?$" + + pretty_printer_collection.add_printer(module_name, type_regex, printer_class) + print(f"Registered pretty-printer: {type_name}") + + except Exception as e: + print(f"Failed to load {file_name}: {e}") + + +this_dir = os.path.dirname(__file__) +printers_dir = os.path.join(this_dir, "printers") +core_dir = os.path.join(printers_dir, "core") + +sys.path.insert(0, printers_dir) +sys.path.insert(0, core_dir) + +pp = gdb.printing.RegexpCollectionPrettyPrinter("original") +load_printers_from_directory(pp, core_dir) +gdb.printing.register_pretty_printer(None, pp) + +print("GDB pretty-printers for 'original' registered successfully.") \ No newline at end of file diff --git a/debug/printers/core/JMap_printer.py b/debug/printers/core/JMap_printer.py new file mode 100644 index 0000000..03ff4c6 --- /dev/null +++ b/debug/printers/core/JMap_printer.py @@ -0,0 +1,27 @@ +# debug/printers/core/JMap_printer.py + +import gdb +from skipList_printer import PrinterBase as Base +from utils import addr_str + + +class Printer(Base): + """Pretty printer for original::JMap""" + + def __init__(self, val): + super().__init__(val) + self.display_mode = "map" + + def class_name(self): + return "original::JMap" + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}].key", cp[0] + yield f"[{i}].value", cp[1] + if gdb.parameter('print pretty') > 0: + yield "head", addr_str(self.val["head_"]) + yield "compare", self.val["compare_"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/JSet_printer.py b/debug/printers/core/JSet_printer.py new file mode 100644 index 0000000..e8b2e65 --- /dev/null +++ b/debug/printers/core/JSet_printer.py @@ -0,0 +1,26 @@ +# debug/printers/core/JSet_printer.py + +import gdb +from skipList_printer import PrinterBase as Base +from utils import addr_str + + +class Printer(Base): + """Pretty printer for original::JSet""" + + def __init__(self, val): + super().__init__(val) + self.display_mode = "array" + + def class_name(self): + return "original::JSet" + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}]", cp[0] + if gdb.parameter('print pretty') > 0: + yield "head", addr_str(self.val["head_"]) + yield "compare", self.val["compare_"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/RBTree_printer.py b/debug/printers/core/RBTree_printer.py new file mode 100644 index 0000000..8db4a04 --- /dev/null +++ b/debug/printers/core/RBTree_printer.py @@ -0,0 +1,79 @@ +# debug/printers/core/RBTree_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +def get_successor_node(ptr): + if ptr == 0: + return 0 + node = ptr.dereference() + if node["right_"] != 0: + ptr = node["right_"] + node = ptr.dereference() + while node["left_"] != 0: + ptr = node["left_"] + node = ptr.dereference() + return ptr + + node = ptr.dereference() + ptr_parent = node["parent_"] + node = ptr_parent.dereference() + + while ptr_parent != 0 and ptr == node["right_"]: + ptr = ptr_parent + node = ptr_parent.dereference() + ptr_parent = node["parent_"] + return ptr_parent + + +class PrinterBase: + """Pretty printer for derived classes of RBTree in original""" + + def __init__(self, val): + self.val = val + + def class_name(self): + return "original::RBTree" + + def to_string(self): + size = int(call(self.val, "size")) + return f"{self.class_name()}(size={size}, {addr_str(self.val)})" + + def get_min_node(self): + p = self.val["root_"] + if p == 0: + return p + node = p.dereference() + while node["left_"] != 0: + p = node["left_"] + node = p.dereference() + return p + + def get_max_node(self): + p = self.val["root_"] + if p == 0: + return p + node = p.dereference() + while node["right_"] != 0: + p = node["right_"] + node = p.dereference() + return p + + def elem(self): + if self.val["root_"] == 0: + return + cur = self.get_min_node() + end = self.get_max_node() + while cur != end: + node = cur.dereference() + cp = node["data_"] + yield cp["first_"], cp["second_"] + cur = get_successor_node(cur) + if end != 0: + node = end.dereference() + cp = node["data_"] + yield cp["first_"], cp["second_"] diff --git a/debug/printers/core/alternative_printer.py b/debug/printers/core/alternative_printer.py new file mode 100644 index 0000000..8861361 --- /dev/null +++ b/debug/printers/core/alternative_printer.py @@ -0,0 +1,23 @@ +# debug/printers/core/alternative_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::alternative""" + + def __init__(self, val): + self.val = val + + def to_string(self): + has_value = bool(call(self.val, "operator bool")) + return f"original::alternative(notnull={has_value}, {addr_str(self.val)})" + + def children(self): + has_value = bool(call(self.val, "operator bool")) + if has_value: + yield "value", self.val["val_"]["type_"] diff --git a/debug/printers/core/array_printer.py b/debug/printers/core/array_printer.py new file mode 100644 index 0000000..ca777d1 --- /dev/null +++ b/debug/printers/core/array_printer.py @@ -0,0 +1,34 @@ +# debug/printers/core/array_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::array""" + + def __init__(self, val): + self.val = val + self.display_mode = "array" + + def to_string(self): + size = int(call(self.val, "size")) + return f"original::array(cap={size}, {addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + for i in range(0, size): + v = call(self.val, "operator[]", f"{i}") + if v.type.code == gdb.TYPE_CODE_REF: + v = v.referenced_value() + yield f"[{i}]", v + if gdb.parameter('print pretty') > 0: + addr = addr_str(self.val["body"]) + yield "body", f"{addr}" + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/autoPtr_printer.py b/debug/printers/core/autoPtr_printer.py new file mode 100644 index 0000000..56edaf0 --- /dev/null +++ b/debug/printers/core/autoPtr_printer.py @@ -0,0 +1,60 @@ +# debug/printers/core/autoPtr_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +def is_array_ptr(val): + type_str = str(val.type) + + if "<" not in type_str or ">" not in type_str: + return False + + inner = type_str[type_str.find("<")+1:type_str.rfind(">")] + parts = [p.strip() for p in inner.split(",")] + if len(parts) < 2: + return False + deleter_param = parts[1] + return deleter_param.endswith("[]>") + + +class PrinterBase: + """Pretty printer for derived classes of autoPtr in original""" + + def __init__(self, val): + self.val = val + self.name = "original::autoPtr" + + def class_name(self): + return self.name + + def to_string(self): + ptr = int(call(self.val, "get")) + if ptr == 0: + return f"{self.class_name()}(nullptr)" + ref_count = self.val['ref_count'] + loaded_ptr = call(ref_count, "operator*") + ref_count_base = loaded_ptr.dereference() + strong_refs_atomic = ref_count_base['strong_refs'] + weak_refs_atomic = ref_count_base['weak_refs'] + strong_refs = call(strong_refs_atomic, "operator*") + weak_refs = call(weak_refs_atomic, "operator*") + strong_refs = to_int(strong_refs, U_INTEGER_SIZE) + weak_refs = to_int(weak_refs, U_INTEGER_SIZE) + return f"{self.class_name()}(strong refs={strong_refs}, weak refs={weak_refs}, {addr_str(ptr)})" + + def children(self): + ptr = call(self.val, "get") + ptr_val = int(ptr) + + if ptr_val != 0: + if is_array_ptr(self.val): + yield "array", f"@{ptr_val:#x}" + else: + yield "object", ptr.dereference() + if gdb.parameter('print pretty') > 0: + yield "ref count", self.val["ref_count"] + yield "alias ptr", self.val["alias_ptr"] \ No newline at end of file diff --git a/debug/printers/core/bitSet_printer.py b/debug/printers/core/bitSet_printer.py new file mode 100644 index 0000000..3f6a19e --- /dev/null +++ b/debug/printers/core/bitSet_printer.py @@ -0,0 +1,31 @@ +# debug/printers/core/bitSet_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::bitset""" + + def __init__(self, val): + self.val = val + self.display_mode = "array" + + def to_string(self): + size = int(call(self.val, "size")) + cnt = int(call(self.val, "count")) + return f"original::bitset(count={cnt}, cap={size}, {addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + for i in range(0, size): + yield f"[{i}]", call(self.val, "get", f"{i}") + if gdb.parameter('print pretty') > 0: + yield "map", self.val["map"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/blocksList_printer.py b/debug/printers/core/blocksList_printer.py new file mode 100644 index 0000000..87f771e --- /dev/null +++ b/debug/printers/core/blocksList_printer.py @@ -0,0 +1,37 @@ +# debug/printers/core/blocksList_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::blocksList""" + + def __init__(self, val): + self.val = val + self.display_mode = "array" + + def to_string(self): + size = int(call(self.val, "size")) + capacity = int(call(self.val["map"], "size")) * 16 + return f"original::blocksList(size={size}, cap={capacity}, {addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + for i in range(0, size): + v = call(self.val, "operator[]", f"{i}") + if v.type.code == gdb.TYPE_CODE_REF: + v = v.referenced_value() + yield f"[{i}]", v + if gdb.parameter('print pretty') > 0: + yield "first", self.val["first_"] + yield "last", self.val["last_"] + yield "first block", self.val["first_block"] + yield "last block", self.val["last_block"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/chain_printer.py b/debug/printers/core/chain_printer.py new file mode 100644 index 0000000..4b3eed3 --- /dev/null +++ b/debug/printers/core/chain_printer.py @@ -0,0 +1,30 @@ +# debug/printers/core/chain_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::chain""" + + def __init__(self, val): + self.val = val + + def to_string(self): + size = int(call(self.val, "size")) + return f"original::chain(size={size}, {addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + p = self.val["begin_"] + for i in range(0, size): + node = p.dereference() + yield f"[{i}]", node["data_"] + p = node["next"] + if gdb.parameter('print pretty') > 0: + yield "begin", self.val["begin_"] + yield "end", self.val["end_"] \ No newline at end of file diff --git a/debug/printers/core/containerAdaptor_printer.py b/debug/printers/core/containerAdaptor_printer.py new file mode 100644 index 0000000..63bbcfd --- /dev/null +++ b/debug/printers/core/containerAdaptor_printer.py @@ -0,0 +1,23 @@ +# debug/printers/core/containerAdaptor_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class PrinterBase: + """Pretty printer for derived classes of containerAdaptor in original""" + + def __init__(self, val): + self.val = val + + def class_name(self): + return "original::containerAdaptor" + + def to_string(self): + return f"{self.class_name()}({addr_str(self.val)})" + + def children(self): + yield "serial", self.val["serial_"] \ No newline at end of file diff --git a/debug/printers/core/couple_printer.py b/debug/printers/core/couple_printer.py new file mode 100644 index 0000000..fe7a922 --- /dev/null +++ b/debug/printers/core/couple_printer.py @@ -0,0 +1,25 @@ +# debug/printers/core/couple_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::couple""" + + def __init__(self, val): + self.val = val + self.display_mode = "array" + + def to_string(self): + return f"original::couple({addr_str(self.val)})" + + def children(self): + yield "[0]", self.val['first_'] + yield "[1]", self.val['second_'] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/deque_printerr.py b/debug/printers/core/deque_printerr.py new file mode 100644 index 0000000..9dc04dd --- /dev/null +++ b/debug/printers/core/deque_printerr.py @@ -0,0 +1,14 @@ +# debug/printers/core/deque_printer.py + +import gdb +from containerAdaptor_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::deque""" + + def __init__(self, val): + super().__init__(val) + + def class_name(self): + return "original::deque" \ No newline at end of file diff --git a/debug/printers/core/forwardChain_printer.py b/debug/printers/core/forwardChain_printer.py new file mode 100644 index 0000000..c428632 --- /dev/null +++ b/debug/printers/core/forwardChain_printer.py @@ -0,0 +1,28 @@ +# debug/printers/core/forwardChain_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::forwardChain""" + + def __init__(self, val): + self.val = val + + def to_string(self): + return f"original::forwardChain({addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + yield "size", size + p = self.val["begin_"] + for i in range(0, size): + node = p.dereference() + yield f"[{i}]", node["data_"] + p = node["next"] + yield "begin", self.val["begin_"] \ No newline at end of file diff --git a/debug/printers/core/hashMap_printer.py b/debug/printers/core/hashMap_printer.py new file mode 100644 index 0000000..0100047 --- /dev/null +++ b/debug/printers/core/hashMap_printer.py @@ -0,0 +1,25 @@ +# debug/printers/core/hashMap_printer.py + +import gdb +from hashTable_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::hashMap""" + + def __init__(self, val): + super().__init__(val) + self.display_mode = "map" + + def class_name(self): + return "original::hashMap" + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}].key", cp[0] + yield f"[{i}].value", cp[1] + if gdb.parameter('print pretty') > 0: + yield "buckets", self.val["buckets"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/hashSet_printer.py b/debug/printers/core/hashSet_printer.py new file mode 100644 index 0000000..39a4e54 --- /dev/null +++ b/debug/printers/core/hashSet_printer.py @@ -0,0 +1,24 @@ +# debug/printers/core/hashSet_printer.py + +import gdb +from hashTable_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::hashSet""" + + def __init__(self, val): + super().__init__(val) + self.display_mode = "array" + + def class_name(self): + return "original::hashSet" + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}]", cp[0] + if gdb.parameter('print pretty') > 0: + yield "buckets", self.val["buckets"] + + def display_hint(self): + return self.display_mode diff --git a/debug/printers/core/hashTable_printer.py b/debug/printers/core/hashTable_printer.py new file mode 100644 index 0000000..fb8958c --- /dev/null +++ b/debug/printers/core/hashTable_printer.py @@ -0,0 +1,39 @@ +# debug/printers/core/hashTable_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class PrinterBase: + """Pretty printer for derived classes of hashTable in original""" + + def __init__(self, val): + self.val = val + + def class_name(self): + return "original::hashTable" + + def to_string(self): + size = int(call(self.val, "size")) + return f"{self.class_name()}(size={size}, {addr_str(self.val)})" + + def elem(self): + size = int(call(self.val, "size")) + if size == 0: + return + cnt = 0 + buckets = self.val['buckets'] + length = int(call(buckets, "size")) + for i in range(length): + p = call(buckets, "operator[]", f"{i}") + while p != 0: + node = p.dereference() + cp = node["data_"] + yield cp["first_"], cp["second_"] + cnt += 1 + if cnt == size: + return + p = node["next_"] diff --git a/debug/printers/core/ownerPtr_printer.py b/debug/printers/core/ownerPtr_printer.py new file mode 100644 index 0000000..49d82f8 --- /dev/null +++ b/debug/printers/core/ownerPtr_printer.py @@ -0,0 +1,15 @@ +# debug/printers/core/ownerPtr_printer.py + +import gdb + +from autoPtr_printer import PrinterBase as Base +class Printer(Base): + """Pretty printer for original::ownerPtr""" + + + def __init__(self, val): + super().__init__(val) + + + def class_name(self): + return "original::ownerPtr" \ No newline at end of file diff --git a/debug/printers/core/prique_printer.py b/debug/printers/core/prique_printer.py new file mode 100644 index 0000000..4b95bf6 --- /dev/null +++ b/debug/printers/core/prique_printer.py @@ -0,0 +1,20 @@ +# debug/printers/core/prique_printer.py + +import gdb +from containerAdaptor_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::prique""" + + def __init__(self, val): + super().__init__(val) + + def class_name(self): + return "original::prique" + + def children(self): + for _, e in enumerate(super().children()): + yield e + if gdb.parameter('print pretty') > 0: + yield "compare", self.val["compare_"] \ No newline at end of file diff --git a/debug/printers/core/queue_printer.py b/debug/printers/core/queue_printer.py new file mode 100644 index 0000000..f9a6e45 --- /dev/null +++ b/debug/printers/core/queue_printer.py @@ -0,0 +1,14 @@ +# debug/printers/core/queue_printer.py + +import gdb +from containerAdaptor_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::queue""" + + def __init__(self, val): + super().__init__(val) + + def class_name(self): + return "original::queue" \ No newline at end of file diff --git a/debug/printers/core/skipList_printer.py b/debug/printers/core/skipList_printer.py new file mode 100644 index 0000000..9bf9da4 --- /dev/null +++ b/debug/printers/core/skipList_printer.py @@ -0,0 +1,39 @@ +# debug/printers/core/skipList_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class PrinterBase: + """Pretty printer for derived classes of skipList in original""" + + def __init__(self, val): + self.val = val + self.name = "original::skipList" + + def class_name(self): + return self.name + + def to_string(self): + size = int(call(self.val, "size")) + levels = int(call(self.val, "getCurLevels")) + return f"{self.class_name()}(size={size}, levels={levels}, {addr_str(self.val)})" + + def elem(self): + size = int(call(self.val, "size")) + if size == 0: + return + cur = self.val["head_"] + first = True + while cur != 0: + node = cur.dereference() + cp = node["data_"] + if first: + first = False + else: + yield cp["first_"], cp["second_"] + next_lst = node["next_"] + cur = call(next_lst, "get", "0") diff --git a/debug/printers/core/stack_printer.py b/debug/printers/core/stack_printer.py new file mode 100644 index 0000000..7fcba7d --- /dev/null +++ b/debug/printers/core/stack_printer.py @@ -0,0 +1,14 @@ +# debug/printers/core/stack_printer.py + +import gdb +from containerAdaptor_printer import PrinterBase as Base + + +class Printer(Base): + """Pretty printer for original::stack""" + + def __init__(self, val): + super().__init__(val) + + def class_name(self): + return "original::stack" \ No newline at end of file diff --git a/debug/printers/core/strongPtr_printer.py b/debug/printers/core/strongPtr_printer.py new file mode 100644 index 0000000..629c30b --- /dev/null +++ b/debug/printers/core/strongPtr_printer.py @@ -0,0 +1,15 @@ +# debug/printers/core/strongPtr_printer.py + +import gdb + +from autoPtr_printer import PrinterBase as Base +class Printer(Base): + """Pretty printer for original::strongPtr""" + + + def __init__(self, val): + super().__init__(val) + + + def class_name(self): + return "original::strongPtr" \ No newline at end of file diff --git a/debug/printers/core/treeMap_printer.py b/debug/printers/core/treeMap_printer.py new file mode 100644 index 0000000..83594fe --- /dev/null +++ b/debug/printers/core/treeMap_printer.py @@ -0,0 +1,33 @@ +# debug/printers/core/treeMap_printer.py + +import gdb +from RBTree_printer import PrinterBase as Base +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer(Base): + """Pretty printer for original::treeMap""" + + def __init__(self, val): + super().__init__(val) + self.name = "original::treeMap" + self.display_mode = "map" + + def class_name(self): + return self.name + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}].key", cp[0] + yield f"[{i}].value", cp[1] + if gdb.parameter('print pretty') > 0: + addr_root = addr_str(self.val["root_"]) + addr_cmp = addr_str(self.val["compare_"]) + yield "root", f"{addr_root}" + yield "compare", f"{addr_cmp}" + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/treeSet_printer.py b/debug/printers/core/treeSet_printer.py new file mode 100644 index 0000000..9ad5c22 --- /dev/null +++ b/debug/printers/core/treeSet_printer.py @@ -0,0 +1,32 @@ +# debug/printers/core/treeSet_printer.py + +import gdb +from RBTree_printer import PrinterBase as Base +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer(Base): + """Pretty printer for original::treeSet""" + + def __init__(self, val): + super().__init__(val) + self.name = "original::treeSet" + self.display_mode = "array" + + def class_name(self): + return self.name + + def children(self): + for i, cp in enumerate(self.elem()): + yield f"[{i}]", cp[0] + if gdb.parameter('print pretty') > 0: + addr_root = addr_str(self.val["root_"]) + addr_cmp = addr_str(self.val["compare_"]) + yield "root", f"{addr_root}" + yield "compare", f"{addr_cmp}" + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/tuple_printer.py b/debug/printers/core/tuple_printer.py new file mode 100644 index 0000000..1696d4f --- /dev/null +++ b/debug/printers/core/tuple_printer.py @@ -0,0 +1,47 @@ +# debug/printers/core/tuple_printer.py + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::tuple""" + + def __init__(self, val): + self.val = val + self.size = 0 + self.display_mode = "array" + try: + elems = val['elems'] + current = elems + while True: + try: + _ = current['cur_elem'] + self.size += 1 + current = current['next'] + except gdb.error: + break + except gdb.error: + self.size = 0 + + def to_string(self): + return f"original::tuple(size={self.size}, {addr_str(self.val)})" + + def children(self): + elems = self.val['elems'] + current = elems + index = 0 + while True: + try: + elem = current['cur_elem'] + yield f"[{index}]", elem + index += 1 + current = current['next'] + except gdb.error: + break + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/vector_printer.py b/debug/printers/core/vector_printer.py new file mode 100644 index 0000000..25e082d --- /dev/null +++ b/debug/printers/core/vector_printer.py @@ -0,0 +1,35 @@ +# debug/printers/core/vector_printer.py + + +import gdb +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +from utils import * + + +class Printer: + """Pretty printer for original::vector""" + + def __init__(self, val): + self.val = val + self.display_mode = "array" + + def to_string(self): + size = int(call(self.val, "size")) + capacity = self.val["max_size"] + return f"original::vector(size={size}, cap={capacity}, {addr_str(self.val)})" + + def children(self): + size = int(call(self.val, "size")) + for i in range(0, size): + v = call(self.val, "operator[]", f"{i}") + if v.type.code == gdb.TYPE_CODE_REF: + v = v.referenced_value() + yield f"[{i}]", v + if gdb.parameter('print pretty') > 0: + yield "max size", self.val["max_size"] + yield "inner begin", self.val["inner_begin"] + + def display_hint(self): + return self.display_mode \ No newline at end of file diff --git a/debug/printers/core/weakPtr_printer.py b/debug/printers/core/weakPtr_printer.py new file mode 100644 index 0000000..2333413 --- /dev/null +++ b/debug/printers/core/weakPtr_printer.py @@ -0,0 +1,15 @@ +# debug/printers/core/weakPtr_printer.py + +import gdb + +from autoPtr_printer import PrinterBase as Base +class Printer(Base): + """Pretty printer for original::weakPtr""" + + + def __init__(self, val): + super().__init__(val) + + + def class_name(self): + return "original::weakPtr" \ No newline at end of file diff --git a/debug/printers/utils.py b/debug/printers/utils.py new file mode 100644 index 0000000..0a9fd31 --- /dev/null +++ b/debug/printers/utils.py @@ -0,0 +1,59 @@ +# debug/printers/utils.py + +import gdb + + +U_INTEGER_SIZE = 4 + +def to_int(val, size = 8): + mask = (1 << (size * 8)) - 1 + v = int(val) + return v & mask + +def address(obj): + try: + return int(obj.address) if obj.address else None + except gdb.error: + return None + +def addr_str(item): + if isinstance(item, int) or isinstance(item, str): + return f"@{item:#x}" + return f"@{address(item):#x}" + +def type_name(obj): + return obj.type.name + +def call(obj, method_name, *args): + """ + Safely invoke a member function on an object using GDB. + + :param obj: gdb.Value - the object instance + :param method_name: str - member function name (e.g., 'size') + :param args: optional arguments to pass to the function + :return: gdb.Value | None - result of the call or None if failed + """ + try: + if obj.type.code == gdb.TYPE_CODE_REF: + obj = obj.referenced_value() + + addr = address(obj) + if addr is None or addr == 0: + return None + + args_str = ', '.join(str(arg) for arg in args) + + expr = f'(({type_name(obj)}*)({addr}))->{method_name}({args_str})' + + for _ in range(3): + try: + return gdb.parse_and_eval(expr) + except gdb.error as e: + msg = str(e) + if "signaled" in msg or "The program being debugged" in msg: + return None + raise + + except Exception as e: + print(f"[GDB:call] Error calling {method_name} on {obj}: {e}") + return None diff --git a/docs/index.md b/docs/index.md index 7420221..ae6d613 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,6 +35,19 @@ Original是一个C++基础工具库,也是本人的第一个正式项目,用 --- +## 环境要求 + +为了编译和使用 Original,请确保开发环境满足以下最低版本要求: + +- **C++ 标准**: C++23 +- **CMake**: 3.31 或更高版本 +- **编译器**(任选其一): + - **GCC**: 13.0 或更高版本 + - **Clang**: 20.0 或更高版本 + - **MSVC**(Visual Studio 2022):17.10 (版本 14.44.35207) 或更高版本 + +> 注意:如果使用 GCC 或 Clang,请确保支持 `-std=c++23` 标志。 + ## 🚀 快速开始 这里以项目`hello_original`为例: @@ -47,7 +60,7 @@ Original是一个C++基础工具库,也是本人的第一个正式项目,用 配置`CMakeLists.txt`: ```cmake -cmake_minimum_required(VERSION 3.30) +cmake_minimum_required(VERSION 3.31) project(hello_original) set(CMAKE_CXX_STANDARD 23) @@ -86,7 +99,7 @@ cmake -P install.cmake 配置`CMakeLists.txt`: ```cmake -cmake_minimum_required(VERSION 3.30) +cmake_minimum_required(VERSION 3.31) project(hello_original) set(CMAKE_CXX_STANDARD 23) @@ -129,7 +142,7 @@ array("hello world!") ##### 容器接口: -格式化输出接口 printable,元素比较接口 comparable,堆对象深拷贝接口 cloneable,可迭代接口 iterable +格式化输出接口 printable,元素比较接口 comparable,堆对象深拷贝接口 cloneable,可迭代接口 iterable,数组视图 arrayView ##### 算法库: @@ -192,6 +205,10 @@ array("hello world!") 任务包装类 taskBase/task,任务委派器 taskDelegator +##### 协程: + +生成器 coroutine::generator + #### matrix 计划实现,包含张量,线性代数工具功能。 diff --git a/src/core/RBTree.h b/src/core/RBTree.h index 4e2d01b..713f737 100644 --- a/src/core/RBTree.h +++ b/src/core/RBTree.h @@ -38,7 +38,7 @@ namespace original { typename ALLOC = allocator, typename Compare = increaseComparator> class RBTree { - protected: + public: /** * @class RBNode @@ -200,10 +200,10 @@ namespace original { static void connect(RBNode* parent, RBNode* child, bool is_left); }; - using color = typename RBNode::color; ///< Color type alias + using color = RBNode::color; ///< Color type alias static constexpr color BLACK = color::BLACK; ///< Black color constant static constexpr color RED = color::RED; ///< Red color constant - using rebind_alloc_node = typename ALLOC::template rebind_alloc; ///< Rebound allocator type + using rebind_alloc_node = ALLOC::template rebind_alloc; ///< Rebound allocator type RBNode* root_; ///< Root node pointer u_integer size_; ///< Number of elements @@ -460,7 +460,7 @@ namespace original { * @brief Destructor * @details Cleans up all tree nodes and allocated memory */ - ~RBTree(); + virtual ~RBTree(); }; } @@ -476,7 +476,7 @@ original::RBTree::RBNode::RBNode(const RBNode &o } template -typename original::RBTree::RBNode& +original::RBTree::RBNode& original::RBTree::RBNode::operator=(const RBNode &other) { if (this == &other) return *this; @@ -539,38 +539,38 @@ void original::RBTree::RBNode::setValue(const V_ } template -typename original::RBTree::RBNode::color +original::RBTree::RBNode::color original::RBTree::RBNode::getColor() const { return this->color_; } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::RBNode::getPParent() const { return this->parent_; } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::RBNode::getPLeft() const { return this->left_; } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::RBNode::getPRight() const { return this->right_; } template -typename original::RBTree::RBNode*& +original::RBTree::RBNode*& original::RBTree::RBNode::getPLeftRef() { return this->left_; } template -typename original::RBTree::RBNode*& +original::RBTree::RBNode*& original::RBTree::RBNode::getPRightRef() { return this->right_; @@ -617,7 +617,7 @@ original::RBTree::Iterator::Iterator(const Itera } template -typename original::RBTree::Iterator& +original::RBTree::Iterator& original::RBTree::Iterator::operator=(const Iterator& other) { if (this == &other) @@ -704,7 +704,7 @@ bool original::RBTree::Iterator::isValid() const template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::treeCopy() const { if (!this->root_) { return nullptr; @@ -740,7 +740,7 @@ original::RBTree::treeCopy() const { } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::getPrecursorNode(RBNode *cur) const { if (!cur) return nullptr; @@ -762,7 +762,7 @@ original::RBTree::getPrecursorNode(RBNode *cur) } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::getSuccessorNode(RBNode *cur) const { if (!cur) return nullptr; @@ -784,7 +784,7 @@ original::RBTree::getSuccessorNode(RBNode *cur) } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::getMinNode() const { if (!root_) return nullptr; @@ -797,7 +797,7 @@ original::RBTree::getMinNode() const } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::getMaxNode() const { if (!this->root_) { @@ -812,7 +812,7 @@ original::RBTree::getMaxNode() const } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::replaceNode(RBNode* src, RBNode* tar) { auto moved_src = this->createNode(std::move(*src)); @@ -829,7 +829,7 @@ original::RBTree::replaceNode(RBNode* src, RBNod } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::createNode(const K_TYPE &key, const V_TYPE &value, color color, RBNode* parent, RBNode* left, RBNode* right) const { @@ -839,7 +839,7 @@ original::RBTree::createNode(const K_TYPE &key, } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::createNode(RBNode&& other_node) const { auto node = this->rebind_alloc.allocate(1); @@ -870,7 +870,7 @@ bool original::RBTree::highPriority(const K_TYPE } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::rotateLeft(RBNode *cur) { RBNode* child_left = cur; RBNode* child_root = child_left->getPRight(); @@ -884,7 +884,7 @@ original::RBTree::rotateLeft(RBNode *cur) { } template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::rotateRight(RBNode *cur) { RBNode* child_right = cur; RBNode* child_root = cur->getPLeft(); @@ -1117,7 +1117,7 @@ original::RBTree::RBTree(Compare compare) : root_(nullptr), size_(0), compare_(std::move(compare)) {} template -typename original::RBTree::RBNode* +original::RBTree::RBNode* original::RBTree::find(const K_TYPE &key) const { auto cur = this->root_; while (cur){ diff --git a/src/core/allocator.h b/src/core/allocator.h index 5d666f7..271c614 100644 --- a/src/core/allocator.h +++ b/src/core/allocator.h @@ -488,15 +488,15 @@ original::objPoolAllocator& original::objPoolAllocator::operator+=(o if (this == &other) return *this; - auto size_class_count_max = max(this->size_class_count, other.size_class_count); - auto chunk_count_init_max = max(this->chunk_count_init, other.chunk_count_init); + auto size_class_count_max = maximum(this->size_class_count, other.size_class_count); + auto chunk_count_init_max = maximum(this->chunk_count_init, other.chunk_count_init); auto chunk_count_max = allocators::malloc(size_class_count_max); auto free_list_head_max = allocators::malloc(size_class_count_max); auto chunks_available_max = allocators::malloc(size_class_count_max); for (u_integer i = 0; i < size_class_count_max; i++) { if (i < this->size_class_count && i < other.size_class_count) { - chunk_count_max[i] = max(this->chunk_count[i], other.chunk_count[i]); + chunk_count_max[i] = maximum(this->chunk_count[i], other.chunk_count[i]); chunks_available_max[i] = this->chunks_available[i] + other.chunks_available[i]; auto cur_list_head_i = other.free_list_head[i]; if (cur_list_head_i) { @@ -597,7 +597,7 @@ TYPE* original::objPoolAllocator::allocate(const u_integer size) } if (!this->free_list_head[index] || this->chunks_available[index] * (static_cast(1) << index) < size) { - this->chunkAllocate(max(1, this->chunk_count[index]), index); + this->chunkAllocate(maximum(1, this->chunk_count[index]), index); } auto cur_ptr = this->free_list_head[index]; diff --git a/src/core/array.h b/src/core/array.h index d30184f..71ee47c 100644 --- a/src/core/array.h +++ b/src/core/array.h @@ -4,6 +4,7 @@ #include #include "allocator.h" +#include "arrayView.h" #include "config.h" #include "baseArray.h" #include "iterationStream.h" @@ -160,6 +161,59 @@ namespace original { */ array(const std::initializer_list& lst); + /** + * @brief Constructs an array from a built-in array (non-const version) + * @tparam N Size of the built-in array + * @param arr Reference to the built-in array + * @param alloc Allocator instance to use for memory management + * @details This constructor creates an array by copying elements from a built-in array. + * The size of the created array will be N, and all elements will be copied from the source array. + */ + template + explicit constexpr array(TYPE (&arr)[N], ALLOC alloc = ALLOC{}) noexcept; + + /** + * @brief Constructs an empty array from a zero-sized built-in array (non-const version) + * @param arr Reference to the zero-sized built-in array + * @param alloc Allocator instance to use for memory management + * @details This constructor creates an empty array from a zero-sized built-in array. + * The resulting array will have size 0. + */ + explicit constexpr array(TYPE (&arr)[0], ALLOC alloc = ALLOC{}) noexcept; + + /** + * @brief Constructs an array from a built-in array (const version) + * @tparam N Size of the built-in array + * @param arr Reference to the const built-in array + * @param alloc Allocator instance to use for memory management + * @details This constructor creates an array by copying elements from a const built-in array. + * The size of the created array will be N, and all elements will be copied from the source array. + * This version allows construction from const arrays. + */ + template + explicit constexpr array(const TYPE (&arr)[N], ALLOC alloc = ALLOC{}) noexcept; + + /** + * @brief Constructs an empty array from a zero-sized built-in array (const version) + * @param arr Reference to the const zero-sized built-in array + * @param alloc Allocator instance to use for memory management + * @details This constructor creates an empty array from a const zero-sized built-in array. + * The resulting array will have size 0. + * This version allows construction from const arrays. + */ + explicit constexpr array(const TYPE (&arr)[0], ALLOC alloc = ALLOC{}) noexcept; + + /** + * @brief Constructs an array from an arrayView + * @param view The arrayView to copy elements from + * @param alloc Allocator instance to use for memory management + * @details This constructor creates an array by copying elements from an arrayView. + * The size of the created array will be equal to the count of the arrayView, + * and all elements will be copied from the view. + * This allows seamless conversion between arrayView and array types. + */ + explicit array(arrayView view, ALLOC alloc = ALLOC{}); + /** * @brief Copy constructor. * @param other The array to copy from. @@ -216,6 +270,36 @@ namespace original { */ TYPE& data() const; + /** + * @brief Returns a view over the entire array + * @return arrayView providing access to the array elements + */ + arrayView view(); + + /** + * @brief Returns a slice view of the array + * @param start Starting index of the slice (inclusive) + * @param end Ending index of the slice (exclusive) + * @return arrayView covering the specified range + * @throws outOfBoundError if the range is invalid + */ + arrayView slice(u_integer start, u_integer end); + + /** + * @brief Returns a const view over the entire array + * @return const arrayView providing read-only access to the array elements + */ + arrayView view() const; + + /** + * @brief Returns a const slice view of the array + * @param start Starting index of the slice (inclusive) + * @param end Ending index of the slice (exclusive) + * @return const arrayView covering the specified range + * @throws outOfBoundError if the range is invalid + */ + arrayView slice(u_integer start, u_integer end) const; + /** * @brief Retrieves an element at a specified index. * @param index The index of the element to retrieve. @@ -399,6 +483,41 @@ namespace std { } } + template + template + constexpr original::array::array(TYPE(& arr)[N], ALLOC alloc) noexcept : array(N, std::move(alloc)) + { + for (u_integer i = 0; i < N; ++i) + { + this->setElem(i, arr[i]); + } + } + + template + constexpr original::array::array(TYPE(&)[0], ALLOC alloc) noexcept : array(0, std::move(alloc)) {} + + template + template + constexpr original::array::array(const TYPE(& arr)[N], ALLOC alloc) noexcept : array(N, std::move(alloc)) + { + for (u_integer i = 0; i < N; ++i) + { + this->setElem(i, arr[i]); + } + } + + template + constexpr original::array::array(const TYPE(&)[0], ALLOC alloc) noexcept : array(0, std::move(alloc)) {} + + template + original::array::array(arrayView view, ALLOC alloc) : array(view.count(), std::move(alloc)) + { + for (u_integer i = 0; i < view.count(); ++i) + { + this->setElem(i, view[i]); + } + } + template original::array::array(const array& other) : array(other.size()) { @@ -472,6 +591,48 @@ namespace std { return this->body[0]; } + template + original::arrayView original::array::view() + { + return arrayView{this->body, this->size_}; + } + + template + original::arrayView + original::array::slice(u_integer start, u_integer end) + { + if (start > this->size() || end > this->size()) + throw outOfBoundError( + printable::formatStrings("Slice range [", start, ":", end, + "] out of bounds [0:", this->size(), "].")); + + if (start >= end) + return arrayView{}; + + return arrayView{this->body + start, end - start}; + } + + template + original::arrayView original::array::view() const + { + return arrayView{this->body, this->size_}; + } + + template + original::arrayView + original::array::slice(u_integer start, u_integer end) const + { + if (start > this->size() || end > this->size()) + throw outOfBoundError( + printable::formatStrings("Slice range [", start, ":", end, + "] out of bounds [0:", this->size(), "].")); + + if (start >= end) + return arrayView{}; + + return arrayView{this->body + start, end - start}; + } + template auto original::array::get(integer index) const -> TYPE { diff --git a/src/core/arrayView.h b/src/core/arrayView.h new file mode 100644 index 0000000..67d14d5 --- /dev/null +++ b/src/core/arrayView.h @@ -0,0 +1,432 @@ +#ifndef ORIGINAL_ARRAYVIEW_H +#define ORIGINAL_ARRAYVIEW_H +#include "error.h" + + +namespace original { + /** + * @class arrayView + * @tparam TYPE Type of the elements viewed by the arrayView + * @brief A lightweight, non-owning view over a contiguous sequence of elements. + * @details The `arrayView` class provides a uniform interface for accessing array-like data structures + * including built-in arrays and other contiguous containers. It does not own the data and + * provides read and write access to the underlying elements through a safe, bounds-checked interface. + * + * This class supports iteration, slicing, and element access operations, making it suitable for + * functions that need to work with different array-like types without copying data. + */ + template + class arrayView { + TYPE* const data_; ///< Pointer to the viewed data + const u_integer count_; ///< Number of elements in the view + + public: + /** + * @class iterator + * @brief Random access iterator for arrayView + * @details Provides random access iteration over the elements of an arrayView. + * Supports standard iterator operations including increment, decrement, + * and pointer arithmetic. + */ + class iterator { + TYPE* data_; ///< Current pointer position + + /** + * @brief Constructs an iterator pointing to the specified data + * @param data Pointer to the element + */ + explicit iterator(TYPE* data); + public: + friend arrayView; + + using iterator_category = std::random_access_iterator_tag; ///< Iterator category + using value_type = TYPE; ///< Type of elements + using difference_type = std::ptrdiff_t; ///< Difference type + using pointer = TYPE*; ///< Pointer type + using reference = TYPE&; ///< Reference type + + /** + * @brief Pre-increment operator + * @return Reference to this iterator after incrementing + */ + iterator& operator++(); + + /** + * @brief Dereference operator + * @return Reference to the current element + */ + TYPE& operator*(); + + /** + * @brief Arrow operator + * @return Pointer to the current element + */ + TYPE* operator->(); + + /** + * @brief Inequality comparison + * @param other Iterator to compare with + * @return true if iterators point to different elements + */ + bool operator!=(const iterator& other) const; + + /** + * @brief Equality comparison + * @param other Iterator to compare with + * @return true if iterators point to the same element + */ + bool operator==(const iterator& other) const; + + /** + * @brief Compound addition assignment + * @param offset Number of elements to advance + * @return Reference to this iterator + */ + iterator& operator+=(difference_type offset); + + /** + * @brief Compound subtraction assignment + * @param offset Number of elements to retreat + * @return Reference to this iterator + */ + iterator& operator-=(difference_type offset); + + /** + * @brief Addition operator + * @param offset Number of elements to advance + * @return New iterator at the advanced position + */ + iterator operator+(difference_type offset) const; + + /** + * @brief Subtraction operator + * @param offset Number of elements to retreat + * @return New iterator at the retreated position + */ + iterator operator-(difference_type offset) const; + + /** + * @brief Difference operator + * @param other Iterator to subtract from this one + * @return Number of elements between the two iterators + */ + difference_type operator-(const iterator& other) const; + }; + + /** + * @brief Constructs an empty arrayView + */ + arrayView(); + + /** + * @brief Copy constructor (default) + */ + arrayView(const arrayView&) = default; + + /** + * @brief Copy assignment operator (deleted) + */ + arrayView& operator=(const arrayView&) = delete; + + /** + * @brief Move constructor (default) + */ + arrayView(arrayView&&) noexcept = default; + + /** + * @brief Move assignment operator (deleted) + */ + arrayView& operator=(arrayView&&) noexcept = delete; + + /** + * @brief Constructs an arrayView from a pointer and count + * @param data Pointer to the data + * @param count Number of elements in the data + */ + arrayView(TYPE* data, u_integer count); + + /** + * @brief Constructs an arrayView from a built-in array + * @tparam N Size of the array + * @param arr Reference to the built-in array + */ + template + explicit constexpr arrayView(TYPE (&arr)[N]) noexcept; + + /** + * @brief Constructs an arrayView from a zero-sized built-in array + * @param arr Reference to the zero-sized array + */ + explicit constexpr arrayView(TYPE (&arr)[0]) noexcept; + + /** + * @brief Subscript operator for element access + * @param index Index of the element to access + * @return Reference to the element at the specified index + * @throws outOfBoundError if index is out of bounds + */ + TYPE& operator[](u_integer index); + + /** + * @brief Const subscript operator for element access + * @param index Index of the element to access + * @return Const reference to the element at the specified index + * @throws outOfBoundError if index is out of bounds + */ + const TYPE& operator[](u_integer index) const; + + /** + * @brief Bounds-checked element access + * @param index Index of the element to access + * @return Reference to the element at the specified index + * @throws outOfBoundError if index is out of bounds + */ + TYPE& get(u_integer index); + + /** + * @brief Const bounds-checked element access + * @param index Index of the element to access + * @return Const reference to the element at the specified index + * @throws outOfBoundError if index is out of bounds + */ + const TYPE& get(u_integer index) const; + + /** + * @brief Returns a pointer to the underlying data + * @return Const pointer to the data + */ + const TYPE* data() const; + + /** + * @brief Returns the number of elements in the view + * @return Number of elements + */ + [[nodiscard]] u_integer count() const; + + /** + * @brief Checks if the view is empty + * @return true if the view contains no elements + */ + [[nodiscard]] bool empty() const; + + /** + * @brief Returns an iterator to the first element + * @return Iterator to the beginning + */ + iterator begin(); + + /** + * @brief Returns an iterator to the element after the last + * @return Iterator to the end + */ + iterator end(); + + /** + * @brief Creates a subview of this view + * @param start Starting index of the subview + * @param count Number of elements in the subview + * @return New arrayView covering the specified range + * @throws outOfBoundError if the range is invalid + */ + arrayView subview(u_integer start, u_integer count) const; + + /** + * @brief Creates a slice of this view from start to end index + * @param start Starting index of the slice (inclusive) + * @param end Ending index of the slice (exclusive) + * @return New arrayView covering the specified range [start, end) + * @throws outOfBoundError if start or end is out of bounds [0, count()] + * + * @details The slice method creates a new arrayView that represents a contiguous + * subsequence of the original view. The range includes the element at + * the start index and excludes the element at the end index, following + * the common [start, end) half-open interval convention. + * + * If start >= end, an empty arrayView is returned. + * If start or end exceeds the view's bounds, an outOfBoundError is thrown. + * + * @example + * arrayView view = ...; // view with elements [0, 1, 2, 3, 4] + * auto slice1 = view.slice(1, 4); // contains [1, 2, 3] + * auto slice2 = view.slice(2, 2); // empty view + * auto slice3 = view.slice(0, 5); // entire view + */ + arrayView slice(u_integer start, u_integer end) const; + + /** + * @brief Destructor (default) + */ + ~arrayView() = default; + }; +} + +template +original::arrayView::iterator::iterator(TYPE* data) : data_(data) {} + +template +original::arrayView::iterator& +original::arrayView::iterator::operator++() +{ + ++this->data_; + return *this; +} + +template +TYPE& original::arrayView::iterator::operator*() +{ + return *this->data_; +} + +template +TYPE* original::arrayView::iterator::operator->() +{ + return this->data_; +} + +template +bool original::arrayView::iterator::operator!=(const iterator& other) const +{ + return this->data_ != other.data_; +} + +template +bool original::arrayView::iterator::operator==(const iterator& other) const +{ + return this->data_ == other.data_; +} + +template +original::arrayView::iterator& +original::arrayView::iterator::operator+=(difference_type offset) +{ + this->data_ += offset; + return *this; +} + +template +original::arrayView::iterator& +original::arrayView::iterator::operator-=(difference_type offset) +{ + this->data_ -= offset; + return *this; +} + +template +original::arrayView::iterator +original::arrayView::iterator::operator+(difference_type offset) const +{ + auto front = *this; + front += offset; + return front; +} + +template +original::arrayView::iterator +original::arrayView::iterator::operator-(difference_type offset) const +{ + auto back = *this; + back -= offset; + return back; +} + +template +original::arrayView::iterator::difference_type +original::arrayView::iterator::operator-(const iterator& other) const +{ + return static_cast(this->data_ - other.data_); +} + +template +original::arrayView::arrayView() : data_(nullptr), count_(0) {} + +template +original::arrayView::arrayView(TYPE* data, const u_integer count) : data_(data), count_(count) {} + +template +template +constexpr original::arrayView::arrayView(TYPE(& arr)[N]) noexcept : data_(arr), count_(N) {} + +template +constexpr original::arrayView::arrayView(TYPE(& arr)[0]) noexcept : data_(arr), count_(0) {} + +template +TYPE& original::arrayView::operator[](u_integer index) +{ + return this->data_[index]; +} + +template +const TYPE& original::arrayView::operator[](u_integer index) const +{ + return this->data_[index]; +} + +template +TYPE& original::arrayView::get(u_integer index) +{ + if (index >= this->count_) + throw outOfBoundError("Index " + printable::formatString(index) + + " out of bound max index " + printable::formatString(this->count() - 1) + "."); + return this->data_[index]; +} + +template +const TYPE& original::arrayView::get(u_integer index) const +{ + if (index >= this->count_) + throw outOfBoundError("Index " + printable::formatString(index) + + " out of bound max index " + printable::formatString(this->count() - 1) + "."); + return this->data_[index]; +} + +template +const TYPE* original::arrayView::data() const +{ + return this->data_; +} + +template +original::u_integer original::arrayView::count() const +{ + return this->count_; +} + +template +bool original::arrayView::empty() const +{ + return this->count_ == 0; +} + +template +original::arrayView::iterator original::arrayView::begin() +{ + return iterator{this->data_}; +} + +template +original::arrayView::iterator original::arrayView::end() +{ + return iterator{this->data_ + this->count_}; +} + +template +original::arrayView original::arrayView::subview(const u_integer start, const u_integer count) const +{ + return this->slice(start, start + count); +} + +template +original::arrayView original::arrayView::slice(const u_integer start, const u_integer end) const +{ + if (start > this->count() || end > this->count()) + throw outOfBoundError( + printable::formatStrings("Slice range [", start, ":", end, + "] out of bounds [0:", this->count(), "].")); + + if (start >= end) + return arrayView{}; + + return arrayView{this->data_ + start, end - start}; +} + +#endif //ORIGINAL_ARRAYVIEW_H diff --git a/src/core/bitSet.h b/src/core/bitSet.h index ed1216a..3cf4267 100644 --- a/src/core/bitSet.h +++ b/src/core/bitSet.h @@ -674,12 +674,12 @@ namespace std { auto* other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); if (this->container_ != other_it->container_) return this->container_ > other_it->container_ ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); return toOuterIdx(this->cur_block, this->cur_bit) - toOuterIdx(other_it->cur_block, other_it->cur_bit); } @@ -800,7 +800,7 @@ namespace std { } auto nb = bitSet(new_size); - const u_integer blocks_min = min(nb.map.size(), + const u_integer blocks_min = minimum(nb.map.size(), this->map.size()); for (u_integer i = 0; i < blocks_min; i++) { nb.map.set(i, this->map.get(i)); diff --git a/src/core/blocksList.h b/src/core/blocksList.h index 06ff3ce..d7aba84 100644 --- a/src/core/blocksList.h +++ b/src/core/blocksList.h @@ -742,12 +742,12 @@ namespace std { auto* other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); if (this->container_ != other_it->container_) return this->container_ > other_it->container_ ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); return static_cast(innerIdxToAbsIdx(this->cur_block, this->cur_pos)) - static_cast(innerIdxToAbsIdx(other_it->cur_block, other_it->cur_pos)); diff --git a/src/core/chain.h b/src/core/chain.h index 581ee41..b92083d 100644 --- a/src/core/chain.h +++ b/src/core/chain.h @@ -591,7 +591,12 @@ namespace std { } template - original::chain::chain(ALLOC alloc) : baseList(std::move(alloc)), size_(0), rebind_alloc(std::move(rebind_alloc_node{})) + original::chain::chain(ALLOC alloc) + : baseList(std::move(alloc)), + size_(0), + begin_(nullptr), + end_(nullptr), + rebind_alloc(std::move(rebind_alloc_node{})) { chainInit(); } diff --git a/src/core/config.h b/src/core/config.h index b373d2e..741358d 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -17,22 +17,37 @@ * @{ */ +// Initialize all platform macros to false +#define ORIGINAL_PLATFORM_WINDOWS 0 +#define ORIGINAL_PLATFORM_WINDOWS_32 0 +#define ORIGINAL_PLATFORM_WINDOWS_64 0 +#define ORIGINAL_PLATFORM_LINUX 0 +#define ORIGINAL_PLATFORM_MACOS 0 +#define ORIGINAL_PLATFORM_UNIX 0 +#define ORIGINAL_PLATFORM_UNKNOWN 0 + +// Override based on actual platform detection #if defined(_WIN32) || defined(_WIN64) -#define ORIGINAL_PLATFORM_WINDOWS 1 -#ifdef _WIN64 -#define ORIGINAL_PLATFORM_WINDOWS_64 1 -#elif -#define ORIGINAL_PLATFORM_WINDOWS_32 1 -#else - #define ORIGINAL_PLATFORM_WINDOWS 0 -#endif + #undef ORIGINAL_PLATFORM_WINDOWS + #define ORIGINAL_PLATFORM_WINDOWS 1 + #ifdef _WIN64 + #undef ORIGINAL_PLATFORM_WINDOWS_64 + #define ORIGINAL_PLATFORM_WINDOWS_64 1 + #else + #undef ORIGINAL_PLATFORM_WINDOWS_32 + #define ORIGINAL_PLATFORM_WINDOWS_32 1 + #endif #elif defined(__linux__) -#define ORIGINAL_PLATFORM_LINUX 1 + #undef ORIGINAL_PLATFORM_LINUX + #define ORIGINAL_PLATFORM_LINUX 1 #elif defined(__APPLE__) && defined(__MACH__) + #undef ORIGINAL_PLATFORM_MACOS #define ORIGINAL_PLATFORM_MACOS 1 #elif defined(__unix__) + #undef ORIGINAL_PLATFORM_UNIX #define ORIGINAL_PLATFORM_UNIX 1 #else + #undef ORIGINAL_PLATFORM_UNKNOWN #define ORIGINAL_PLATFORM_UNKNOWN 1 #endif /** @} */ // end of PlatformDetection group @@ -42,16 +57,28 @@ * @brief Macros for identifying the compiler being used * @{ */ + +// Initialize all compiler macros to false +#define ORIGINAL_COMPILER_CLANG 0 +#define ORIGINAL_COMPILER_GCC 0 +#define ORIGINAL_COMPILER_MSVC 0 +#define ORIGINAL_COMPILER_UNKNOWN 0 + +// Override based on actual compiler detection #if defined(__clang__) -#define ORIGINAL_COMPILER_CLANG 1 + #undef ORIGINAL_COMPILER_CLANG + #define ORIGINAL_COMPILER_CLANG 1 #define ORIGINAL_COMPILER_VERSION __clang_major__.__clang_minor__.__clang_patchlevel__ #elif defined(__GNUC__) || defined(__GNUG__) -#define ORIGINAL_COMPILER_GCC 1 -#define ORIGINAL_COMPILER_VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__ + #undef ORIGINAL_COMPILER_GCC + #define ORIGINAL_COMPILER_GCC 1 + #define ORIGINAL_COMPILER_VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__ #elif defined(_MSC_VER) -#define ORIGINAL_COMPILER_MSVC 1 + #undef ORIGINAL_COMPILER_MSVC + #define ORIGINAL_COMPILER_MSVC 1 #define ORIGINAL_COMPILER_VERSION _MSC_VER #else + #undef ORIGINAL_COMPILER_UNKNOWN #define ORIGINAL_COMPILER_UNKNOWN 1 #endif /** @} */ // end of CompilerDetection group @@ -228,50 +255,71 @@ namespace original { * @{ */ - /** - * @brief Unsigned 8-bit integer type (byte) - * @details Typically used for raw byte manipulation and binary data handling. - * @note Range: 0 to 255 - * @note Equivalent to std::uint8_t - */ - using byte = std::uint8_t; + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + /** + * @brief Unsigned 8-bit integer type (byte) + * @details Typically used for raw byte manipulation and binary data handling. + * @note Range: 0 to 255 + * @note Equivalent to std::uint8_t + */ + using byte = std::uint8_t; + #elif ORIGINAL_COMPILER_MSVC + using byte = uint8_t; + #endif - /** - * @brief Signed 8-bit integer type - * @details Used for small signed numeric values. - * @note Range: -128 to 127 - * @note Equivalent to std::int8_t - */ - using s_byte = std::int8_t; - /** - * @brief 64-bit signed integer type for arithmetic operations - * @details Primary type for most arithmetic operations where large range is needed. - * @note Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - * @note Use this for calculations that may require large numbers - * @note Equivalent to std::int64_t - */ - using integer = std::int64_t; + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + /** + * @brief Signed 8-bit integer type + * @details Used for small signed numeric values. + * @note Range: -128 to 127 + * @note Equivalent to std::int8_t + */ + using s_byte = std::int8_t; + #elif ORIGINAL_COMPILER_MSVC + using s_byte = int8_t; + #endif - /** - * @brief 32-bit unsigned integer type for sizes and indexes - * @details Used for array indexing, sizes, and counts where negative values are not needed. - * @note Range: 0 to 4,294,967,295 - * @warning Not suitable for very large containers (>4GB) - * @note Equivalent to std::uint32_t - */ - using u_integer = std::uint32_t; + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + /** + * @brief 64-bit signed integer type for arithmetic operations + * @details Primary type for most arithmetic operations where large range is needed. + * @note Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + * @note Use this for calculations that may require large numbers + * @note Equivalent to std::int64_t + */ + using integer = std::int64_t; + #elif ORIGINAL_COMPILER_MSVC + using integer = int64_t; + #endif - /** - * @brief 64-bit unsigned integer type - * @details Large unsigned integer type for big sizes and counters. - * Guaranteed to be exactly 64 bits wide across all platforms. - * @note Range: 0 to 18,446,744,073,709,551,615 - * @note Preferred for: Large containers, filesystem sizes, big counters - * @see u_integer For smaller unsigned needs - * @note Equivalent to std::uint64_t - */ - using ul_integer = std::uint64_t; + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + /** + * @brief 32-bit unsigned integer type for sizes and indexes + * @details Used for array indexing, sizes, and counts where negative values are not needed. + * @note Range: 0 to 4,294,967,295 + * @warning Not suitable for very large containers (>4GB) + * @note Equivalent to std::uint32_t + */ + using u_integer = std::uint32_t; + #elif ORIGINAL_COMPILER_MSVC + using u_integer = uint32_t; + #endif + + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + /** + * @brief 64-bit unsigned integer type + * @details Large unsigned integer type for big sizes and counters. + * Guaranteed to be exactly 64 bits wide across all platforms. + * @note Range: 0 to 18,446,744,073,709,551,615 + * @note Preferred for: Large containers, filesystem sizes, big counters + * @see u_integer For smaller unsigned needs + * @note Equivalent to std::uint64_t + */ + using ul_integer = std::uint64_t; + #elif ORIGINAL_COMPILER_MSVC + using ul_integer = uint64_t; + #endif /** * @brief Double-precision floating-point type diff --git a/src/core/core.h b/src/core/core.h index 869e7fa..d2d4f4f 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -57,6 +57,7 @@ #include "algorithms.h" #include "allocator.h" #include "array.h" +#include "arrayView.h" #include "autoPtr.h" #include "baseArray.h" #include "baseList.h" diff --git a/src/core/filterStream.h b/src/core/filterStream.h index b903183..0705e2e 100644 --- a/src/core/filterStream.h +++ b/src/core/filterStream.h @@ -26,14 +26,15 @@ namespace original{ template class filterStream { - ///< Null filter instance used as operator placeholder in stream processing - static const strongPtr> nullFilter; - + public: /// @internal Operator types for postfix conversion enum class opts{AND = 1, OR = 0, NOT = 2, LEFT_BRACKET = 3, RIGHT_BRACKET = 4}; + private: + ///< Null filter instance used as operator placeholder in stream processing + static const strongPtr> nullFilter; - mutable chain>> stream; ///< Filter operand chain - mutable chain ops; ///< Operator sequence storage + mutable chain>> stream{}; ///< Filter operand chain + mutable chain ops{}; ///< Operator sequence storage mutable bool flag; ///< Postfix conversion status flag protected: diff --git a/src/core/forwardChain.h b/src/core/forwardChain.h index a00e4ee..eee4eda 100644 --- a/src/core/forwardChain.h +++ b/src/core/forwardChain.h @@ -559,7 +559,10 @@ namespace original { template original::forwardChain::forwardChain(ALLOC alloc) - : baseList(std::move(alloc)), size_(0) , rebind_alloc(std::move(rebind_alloc_node{})) + : baseList(std::move(alloc)), + size_(0), + begin_(nullptr), + rebind_alloc(std::move(rebind_alloc_node{})) { this->chainInit(); } diff --git a/src/core/hashTable.h b/src/core/hashTable.h index 5b70e87..e9bc169 100644 --- a/src/core/hashTable.h +++ b/src/core/hashTable.h @@ -4,7 +4,6 @@ #include "allocator.h" #include "couple.h" #include "hash.h" -#include "singleDirectionIterator.h" #include "vector.h" #include "wrapper.h" @@ -53,7 +52,7 @@ namespace original { */ template, typename HASH = hash> class hashTable{ - protected: + public: /** * @class hashNode @@ -165,13 +164,13 @@ namespace original { * @typedef rebind_alloc_node * @brief Rebound allocator type for node storage */ - using rebind_alloc_node = typename ALLOC::template rebind_alloc; + using rebind_alloc_node = ALLOC::template rebind_alloc; /** * @typedef rebind_alloc_pointer * @brief Rebound allocator type for pointer storage */ - using rebind_alloc_pointer = typename ALLOC::template rebind_alloc; + using rebind_alloc_pointer = ALLOC::template rebind_alloc; /** * @typedef buckets_type @@ -492,7 +491,7 @@ namespace original { * @brief Destroys hashTable * @details Cleans up all nodes and buckets */ - ~hashTable(); + virtual ~hashTable(); }; } @@ -506,7 +505,7 @@ original::hashTable::hashNode::hashNode(const hashN } template -typename original::hashTable::hashNode& +original::hashTable::hashNode& original::hashTable::hashNode::operator=(const hashNode& other) { if (this == &other) return *this; @@ -553,13 +552,13 @@ void original::hashTable::hashNode::setValue(const } template -typename original::hashTable::hashNode* +original::hashTable::hashNode* original::hashTable::hashNode::getPPrev() const { throw unSupportedMethodError(); } template -typename original::hashTable::hashNode* +original::hashTable::hashNode* original::hashTable::hashNode::getPNext() const { return this->next_; } @@ -611,7 +610,7 @@ original::hashTable::Iterator::Iterator(const Itera } template -typename original::hashTable::Iterator& +original::hashTable::Iterator& original::hashTable::Iterator::operator=(const Iterator &other) { if (this == &other) return *this; @@ -689,7 +688,7 @@ bool original::hashTable::Iterator::isValid() const } template -typename original::hashTable::buckets_type +original::hashTable::buckets_type original::hashTable::bucketsCopy(const buckets_type& old_buckets) const { buckets_type new_buckets = buckets_type(old_buckets.size(), rebind_alloc_pointer{}, nullptr); @@ -714,7 +713,7 @@ original::hashTable::bucketsCopy(const buckets_type } template -typename original::hashTable::hashNode* +original::hashTable::hashNode* original::hashTable::createNode(const K_TYPE& key, const V_TYPE& value, hashNode* next) const { auto node = this->rebind_alloc.allocate(1); this->rebind_alloc.construct(node, key, value, next); @@ -740,7 +739,7 @@ original::hashTable::getBucketCount() const { } template -typename original::hashTable::hashNode* +original::hashTable::hashNode* original::hashTable::getBucket(const K_TYPE &key) const { u_integer code = this->getHashCode(key); return this->buckets[code]; @@ -812,7 +811,7 @@ original::hashTable::hashTable(HASH hash) } template -typename original::hashTable::hashNode* +original::hashTable::hashNode* original::hashTable::find(const K_TYPE& key) const { if (this->size_ == 0) return nullptr; diff --git a/src/core/maps.h b/src/core/maps.h index 33ba211..c06034c 100644 --- a/src/core/maps.h +++ b/src/core/maps.h @@ -830,8 +830,8 @@ namespace original { typename V_TYPE, typename Compare = increaseComparator, typename ALLOC = allocator>> - class JMap final : public skipList, - public map, + class JMap final : public map, + public skipList, public iterable>, public printable { @@ -1923,8 +1923,8 @@ original::JMap::Iterator::operator-( auto other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); return skipListType::Iterator::operator-(*other_it); } @@ -1993,8 +1993,7 @@ bool original::JMap::Iterator::isValid() const { template original::JMap::JMap(Compare comp, ALLOC alloc) - : skipListType(std::move(comp)) , - map(std::move(alloc)) {} + : map(std::move(alloc)), skipListType(std::move(comp)) {} template original::JMap::JMap(const JMap& other) : JMap() { diff --git a/src/core/maths.h b/src/core/maths.h index b5299d0..982b69c 100644 --- a/src/core/maths.h +++ b/src/core/maths.h @@ -79,7 +79,7 @@ TYPE abs(TYPE a); * @endcode */ template -TYPE max(TYPE a, TYPE b); +TYPE maximum(TYPE a, TYPE b); /** * @brief Returns the smaller of two given values. @@ -100,7 +100,7 @@ TYPE max(TYPE a, TYPE b); * @endcode */ template -TYPE min(TYPE a, TYPE b); +TYPE minimum(TYPE a, TYPE b); /** * @brief Returns the result of raising a base to an exponent. @@ -186,13 +186,13 @@ auto original::abs(TYPE a) -> TYPE } template -auto original::max(TYPE a, TYPE b) -> TYPE +auto original::maximum(TYPE a, TYPE b) -> TYPE { return a > b ? a : b; } template -auto original::min(TYPE a, TYPE b) -> TYPE +auto original::minimum(TYPE a, TYPE b) -> TYPE { return a < b ? a : b; } diff --git a/src/core/optional.h b/src/core/optional.h index 9726891..f86036c 100644 --- a/src/core/optional.h +++ b/src/core/optional.h @@ -8,7 +8,7 @@ * @file optional.h * @brief Type-safe optional value container * @details Provides an alternative class that can either contain a value of type 'TYPE' - * or be in an empty state (represented by original::none from types.h). This implementation + * or be in an empty state (represented by original::null from types.h). This implementation * provides: * - Value semantics with proper construction/destruction * - Safe access operations with error checking @@ -20,7 +20,7 @@ * Key features: * - Type-safe alternative to raw pointers for optional values * - No dynamic memory allocation (uses union storage) - * - Explicit empty state handling with original::none + * - Explicit empty state handling with original::null * - Exception-safe operations * - STL-compatible interface */ @@ -33,7 +33,7 @@ namespace original { * @tparam TYPE The type of value to store * @details This class provides a way to represent optional values without * using pointers. It can either contain a value of type TYPE or be empty. - * The empty state is represented using original::none from types.h. + * The empty state is represented using original::null from types.h. * * The implementation uses a union for storage to avoid dynamic allocation * and ensure optimal performance. All operations provide strong exception @@ -62,18 +62,18 @@ namespace original { class alternative { /** * @union storage - * @brief Internal storage for either TYPE or none - * @details Uses a union to store either the value type or none, + * @brief Internal storage for either TYPE or null + * @details Uses a union to store either the value type or null, * with proper construction/destruction handling. The union ensures * that only one member is active at any time, managed by the - * non_none_type_ flag. + * non_null_type_ flag. */ union storage { TYPE type_; ///< Storage for the contained value - none none_; ///< Storage for empty state (from types.h) + null null_; ///< Storage for empty state (from types.h) /** - * @brief Default constructor (initializes to none) + * @brief Default constructor (initializes to null) */ storage() noexcept; @@ -87,14 +87,14 @@ namespace original { ~storage(); }; - bool non_none_type_; ///< Flag indicating whether value is present (true = TYPE, false = none) + bool non_null_type_; ///< Flag indicating whether value is present (true = TYPE, false = null) storage val_; ///< The actual storage union /** * @brief Safely destroys the current contents * @note Properly calls destructor based on current state - * @details If non_none_type_ is true, calls TYPE destructor, - * otherwise calls none destructor. Ensures RAII compliance. + * @details If non_null_type_ is true, calls TYPE destructor, + * otherwise calls null destructor. Ensures RAII compliance. */ void destroy() noexcept; @@ -286,11 +286,11 @@ namespace original { explicit alternative(); /** - * @brief Constructs from none (empty state) - * @param n none value indicating empty state + * @brief Constructs from null (empty state) + * @param n null value indicating empty state * @post hasValue() == false */ - explicit alternative(none n); + explicit alternative(null n); /** * @brief Constructs with value present @@ -331,19 +331,19 @@ namespace original { alternative& operator=(std::in_place_t t) noexcept; /** - * @brief Assignment from none to reset to empty state - * @param n none value indicating empty state + * @brief Assignment from null to reset to empty state + * @param n null value indicating empty state * @return Reference to this alternative * @post hasValue() == false * @details Resets the alternative to the empty state. This operation * is equivalent to calling reset() but provides a more expressive syntax - * when working with none values. The assignment clears the "present" state + * when working with null values. The assignment clears the "present" state * and ensures the alternative is empty. * * Example usage: * @code * alternative flag(std::in_place); // Present state - * flag = original::none; // Now empty + * flag = original::null; // Now empty * * if (!flag) { * // This will execute since flag is now empty @@ -352,14 +352,14 @@ namespace original { * // In generic code: * template * void clearAlternative(alternative& opt) { - * opt = original::none; // Works for both alternative and alternative + * opt = original::null; // Works for both alternative and alternative * } * @endcode * * @note This operation is noexcept and always succeeds. * @see reset() */ - alternative& operator=(none n) noexcept; + alternative& operator=(null n) noexcept; /** * @brief Resets to empty state @@ -422,7 +422,7 @@ namespace std { template original::alternative::storage::storage() noexcept { - new(&this->none_) none{}; + new(&this->null_) null; } template @@ -430,33 +430,33 @@ original::alternative::storage::~storage() {} template void original::alternative::destroy() noexcept { - if (this->non_none_type_){ + if (this->non_null_type_){ this->val_.type_.~TYPE(); - this->non_none_type_ = false; + this->non_null_type_ = false; } else { - this->val_.none_.~none(); - this->non_none_type_ = true; + this->val_.null_.~null(); + this->non_null_type_ = true; } } template original::alternative::alternative() - : non_none_type_(false), val_() {} + : non_null_type_(false), val_() {} template template original::alternative::alternative(Args &&... args) - : non_none_type_(true), val_() { + : non_null_type_(true), val_() { new (&this->val_.type_) TYPE{ std::forward(args)... }; } template original::alternative::alternative(const alternative& other) { - this->non_none_type_ = other.non_none_type_; - if (other.non_none_type_) { + this->non_null_type_ = other.non_null_type_; + if (other.non_null_type_) { new (&val_.type_) TYPE{ other.val_.type_ }; } else { - new (&val_.none_) none{}; + new (&val_.null_) null; } } @@ -467,22 +467,22 @@ original::alternative::operator=(const alternative& other) { return *this; this->destroy(); - this->non_none_type_ = other.non_none_type_; - if (other.non_none_type_) { + this->non_null_type_ = other.non_null_type_; + if (other.non_null_type_) { new (&val_.type_) TYPE{ other.val_.type_ }; } else { - new (&val_.none_) none{}; + new (&val_.null_) null; } return *this; } template original::alternative::alternative(alternative&& other) noexcept { - this->non_none_type_ = other.non_none_type_; - if (this->non_none_type_){ + this->non_null_type_ = other.non_null_type_; + if (this->non_null_type_){ new (&val_.type_) TYPE{ std::move(other.val_.type_) }; } else{ - new (&val_.none_) none{}; + new (&val_.null_) null; } } @@ -493,11 +493,11 @@ original::alternative::operator=(alternative&& other) noexcept { return *this; this->destroy(); - this->non_none_type_ = other.non_none_type_; - if (this->non_none_type_){ + this->non_null_type_ = other.non_null_type_; + if (this->non_null_type_){ new (&val_.type_) TYPE{ std::move(other.val_.type_) }; } else{ - new (&val_.none_) none{}; + new (&val_.null_) null; } return *this; } @@ -508,15 +508,15 @@ void original::alternative::swap(alternative& other) noexcept if (this == &other) return; - std::swap(this->non_none_type_, other.non_none_type_); + std::swap(this->non_null_type_, other.non_null_type_); std::swap(this->val_, other.val_); } template const TYPE& original::alternative::operator*() const { - if (!this->non_none_type_) - throw valueError("Dereferencing a original::none value"); + if (!this->non_null_type_) + throw valueError("Dereferencing a original::null value"); return this->val_.type_; } @@ -524,8 +524,8 @@ original::alternative::operator*() const { template TYPE& original::alternative::operator*() { - if (!this->non_none_type_) - throw valueError("Dereferencing a original::none value"); + if (!this->non_null_type_) + throw valueError("Dereferencing a original::null value"); return this->val_.type_; } @@ -533,8 +533,8 @@ original::alternative::operator*() { template const TYPE* original::alternative::operator->() const { - if (!this->non_none_type_) - throw valueError("Accessing member of a original::none value"); + if (!this->non_null_type_) + throw valueError("Accessing member of a original::null value"); return &this->val_.type_; } @@ -542,20 +542,20 @@ original::alternative::operator->() const { template TYPE* original::alternative::operator->() { - if (!this->non_none_type_) - throw valueError("Accessing member of a original::none value"); + if (!this->non_null_type_) + throw valueError("Accessing member of a original::null value"); return &this->val_.type_; } template const TYPE* original::alternative::get() const { - return this->non_none_type_ ? &this->val_.type_ : nullptr; + return this->non_null_type_ ? &this->val_.type_ : nullptr; } template TYPE* original::alternative::get() { - return this->non_none_type_ ? &this->val_.type_ : nullptr; + return this->non_null_type_ ? &this->val_.type_ : nullptr; } template @@ -567,14 +567,14 @@ template template void original::alternative::emplace(Args &&... args) { this->destroy(); - this->non_none_type_ = true; + this->non_null_type_ = true; new (&val_.type_) TYPE{ std::forward(args)... }; } template void original::alternative::set(const TYPE &t) { this->destroy(); - this->non_none_type_ = true; + this->non_null_type_ = true; new (&val_.type_) TYPE{ t }; } @@ -588,7 +588,7 @@ original::alternative::operator=(const TYPE& t) template original::alternative::operator bool() const { - return this->non_none_type_; + return this->non_null_type_; } template @@ -604,7 +604,7 @@ original::alternative::~alternative() { inline original::alternative::alternative() = default; -inline original::alternative::alternative(none) {} +inline original::alternative::alternative(null) {} inline original::alternative::alternative(std::in_place_t) : has_value_(true) {} @@ -621,7 +621,7 @@ original::alternative::operator=(std::in_place_t) noexcept } inline original::alternative& -original::alternative::operator=(none) noexcept +original::alternative::operator=(null) noexcept { this->reset(); return *this; diff --git a/src/core/printable.h b/src/core/printable.h index dc734b1..4fbdff3 100644 --- a/src/core/printable.h +++ b/src/core/printable.h @@ -207,6 +207,9 @@ class printable { template static std::string formatEnum(const TYPE& t); + template + static std::string formatStrings(Args&&... args); + friend std::ostream& operator<<(std::ostream& os, const printable& p); }; @@ -365,6 +368,14 @@ auto original::printable::formatEnum(const TYPE& t) -> std::string return enum_name + "(" + std::to_string(enum_value) + ")"; } +template +std::string original::printable::formatStrings(Args&&... args) +{ + std::stringstream ss; + (ss << ... << formatString(std::forward(args))); + return ss.str(); +} + template<> inline auto original::printable::formatString(const std::nullptr_t&) -> std::string { @@ -412,7 +423,7 @@ inline std::ostream& original::operator<<(std::ostream& os, const printable& p){ template requires original::ExtendsOf -std::string std::to_string(const T& t) +std::string std::to_string(const T& t) // NOLINT { return original::printable::formatString(t); } diff --git a/src/core/randomAccessIterator.h b/src/core/randomAccessIterator.h index fd7305a..3c79b60 100644 --- a/src/core/randomAccessIterator.h +++ b/src/core/randomAccessIterator.h @@ -260,12 +260,12 @@ namespace original { auto* other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); if (this->_container != other_it->_container) return this->_container > other_it->_container ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); return this->_ptr - other_it->_ptr; } diff --git a/src/core/sets.h b/src/core/sets.h index a9d0db9..9c784fd 100644 --- a/src/core/sets.h +++ b/src/core/sets.h @@ -708,8 +708,8 @@ namespace original { template , typename ALLOC = allocator>> - class JSet final : public skipList, - public set, + class JSet final : public set, + public skipList, public iterable, public printable { using skipListType = skipList; @@ -1653,8 +1653,8 @@ original::JSet::Iterator::operator-( auto other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); return skipListType::Iterator::operator-(*other_it); } @@ -1723,8 +1723,8 @@ bool original::JSet::Iterator::isValid() const { template original::JSet::JSet(Compare comp, ALLOC alloc) - : skipListType(std::move(comp)), - set(std::move(alloc)) {} + : set(std::move(alloc)), + skipListType(std::move(comp)) {} template original::JSet::JSet(const JSet& other) : JSet() { diff --git a/src/core/skipList.h b/src/core/skipList.h index 08c5367..96fa80d 100644 --- a/src/core/skipList.h +++ b/src/core/skipList.h @@ -43,7 +43,7 @@ namespace original { typename ALLOC = allocator, typename Compare = increaseComparator> class skipList { - protected: + public: /** * @class skipListNode * @brief Internal node class for Skip List @@ -366,7 +366,7 @@ namespace original { * @brief Destructor * @details Cleans up all list nodes and allocated memory */ - ~skipList(); + virtual ~skipList(); }; } @@ -517,12 +517,12 @@ template original::integer original::skipList::Iterator::operator-(const Iterator& other) const { if (const integer pos_dis = ptrDistance(&other, this); - pos_dis != std::numeric_limits::max()) return pos_dis; + pos_dis != (std::numeric_limits::max)()) return pos_dis; if (const integer neg_dis = ptrDistance(this, &other); - neg_dis != std::numeric_limits::max()) return -neg_dis; + neg_dis != (std::numeric_limits::max)()) return -neg_dis; return this->cur_ > other.cur_ ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); } template @@ -564,7 +564,7 @@ original::skipList::Iterator::ptrDistance(const s->next(); } if (e->isValid()){ - dis = std::numeric_limits::max(); + dis = (std::numeric_limits::max)(); } return dis; } @@ -672,7 +672,10 @@ original::skipList::findLastNode() const { template original::skipList::skipList(Compare compare) - : size_(0), head_(this->createNode()), compare_(std::move(compare)) {} + : size_(0), head_(nullptr), compare_(std::move(compare)) +{ + this->head_ = this->createNode(); +} template original::skipList::skipListNode* diff --git a/src/core/stepIterator.h b/src/core/stepIterator.h index 8b037a8..2ab1079 100644 --- a/src/core/stepIterator.h +++ b/src/core/stepIterator.h @@ -199,7 +199,7 @@ namespace original s->next(); } if (e->isValid()){ - dis = std::numeric_limits::max(); + dis = (std::numeric_limits::max)(); } return dis; } @@ -302,15 +302,15 @@ namespace original auto* other_it = dynamic_cast(&other); if (other_it == nullptr) return this > &other ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); if (const integer pos_dis = ptrDistance(other_it, this); - pos_dis != std::numeric_limits::max()) return pos_dis; + pos_dis != (std::numeric_limits::max)()) return pos_dis; if (const integer neg_dis = ptrDistance(this, other_it); - neg_dis != std::numeric_limits::max()) return -neg_dis; + neg_dis != (std::numeric_limits::max)()) return -neg_dis; return this->_ptr > other_it->_ptr ? - std::numeric_limits::max() : - std::numeric_limits::min(); + (std::numeric_limits::max)() : + (std::numeric_limits::min)(); } template diff --git a/src/core/types.h b/src/core/types.h index 0a2f997..634fca3 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -24,19 +24,19 @@ namespace original { // ==================== Fundamental Types ==================== /** - * @class none + * @class null * @brief A placeholder type representing the absence of a value. * @details This class provides consteval operations and can be converted to bool, * always returning false. Useful for template metaprogramming and * representing empty states. */ - class none { + class null { public: /// @brief Default constructor (consteval) - consteval explicit none() = default; + consteval explicit null() = default; /// @brief Default destructor (constexpr) - constexpr ~none() = default; + constexpr ~null() = default; /** * @brief Bool conversion operator @@ -516,6 +516,89 @@ namespace original { c.pop_back(); }; + /** + * @class some + * @brief Compile-time conditional type selector based on boolean condition + * @tparam MATCH Boolean condition determining which type to select + * @tparam MATCH_TYPE Type to select when MATCH is true + * @tparam OTHER_TYPE Type to select when MATCH is false + * @details This template provides compile-time type selection similar to std::conditional, + * but with a more explicit naming convention. It's useful for template metaprogramming + * where type selection needs to be based on compile-time conditions. + * + * The class contains a single public type alias: + * - `type`: MATCH_TYPE when MATCH is true, OTHER_TYPE when MATCH is false + * + * @code{.cpp} + * // Example: Select type based on condition + * template + * using CleanType = some_t, + * std::remove_pointer_t, T>; + * + * static_assert(std::is_same_v, int>); // true + * static_assert(std::is_same_v, int>); // true + * + * // Example: Conditional member type + * template + * class MyClass { + * using ValueType = some_t; + * // ValueType is double when USE_DOUBLE=true, float otherwise + * }; + * @endcode + * + * @see some_t + */ + template + class some; + + /** + * @class some + * @brief Specialization for true condition - selects MATCH_TYPE + * @tparam MATCH_TYPE Type to select when condition is true + * @tparam OTHER_TYPE Type to ignore (not selected) + */ + template + class some + { + public: + using type = MATCH_TYPE; ///< Selected type when condition is true + }; + + /** + * @class some + * @brief Specialization for false condition - selects OTHER_TYPE + * @tparam MATCH_TYPE Type to ignore (not selected) + * @tparam OTHER_TYPE Type to select when condition is false + */ + template + class some + { + public: + using type = OTHER_TYPE; ///< Selected type when condition is false + }; + + /** + * @brief Convenience alias template for some::type + * @tparam MATCH Boolean condition determining type selection + * @tparam MATCH_TYPE Type to select when MATCH is true + * @tparam OTHER_TYPE Type to select when MATCH is false + * @details Provides direct access to the selected type without needing to + * explicitly reference the ::type member. This follows the common + * pattern in C++ template metaprogramming utilities. + * + * @code{.cpp} + * // Using some_t directly + * template + * using OptionalRef = some_t, T, T&>; + * + * // Equivalent to: + * // template + * // using OptionalRef = some, T, T&>::type; + * @endcode + */ + template + using some_t = some::type; + // ==================== Compile-time Index Sequences ==================== /** @@ -642,11 +725,11 @@ namespace original { } // namespace original -consteval original::none::operator bool() const { +consteval original::null::operator bool() const { return false; } -consteval bool original::none::operator!() const { +consteval bool original::null::operator!() const { return true; } diff --git a/src/core/vector.h b/src/core/vector.h index 1c7d4a3..cb54e1b 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -276,6 +276,17 @@ namespace original { */ vector(const std::initializer_list& list); + /** + * @brief Constructs a vector from an arrayView + * @param view The arrayView to copy elements from + * @param alloc Allocator instance to use for memory management + * @details This constructor creates a vector by copying elements from an arrayView. + * The size of the created vector will be equal to the count of the arrayView, + * and all elements will be copied from the view. + * This allows seamless conversion between arrayView and vector types. + */ + explicit vector(arrayView view, ALLOC alloc = ALLOC{}); + /** * @brief Constructs a vector from an array. * @param arr The array to construct the vector from. @@ -307,6 +318,12 @@ namespace original { */ ~vector() override; + /** + * @brief Swaps the contents of two vectors + * @param other Vector to swap with + * @details Exchanges the size, capacity, internal buffer, and optionally the allocator between two vectors. + * This operation is noexcept and provides strong exception guarantee. + */ void swap(vector& other) noexcept; // ==================== Capacity Methods ==================== @@ -331,6 +348,41 @@ namespace original { */ TYPE& data() const; + /** + * @brief Returns a view over the entire vector + * @return arrayView providing access to the vector elements + * @details The returned view provides a lightweight, non-owning interface + * to the vector's elements. Changes to the vector may invalidate + * the view if reallocation occurs. + */ + arrayView view(); + + /** + * @brief Returns a slice view of the vector + * @param start Starting index of the slice (inclusive) + * @param end Ending index of the slice (exclusive) + * @return arrayView covering the specified range + * @throws outOfBoundError if the range is invalid + * @details The returned view provides access to elements in the range [start, end). + * If start >= end, returns an empty view. + */ + arrayView slice(u_integer start, u_integer end); + + /** + * @brief Returns a const view over the entire vector + * @return const arrayView providing read-only access to the vector elements + */ + arrayView view() const; + + /** + * @brief Returns a const slice view of the vector + * @param start Starting index of the slice (inclusive) + * @param end Ending index of the slice (exclusive) + * @return const arrayView covering the specified range + * @throws outOfBoundError if the range is invalid + */ + arrayView slice(u_integer start, u_integer end) const; + /** * @brief Gets an element at the specified index. * @param index The index of the element. @@ -634,21 +686,23 @@ namespace std { this->vectorInit(); } -template -template -original::vector::vector(u_integer size, ALLOC alloc, ARGS&&... args) + template + template + original::vector::vector(u_integer size, ALLOC alloc, ARGS&&... args) : vector(size, std::move(alloc)) { - this->body = this->allocate(this->max_size); - for (u_integer i = 0; i < this->size_; ++i) { - this->construct(&this->body[this->toInnerIdx(i)], std::forward(args)...); + for (u_integer i = 0; i < this->max_size; ++i) { + if (i >= this->inner_begin && i < this->inner_begin + this->size()) + this->construct(&this->body[i], std::forward(args)...); + else + this->construct(&this->body[i], TYPE{}); + } } -} -template + template original::vector::vector(const u_integer size, ALLOC alloc) - : baseList(std::move(alloc)), size_(size), - max_size(size * 4 / 3), inner_begin(size / 3 >= 1 ? size / 3 - 1 : 0), body(nullptr) { -} + : baseList(std::move(alloc)), size_(size), + max_size(size * 4 / 3), inner_begin(size / 3 >= 1 ? size / 3 - 1 : 0), body(this->allocate(this->max_size)) { + } template original::vector::vector(const vector& other) : vector(){ @@ -666,6 +720,17 @@ template } } + template + original::vector::vector(arrayView view, ALLOC alloc) : vector(view.count(), std::move(alloc)) + { + for (u_integer i = 0; i < this->max_size; ++i) { + if (i >= this->inner_begin && i < this->inner_begin + this->size()) + this->construct(&this->body[i], view[i - this->inner_begin]); + else + this->construct(&this->body[i], TYPE{}); + } + } + template auto original::vector::operator=(const vector& other) -> vector& { @@ -741,6 +806,47 @@ template return this->body[this->toInnerIdx(0)]; } + template + original::arrayView original::vector::view() + { + return arrayView{&this->data(), this->size()}; + } + + template + original::arrayView original::vector::slice(u_integer start, u_integer end) + { + if (start > this->size() || end > this->size()) + throw outOfBoundError( + printable::formatStrings("Slice range [", start, ":", end, + "] out of bounds [0:", this->size(), "].")); + + if (start >= end) + return arrayView{}; + + return arrayView{&this->data() + start, end - start}; + } + + template + original::arrayView original::vector::view() const + { + return arrayView{&this->data(), this->size()}; + } + + template + original::arrayView + original::vector::slice(u_integer start, u_integer end) const + { + if (start > this->size() || end > this->size()) + throw outOfBoundError( + printable::formatStrings("Slice range [", start, ":", end, + "] out of bounds [0:", this->size(), "].")); + + if (start >= end) + return arrayView{}; + + return arrayView{&this->data() + start, end - start}; + } + template auto original::vector::get(integer index) const -> TYPE { diff --git a/src/vibrant/async.h b/src/vibrant/async.h index 5312a7c..7bc0e15 100644 --- a/src/vibrant/async.h +++ b/src/vibrant/async.h @@ -31,8 +31,8 @@ namespace original { class asyncWrapper { atomic ready_{makeAtomic(false)}; ///< Atomic flag indicating result readiness strongPtr result_; ///< Storage of asynchronous computation result - mutable pCondition cond_{}; ///< Condition variable for synchronization - mutable pMutex mutex_{}; ///< Mutex for thread safety + mutable condition cond_{}; ///< Condition variable for synchronization + mutable mutex mutex_{}; ///< Mutex for thread safety std::exception_ptr e_{}; ///< Exception pointer for error handling public: @@ -48,7 +48,7 @@ namespace original { * @brief Sets an exception and marks as ready * @param e Exception pointer to store */ - void setException(std::exception_ptr e); + void setException(const std::exception_ptr& e); /** * @brief Checks if the result is ready @@ -66,7 +66,7 @@ namespace original { * @param timeout Maximum time to wait * @return True if result is ready within timeout, false otherwise */ - bool waitFor(time::duration timeout) const; + [[nodiscard]] bool waitFor(time::duration timeout) const; /** * @brief Retrieves the result value (blocks until ready) @@ -86,7 +86,7 @@ namespace original { * @brief Gets a strong pointer to the result value * @return Strong pointer to the result */ - strongPtr getPtr() const; + [[nodiscard]] strongPtr getPtr() const; /** * @brief Throws stored exception if present @@ -487,7 +487,7 @@ namespace original { * @return A new future holding the result of the callback */ template - auto operator|(async::sharedFuture sf, Callback&& c); + auto operator|(const async::sharedFuture& sf, Callback&& c); /** * @brief Pipe operator specialization for sharedFuture @@ -501,7 +501,7 @@ namespace original { * @return A new future holding the result of the callback */ template - auto operator|(async::sharedFuture sf, Callback&& c); + auto operator|(const async::sharedFuture& sf, Callback&& c); /** * @brief Lazy pipe operator for chaining promise computations @@ -556,8 +556,8 @@ namespace original { class async::asyncWrapper { atomic ready_{makeAtomic(false)}; ///< Atomic flag indicating completion alternative result_; ///< Mark of asynchronous computation status - mutable pCondition cond_{}; ///< Condition variable for synchronization - mutable pMutex mutex_{}; ///< Mutex for thread safety + mutable condition cond_{}; ///< Condition variable for synchronization + mutable mutex mutex_{}; ///< Mutex for thread safety std::exception_ptr e_{}; ///< Exception pointer for error handling public: @@ -572,7 +572,7 @@ namespace original { * @brief Sets an exception and marks as completed * @param e Exception pointer to store */ - void setException(std::exception_ptr e); + void setException(const std::exception_ptr& e); /** * @brief Checks if the computation is completed @@ -590,7 +590,7 @@ namespace original { * @param timeout Maximum time to wait * @return True if result is ready within timeout, false otherwise */ - bool waitFor(const time::duration& timeout) const; + [[nodiscard]] bool waitFor(const time::duration& timeout) const; /** * @brief Waits for completion and checks for exceptions @@ -838,11 +838,11 @@ void original::async::asyncWrapper::setValue(TYPE&& v) } template -void original::async::asyncWrapper::setException(std::exception_ptr e) +void original::async::asyncWrapper::setException(const std::exception_ptr &e) { { uniqueLock lock{this->mutex_}; - this->e_ = std::move(e); + this->e_ = e; this->ready_.store(true); } this->cond_.notifyAll(); @@ -1194,7 +1194,7 @@ auto original::operator|(async::future f, Callback&& c) } template -auto original::operator|(async::sharedFuture sf, Callback&& c) +auto original::operator|(const async::sharedFuture& sf, Callback&& c) { using ResultType = std::invoke_result_t; return async::get([sf, c = std::forward(c)] mutable -> ResultType { @@ -1203,7 +1203,7 @@ auto original::operator|(async::sharedFuture sf, Callback&& c) } template -auto original::operator|(async::sharedFuture sf, Callback&& c) +auto original::operator|(const async::sharedFuture& sf, Callback&& c) { using ResultType = std::invoke_result_t; return async::get([sf, c = std::forward(c)]() mutable -> ResultType { @@ -1249,11 +1249,11 @@ inline void original::async::asyncWrapper::setValue() this->cond_.notifyAll(); } -inline void original::async::asyncWrapper::setException(std::exception_ptr e) +inline void original::async::asyncWrapper::setException(const std::exception_ptr& e) { { uniqueLock lock{this->mutex_}; - this->e_ = std::move(e); + this->e_ = e; this->ready_.store(true); } this->cond_.notifyAll(); diff --git a/src/vibrant/atomic.h b/src/vibrant/atomic.h index 37d482d..fdb5e29 100644 --- a/src/vibrant/atomic.h +++ b/src/vibrant/atomic.h @@ -1,10 +1,41 @@ #ifndef ORIGINAL_ATOMIC_H #define ORIGINAL_ATOMIC_H -#include +/** + * @file atomic.h + * @brief Cross-platform atomic operations implementation + * @details Provides type-safe atomic operations with automatic lock-free detection: + * - Lock-free implementation for supported types using hardware atomic instructions + * - Mutex-based fallback for non-lock-free types + * - Consistent API across different platforms and compilers + * + * Platform Support: + * - GCC/Clang: Uses GCC __atomic builtins for lock-free operations (pMutex for fallback) + * - MSVC: Uses Windows Interlocked APIs for lock-free operations (wMutex for fallback) + * - All platforms: Automatic selection between lock-free and mutex-based implementations + * + * Key Features: + * - Automatic lock-free detection based on type characteristics + * - Full memory ordering support (relaxed, acquire, release, acq_rel, seq_cst) + * - Atomic arithmetic operations (add, subtract) + * - Compare-and-exchange (CAS) operations + * - Type-safe interface with operator overloading + * - Factory functions for convenient object creation + */ + +#include "config.h" + +#if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG #include +#elif ORIGINAL_COMPILER_MSVC +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + #include "optional.h" -#include "config.h" #include "mutex.h" namespace original { @@ -62,7 +93,7 @@ namespace original { */ template class atomicImpl { - alignas(TYPE) byte data_[sizeof(TYPE)]; ///< Properly aligned storage + alignas(TYPE) byte data_[sizeof(TYPE)]{}; ///< Properly aligned storage /** * @brief Default constructor (zero-initializes storage) @@ -126,7 +157,7 @@ namespace original { * @brief Assignment operator (atomically stores value) * @param value Value to store */ - void operator=(TYPE value) noexcept; + atomicImpl& operator=(TYPE value) noexcept; /** * @brief Atomic addition assignment @@ -246,7 +277,7 @@ namespace original { * @brief Assignment operator (atomically stores value) * @param value Value to store */ - void operator=(TYPE value) noexcept; + atomicImpl& operator=(TYPE value) noexcept; /** * @brief Atomic addition assignment @@ -289,7 +320,378 @@ namespace original { template friend auto makeAtomic(T value); }; +#elif ORIGINAL_COMPILER_MSVC + /** + * @enum memOrder + * @brief Memory ordering constraints for atomic operations (MSVC implementation) + * @details Specifies the memory synchronization behavior of atomic operations: + * - RELAXED: No ordering constraints, only atomicity guaranteed + * - ACQUIRE: Subsequent reads cannot be reordered before this operation + * - RELEASE: Previous writes cannot be reordered after this operation + * - ACQ_REL: Combination of ACQUIRE and RELEASE semantics + * - SEQ_CST: Sequential consistency (strongest ordering) + * + * @note MSVC implementation uses different underlying mechanisms than GCC: + * - RELAXED/ACQUIRE/RELEASE: Use compiler barriers (_ReadBarrier/_WriteBarrier) + * - SEQ_CST: Uses Interlocked APIs for full memory ordering + * @note Only available when compiled with MSVC + */ + enum class memOrder { + RELAXED, + ACQUIRE, + RELEASE, + ACQ_REL, + SEQ_CST, + }; + + /** + * @brief Determines if a type can be implemented lock-free on MSVC + * @tparam TYPE The type to check for lock-free support + * @return true if type can be implemented lock-free, false otherwise + * @details MSVC supports lock-free operations for 4-byte and 8-byte types + * using Interlocked APIs. Other sizes require mutex-based implementation. + * @implementation Checks if type size is 4 or 8 bytes + * @note Only available when compiled with MSVC + */ + template + constexpr bool isLockFreeType() noexcept; + + template + class atomicImpl; + + template + using atomic = atomicImpl()>; + + /** + * @class atomicImpl + * @brief Lock-free atomic implementation using MSVC Interlocked APIs + * @tparam TYPE The underlying atomic type (must be 4 or 8 bytes) + * @details Provides atomic operations using Windows Interlocked functions + * for types that support hardware-level atomic operations. + * + * Platform-Specific Features: + * - Uses InterlockedExchange for atomic stores with SEQ_CST ordering + * - Uses InterlockedCompareExchange for atomic loads with SEQ_CST ordering + * - Uses InterlockedAdd for atomic arithmetic operations + * - Uses InterlockedCompareExchange for CAS operations + * - Uses compiler barriers (_ReadBarrier/_WriteBarrier) for weaker ordering + * + * Supported Type Sizes: + * - 4-byte types: Uses InterlockedExchange, InterlockedAdd, etc. + * - 8-byte types: Uses InterlockedExchange64, InterlockedAdd64, etc. + * + * @note Only available when compiled with MSVC + * @note Only supports 4-byte and 8-byte types (checked by isLockFreeType) + * @note For other type sizes, falls back to mutex-based implementation + * + * Example usage: + * @code + * // 4-byte integer (lock-free) + * original::atomic atomic_int(42); + * atomic_int.store(100); + * int value = atomic_int.load(); + * + * // 8-byte integer (lock-free) + * original::atomic atomic_ll(123LL); + * atomic_ll += 10; + * @endcode + */ + template + class atomicImpl + { + /// @brief Internal value type selection based on size + using val_type = some_t; + + /// @brief Properly aligned atomic storage + alignas(TYPE) volatile TYPE data_{}; + + /** + * @brief Type conversion helper for atomic operations + * @tparam From Source type + * @tparam To Target type + * @param value Value to convert + * @return Converted value + * @implementation Uses reinterpret_cast for pointers, static_cast for values + */ + template + static To atomicCastTo(From value); + + /** + * @brief Reverse type conversion helper + * @tparam To Target type + * @tparam From Source type + * @param value Value to convert back + * @return Original type value + */ + template + static To atomicCastBack(From value); + + /** + * @brief Default constructor (zero-initializes storage) + * @implementation Uses memset for proper initialization + */ + atomicImpl(); + + /** + * @brief Value constructor with memory ordering + * @param value Initial value to store + * @param order Memory ordering constraint (default: SEQ_CST) + * @implementation Delegates to store() method + */ + explicit atomicImpl(TYPE value, memOrder order = SEQ_CST); + + public: + // Memory ordering constants for convenience + static constexpr auto RELAXED = memOrder::RELAXED; + static constexpr auto ACQUIRE = memOrder::ACQUIRE; + static constexpr auto RELEASE = memOrder::RELEASE; + static constexpr auto ACQ_REL = memOrder::ACQ_REL; + static constexpr auto SEQ_CST = memOrder::SEQ_CST; + + // Disable copying and moving + atomicImpl(const atomicImpl&) = delete; + atomicImpl(atomicImpl&&) = delete; + atomicImpl& operator=(const atomicImpl&) = delete; + atomicImpl& operator=(atomicImpl&&) = delete; + + /** + * @brief Checks if the atomic implementation is lock-free + * @return Always true for this specialization + * @implementation Returns true since this is the lock-free specialization + */ + static constexpr bool isLockFree() noexcept; + + /** + * @brief Atomically stores a value with specified memory ordering + * @param value Value to store + * @param order Memory ordering constraint (default: SEQ_CST) + * @implementation MSVC-specific: + * - RELAXED: Direct assignment + * - RELEASE: _WriteBarrier + direct assignment + * - SEQ_CST: InterlockedExchange/InterlockedExchange64 + * - ACQUIRE/ACQ_REL: Not supported for stores, uses SEQ_CST + */ + void store(TYPE value, memOrder order = SEQ_CST); + + /** + * @brief Atomically loads the current value with specified memory ordering + * @param order Memory ordering constraint (default: SEQ_CST) + * @return The current atomic value + * @implementation MSVC-specific: + * - RELAXED: Direct read + * - ACQUIRE: Direct read + _ReadBarrier + * - SEQ_CST: InterlockedCompareExchange with 0 + * - RELEASE/ACQ_REL: Not supported for loads, uses SEQ_CST + */ + TYPE load(memOrder order = SEQ_CST) const noexcept; + + /** + * @brief Dereference operator (loads current value with SEQ_CST ordering) + * @return The current atomic value + */ + TYPE operator*() const noexcept; + + /** + * @brief Conversion operator to underlying type + * @return The current atomic value + */ + explicit operator TYPE() const noexcept; + + /** + * @brief Assignment operator (atomically stores value with SEQ_CST ordering) + * @param value Value to store + */ + atomicImpl& operator=(TYPE value) noexcept; + + /** + * @brief Atomic addition assignment + * @param value Value to add + * @return Reference to this atomic object + * @implementation Uses InterlockedAdd/InterlockedAdd64 + */ + atomicImpl& operator+=(TYPE value) noexcept; + + /** + * @brief Atomic subtraction assignment + * @param value Value to subtract + * @return Reference to this atomic object + * @implementation Implements as addition of negative value + */ + atomicImpl& operator-=(TYPE value) noexcept; + + /** + * @brief Atomically exchanges value + * @param value New value to store + * @param order Memory ordering constraint (default: SEQ_CST) + * @return The previous value + * @implementation Uses InterlockedExchange/InterlockedExchange64 + */ + TYPE exchange(TYPE value, memOrder order = SEQ_CST) noexcept; + + /** + * @brief Atomically compares and exchanges value (CAS operation) + * @param expected Expected current value (updated if comparison fails) + * @param desired Desired new value + * @param order Memory ordering constraint (default: SEQ_CST) + * @return True if exchange was successful, false otherwise + * @implementation Uses InterlockedCompareExchange/InterlockedCompareExchange64 + */ + bool exchangeCmp(TYPE& expected, TYPE desired, memOrder order = SEQ_CST) noexcept; + + /// @brief Default destructor + ~atomicImpl() = default; + + // Friend factory functions + template + friend auto makeAtomic(); + + template + friend auto makeAtomic(T value); + }; + + /** + * @class atomicImpl + * @brief Mutex-based atomic implementation for non-lock-free types on MSVC + * @tparam TYPE The underlying atomic type + * @details Provides atomic operations using Windows SRW locks for types that + * cannot be handled by hardware atomic operations. + * + * Platform-Specific Features: + * - Uses wMutex (Windows SRW lock) for synchronization + * - Provides same API as lock-free implementation but with locking + * - Memory ordering parameters are ignored (all operations are sequentially consistent) + * + * @note Only available when compiled with MSVC + * @note Used for types that are not 4 or 8 bytes in size + * @note All operations acquire the mutex, providing full sequential consistency + * + * Example usage: + * @code + * // Large struct (requires mutex-based implementation) + * struct LargeData { char data[128]; }; + * original::atomic atomic_data; + * atomic_data.store(LargeData{}); + * LargeData value = atomic_data.load(); + * @endcode + */ + template + class atomicImpl + { + mutable wMutex mutex_; ///< Windows SRW lock for synchronization + alternative data_; ///< Optional storage for the value + + /** + * @brief Default constructor + */ + atomicImpl() = default; + + /** + * @brief Value constructor + * @param value Initial value to store + * @param order Memory ordering (ignored in mutex implementation) + */ + explicit atomicImpl(TYPE value, memOrder order = RELEASE); + + public: + // Memory ordering constants (for API compatibility) + static constexpr auto RELAXED = memOrder::RELAXED; + static constexpr auto ACQUIRE = memOrder::ACQUIRE; + static constexpr auto RELEASE = memOrder::RELEASE; + static constexpr auto ACQ_REL = memOrder::ACQ_REL; + static constexpr auto SEQ_CST = memOrder::SEQ_CST; + + // Disable copying and moving + atomicImpl(const atomicImpl&) = delete; + atomicImpl(atomicImpl&&) = delete; + atomicImpl& operator=(const atomicImpl&) = delete; + atomicImpl& operator=(atomicImpl&&) = delete; + + /** + * @brief Checks if the atomic implementation is lock-free + * @return Always false for this specialization + */ + static constexpr bool isLockFree() noexcept; + + /** + * @brief Atomically stores a value + * @param value Value to store + * @param order Memory ordering (ignored) + * @implementation Locks mutex, stores value, unlocks mutex + */ + void store(TYPE value, memOrder order = SEQ_CST); + + /** + * @brief Atomically loads the current value + * @param order Memory ordering (ignored) + * @return The current atomic value + * @implementation Locks mutex, reads value, unlocks mutex + */ + TYPE load(memOrder order = SEQ_CST) const noexcept; + + /** + * @brief Dereference operator (loads current value) + * @return The current atomic value + */ + TYPE operator*() const noexcept; + + /** + * @brief Conversion operator to underlying type + * @return The current atomic value + */ + explicit operator TYPE() const noexcept; + /** + * @brief Assignment operator (atomically stores value) + * @param value Value to store + */ + atomicImpl& operator=(TYPE value) noexcept; + + /** + * @brief Atomic addition assignment + * @param value Value to add + * @return Reference to this atomic object + * @implementation Locks mutex, performs addition, stores result, unlocks mutex + */ + atomicImpl& operator+=(TYPE value) noexcept; + + /** + * @brief Atomic subtraction assignment + * @param value Value to subtract + * @return Reference to this atomic object + * @implementation Locks mutex, performs subtraction, stores result, unlocks mutex + */ + atomicImpl& operator-=(TYPE value) noexcept; + + /** + * @brief Atomically exchanges value + * @param value New value to store + * @param order Memory ordering (ignored) + * @return The previous value + * @implementation Locks mutex, exchanges values, unlocks mutex + */ + TYPE exchange(const TYPE& value, memOrder order = SEQ_CST) noexcept; + + /** + * @brief Atomically compares and exchanges value (CAS operation) + * @param expected Expected current value (updated if comparison fails) + * @param desired Desired new value + * @param order Memory ordering (ignored) + * @return True if exchange was successful, false otherwise + * @implementation Locks mutex, compares values, exchanges if equal, unlocks mutex + */ + bool exchangeCmp(TYPE& expected, const TYPE& desired, memOrder order = SEQ_CST) noexcept; + + /// @brief Default destructor + ~atomicImpl() = default; + + // Friend factory functions + template + friend auto makeAtomic(); + + template + friend auto makeAtomic(T value); + }; +#endif // ==================== Factory Functions ==================== /** @@ -311,6 +713,7 @@ namespace original { } // namespace original +#if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG template original::atomicImpl::atomicImpl() { std::memset(this->data_, byte{}, sizeof(TYPE)); @@ -351,9 +754,10 @@ original::atomicImpl::operator TYPE() const noexcept } template -void original::atomicImpl::operator=(TYPE value) noexcept +original::atomicImpl& original::atomicImpl::operator=(TYPE value) noexcept { this->store(std::move(value)); + return *this; } template @@ -422,9 +826,271 @@ original::atomicImpl::operator TYPE() const noexcept } template -void original::atomicImpl::operator=(TYPE value) noexcept +original::atomicImpl& +original::atomicImpl::operator=(TYPE value) noexcept +{ + this->store(std::move(value)); + return *this; +} + +template +original::atomicImpl& original::atomicImpl::operator+=(TYPE value) noexcept +{ + uniqueLock lock{this->mutex_}; + TYPE result = *this->data_ + value; + this->data_.set(result); + return *this; +} + +template +original::atomicImpl& original::atomicImpl::operator-=(TYPE value) noexcept +{ + uniqueLock lock{this->mutex_}; + TYPE result = *this->data_ - value; + this->data_.set(result); + return *this; +} + +template +TYPE original::atomicImpl::exchange(const TYPE& value, memOrder) noexcept { + uniqueLock lock{this->mutex_}; + TYPE result = *this->data_; + this->data_.set(value); + return result; +} + +template +bool original::atomicImpl::exchangeCmp(TYPE& expected, const TYPE& desired, memOrder) noexcept { + uniqueLock lock{this->mutex_}; + if (*this->data_ == expected) { + this->data_.set(desired); + return true; + } + expected = *this->data_; + return false; +} +#elif ORIGINAL_COMPILER_MSVC + +template +constexpr bool original::isLockFreeType() noexcept { + return sizeof(TYPE) == 4 || sizeof(TYPE) == 8; +} + +template +template +To original::atomicImpl::atomicCastTo(From value) +{ + if constexpr (std::is_pointer_v) { + return reinterpret_cast(value); + } else { + return static_cast(value); + } +} + +template +template +To original::atomicImpl::atomicCastBack(From value) +{ + if constexpr (std::is_pointer_v) { + return reinterpret_cast(value); + } else { + return static_cast(value); + } +} + +template +original::atomicImpl::atomicImpl() +{ + std::memset(this->data_, byte{}, sizeof(TYPE)); +} + +template +original::atomicImpl::atomicImpl(TYPE value, const memOrder order) +{ + this->store(value, order); +} + +template +constexpr bool +original::atomicImpl::isLockFree() noexcept +{ + return true; +} + +template +void original::atomicImpl::store(TYPE value, const memOrder order) +{ + switch (order) { + case memOrder::RELAXED: + this->data_ = value; + break; + case memOrder::RELEASE: + _WriteBarrier(); + this->data_ = value; + break; + case memOrder::SEQ_CST: + default: + if constexpr (sizeof(TYPE) == 4) { + InterlockedExchange(reinterpret_cast(&this->data_), + atomicCastTo(value)); + } else { + InterlockedExchange64(reinterpret_cast(&this->data_), + atomicCastTo(value)); + } + break; + } +} + +template +TYPE original::atomicImpl::load(const memOrder order) const noexcept +{ + TYPE value; + switch (order) { + case memOrder::RELAXED: + value = this->data_; + break; + case memOrder::ACQUIRE: + value = this->data_; + _ReadBarrier(); + break; + case memOrder::SEQ_CST: + default: + if constexpr (sizeof(TYPE) == 4) { + return atomicCastBack( + InterlockedCompareExchange( + reinterpret_cast(const_cast(&this->data_)), + 0, 0)); + } else { + return atomicCastBack( + InterlockedCompareExchange64( + reinterpret_cast(const_cast(&this->data_)), + 0, 0)); + } + break; + } + return value; +} + +template +TYPE original::atomicImpl::operator*() const noexcept +{ + return this->load(); +} + +template +original::atomicImpl::operator TYPE() const noexcept +{ + return this->load(); +} + +template +original::atomicImpl& +original::atomicImpl::operator=(TYPE value) noexcept +{ + this->store(std::move(value)); + return *this; +} + +template +original::atomicImpl& +original::atomicImpl::operator+=(TYPE value) noexcept +{ + if constexpr (sizeof(TYPE) == 4) { + InterlockedAdd(reinterpret_cast(&this->data_), + atomicCastTo(value)); + } else { + InterlockedAdd64(reinterpret_cast(&this->data_), + atomicCastTo(value)); + } + return *this; +} + +template +original::atomicImpl& +original::atomicImpl::operator-=(TYPE value) noexcept +{ + return *this += -value; +} + +template +TYPE original::atomicImpl::exchange(TYPE value, memOrder) noexcept +{ + if constexpr (sizeof(TYPE) == 4) { + return atomicCastBack( + InterlockedExchange(reinterpret_cast(&this->data_), + atomicCastTo(value)) + ); + } else { + return atomicCastBack( + InterlockedExchange64(reinterpret_cast(&this->data_), + atomicCastTo(value)) + ); + } +} + +template +bool original::atomicImpl::exchangeCmp(TYPE& expected, TYPE desired, memOrder) noexcept +{ + if constexpr (sizeof(TYPE) == 4) { + val_type old = InterlockedCompareExchange( + reinterpret_cast(&this->data_), + atomicCastTo(desired), + atomicCastTo(expected)); + const bool success = old == atomicCastTo(expected); + if (!success) expected = atomicCastBack(old); + return success; + } else { + val_type old = InterlockedCompareExchange64( + reinterpret_cast(&this->data_), + atomicCastTo(desired), + atomicCastTo(expected)); + const bool success = old == atomicCastTo(expected); + if (!success) expected = atomicCastBack(old); + return success; + } +} + +template +original::atomicImpl::atomicImpl(TYPE value, memOrder) { + uniqueLock lock{this->mutex_}; + this->data_.set(value); +} + +template +constexpr bool original::atomicImpl::isLockFree() noexcept { + return false; +} + +template +void original::atomicImpl::store(TYPE value, memOrder) { + uniqueLock lock{this->mutex_}; + this->data_.set(value); +} + +template +TYPE original::atomicImpl::load(memOrder) const noexcept { + uniqueLock lock{this->mutex_}; + return *this->data_; +} + +template +TYPE original::atomicImpl::operator*() const noexcept +{ + return this->load(); +} + +template +original::atomicImpl::operator TYPE() const noexcept +{ + return this->load(); +} + +template +original::atomicImpl& +original::atomicImpl::operator=(TYPE value) noexcept { this->store(std::move(value)); + return *this; } template @@ -464,6 +1130,8 @@ bool original::atomicImpl::exchangeCmp(TYPE& expected, const TYPE& d return false; } +#endif + template auto original::makeAtomic() { @@ -476,4 +1144,3 @@ auto original::makeAtomic(TYPE value) return atomic{std::move(value)}; } #endif -#endif diff --git a/src/vibrant/condition.h b/src/vibrant/condition.h index 02309ec..2c71638 100644 --- a/src/vibrant/condition.h +++ b/src/vibrant/condition.h @@ -1,11 +1,19 @@ #ifndef CONDITION_H #define CONDITION_H + +#include "config.h" + +#if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG +#include +#elif ORIGINAL_COMPILER_MSVC +#endif + #include "mutex.h" #include "zeit.h" /** * @file condition.h - * @brief Condition variable implementation for thread synchronization + * @brief Cross-platform condition variable implementation for thread synchronization * @details Provides condition variable functionality for coordinating * between threads, including: * - Basic wait/notify operations @@ -13,11 +21,18 @@ * - Predicate-based waiting * - Integration with mutex.h locking mechanisms * + * Platform Support: + * - GCC/Clang: Uses pthread_cond_t for condition variable implementation (pCondition) + * - MSVC: Uses CONDITION_VARIABLE for lightweight implementation (wCondition) + * - All platforms: High-level condition class with consistent interface + * * Key features: - * - POSIX-based implementation (pCondition) + * - POSIX-based implementation for GCC/Clang + * - Windows CONDITION_VARIABLE implementation for MSVC * - Thread-safe condition variable operations * - Timeout support using zeit.h duration types * - Predicate templates for safe condition checking + * - Spurious wakeup protection through predicate loops */ namespace original @@ -118,12 +133,45 @@ namespace original conditionBase& operator=(const conditionBase&) = delete; }; +#if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG /** * @class pCondition - * @brief POSIX condition variable implementation + * @brief POSIX condition variable implementation for GCC and Clang compilers * @extends conditionBase * @details Wrapper around pthread_cond_t with RAII semantics. * Provides thread synchronization using POSIX condition variables. + * + * Platform-Specific Features: + * - Uses pthread_cond_init for initialization + * - Implements pthread_cond_wait for blocking waits + * - Uses pthread_cond_timedwait for timed waits with absolute time + * - Provides pthread_cond_signal for single thread notification + * - Uses pthread_cond_broadcast for all threads notification + * - Calls pthread_cond_destroy for cleanup in destructor + * + * @note Only available when compiled with GCC or Clang + * @note Requires POSIX mutex (pMutex) for proper operation + * @note Timed waits use absolute time (CLOCK_REALTIME) for precision + * + * Example usage: + * @code + * original::pCondition cond; + * original::pMutex mtx; + * + * // Waiting thread + * { + * original::uniqueLock lock(mtx); + * cond.wait(mtx, [&](){ return data_ready; }); + * // Process data + * } + * + * // Notifying thread + * { + * original::uniqueLock lock(mtx); + * data_ready = true; + * cond.notify(); + * } + * @endcode */ class pCondition final : public conditionBase { @@ -135,49 +183,254 @@ namespace original using conditionBase::waitFor; /** - * @brief Constructs and initializes the condition variable - * @throws sysError if initialization fails + * @brief Constructs and initializes the POSIX condition variable + * @throws sysError if pthread_cond_init fails + * @note Uses default condition variable attributes + * @details Calls pthread_cond_init with nullptr attributes */ explicit pCondition(); /** * @brief Waits for notification while holding the mutex - * @param mutex Locked mutex to wait on - * @throws sysError if wait operation fails + * @param mutex Locked mutex to wait on (must be pMutex) + * @throws sysError if pthread_cond_wait fails * @throws valueError if mutex is not a pMutex + * @note The mutex must be locked by the calling thread + * @details Uses static type checking to ensure mutex compatibility */ void wait(mutexBase& mutex) override; /** * @brief Waits for notification with timeout - * @param mutex Locked mutex to wait on + * @param mutex Locked mutex to wait on (must be pMutex) * @param d Maximum duration to wait - * @return true if notified, false if timeout occurred - * @throws sysError if wait operation fails + * @return true if notified, false if timeout occurred (ETIMEDOUT) + * @throws sysError if pthread_cond_timedwait fails * @throws valueError if mutex is not a pMutex + * @note Uses absolute time calculation for precise timeout handling + * @details Converts duration to timespec for pthread_cond_timedwait */ bool waitFor(mutexBase& mutex, time::duration d) override; /** * @brief Notifies one waiting thread - * @throws sysError if notification fails + * @throws sysError if pthread_cond_signal fails + * @details Calls pthread_cond_signal */ void notify() override; /** * @brief Notifies all waiting threads - * @throws sysError if notification fails + * @throws sysError if pthread_cond_broadcast fails + * @details Calls pthread_cond_broadcast */ void notifyAll() override; /** * @brief Destroys the condition variable - * @note Calls pthread_cond_destroy() + * @note Logs warning but doesn't throw if destruction fails + * @details Calls pthread_cond_destroy */ ~pCondition() override; }; -} +#elif ORIGINAL_COMPILER_MSVC + /** + * @class wCondition + * @brief Windows condition variable implementation for MSVC compiler + * @extends conditionBase + * @details Wrapper around CONDITION_VARIABLE with RAII semantics. + * Provides thread synchronization using Windows condition variables. + * + * Platform-Specific Features: + * - Uses InitializeConditionVariable for initialization + * - Implements SleepConditionVariableSRW for blocking waits + * - Uses SleepConditionVariableSRW with timeout for timed waits + * - Provides WakeConditionVariable for single thread notification + * - Uses WakeAllConditionVariable for all threads notification + * - No explicit destruction required (Windows manages resources) + * + * @note Only available when compiled with MSVC + * @note Requires SRW lock (wMutex) for proper operation + * @note Timed waits use relative time in milliseconds + * @note More efficient than POSIX condition variables on Windows + * + * Example usage: + * @code + * original::wCondition cond; + * original::wMutex mtx; + * + * // Waiting thread + * { + * original::uniqueLock lock(mtx); + * cond.wait(mtx, [&](){ return data_ready; }); + * // Process data + * } + * + * // Notifying thread + * { + * original::uniqueLock lock(mtx); + * data_ready = true; + * cond.notify(); + * } + * @endcode + */ + class wCondition final : public conditionBase + { + CONDITION_VARIABLE cond_; ///< Windows condition variable handle + + public: + // Inherit template methods from conditionBase + using conditionBase::wait; + using conditionBase::waitFor; + + /** + * @brief Constructs and initializes the Windows condition variable + * @implementation Calls InitializeConditionVariable + * @note Windows condition variables require no explicit destruction + */ + explicit wCondition(); + + /** + * @brief Waits for notification while holding the mutex + * @param mutex Locked mutex to wait on (must be wMutex) + * @throws sysError if SleepConditionVariableSRW fails + * @note Uses INFINITE timeout for blocking wait + * @implementation Calls SleepConditionVariableSRW with INFINITE timeout + */ + void wait(mutexBase& mutex) override; + + /** + * @brief Waits for notification with timeout + * @param mutex Locked mutex to wait on (must be wMutex) + * @param d Maximum duration to wait + * @return true if notified, false if timeout occurred (ERROR_TIMEOUT) + * @throws sysError if SleepConditionVariableSRW fails (other than timeout) + * @note Converts duration to milliseconds for Windows API + * @implementation Uses SleepConditionVariableSRW with millisecond timeout + */ + bool waitFor(mutexBase& mutex, time::duration d) override; + + /** + * @brief Notifies one waiting thread + * @implementation Calls WakeConditionVariable + */ + void notify() override; + + /** + * @brief Notifies all waiting threads + * @implementation Calls WakeAllConditionVariable + */ + void notifyAll() override; + + /** + * @brief Destructor + * @note No explicit cleanup required for Windows condition variables + * @implementation Windows automatically manages condition variable resources + */ + ~wCondition() override; + }; +#endif + + /** + * @class condition + * @brief High-level cross-platform condition variable wrapper + * @extends conditionBase + * @details Provides a unified condition variable interface that automatically + * selects the appropriate platform-specific implementation: + * - pCondition for GCC/Clang on Linux/macOS (POSIX condition variables) + * - wCondition for MSVC on Windows (Windows condition variables) + * + * Key Features: + * - Consistent API across all platforms + * - RAII semantics with automatic cleanup + * - Exception-safe operations + * - Delegates all operations to platform-specific implementation + * - Inherits predicate-based waiting templates from conditionBase + * + * Platform Abstraction: + * - GCC/Clang: Delegates to pCondition (pthread_cond_t) + * - MSVC: Delegates to wCondition (CONDITION_VARIABLE) + * - All platforms: Identical interface and behavior + * + * Example usage: + * @code + * original::condition cond; + * original::mutex mtx; + * bool data_ready = false; + * + * // Waiting thread - works the same on all platforms + * { + * original::uniqueLock lock(mtx); + * cond.wait(lock, [&](){ return data_ready; }); + * // Critical section - condition satisfied + * } + * + * // Notifying thread - works the same on all platforms + * { + * original::uniqueLock lock(mtx); + * data_ready = true; + * cond.notify(); // or cond.notifyAll() for multiple waiters + * } + * @endcode + * + * @see original::pCondition (GCC/Clang implementation) + * @see original::wCondition (MSVC implementation) + * @see original::conditionBase + * @see original::uniqueLock + */ + class condition final : public conditionBase { + #if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG + pCondition cond_; ///< POSIX condition variable implementation (GCC/Clang) + #elif ORIGINAL_COMPILER_MSVC + wCondition cond_; ///< Windows condition variable implementation (MSVC) + #endif + + public: + // Inherit template methods from conditionBase + using conditionBase::wait; + using conditionBase::waitFor; + + /** + * @brief Constructs the platform-specific condition variable + * @implementation Delegates to pCondition or wCondition constructor + */ + condition(); + + /** + * @brief Waits for notification while holding the mutex + * @param mutex Locked mutex to wait on + * @throws sysError if wait operation fails + * @implementation Delegates to underlying condition implementation + */ + void wait(mutexBase& mutex) override; + + /** + * @brief Waits for notification with timeout + * @param mutex Locked mutex to wait on + * @param d Maximum duration to wait + * @return true if notified, false if timeout occurred + * @throws sysError if wait operation fails + * @implementation Delegates to underlying condition implementation + */ + bool waitFor(mutexBase& mutex, time::duration d) override; + + /** + * @brief Notifies one waiting thread + * @throws sysError if notification fails + * @implementation Delegates to underlying condition implementation + */ + void notify() override; + + /** + * @brief Notifies all waiting threads + * @throws sysError if notification fails + * @implementation Delegates to underlying condition implementation + */ + void notifyAll() override; + }; + +} template void original::conditionBase::wait(mutexBase& mutex, Pred predicate) noexcept(noexcept(predicate())) { while (!predicate()){ @@ -211,6 +464,7 @@ inline void original::conditionBase::notifySome(const u_integer n) { } } +#if ORIGINAL_COMPILER_GCC || ORIGINAL_COMPILER_CLANG inline original::pCondition::pCondition() : cond_{} { if (const int code = pthread_cond_init(&this->cond_, nullptr); code != 0) @@ -221,12 +475,9 @@ inline original::pCondition::pCondition() : cond_{} inline void original::pCondition::wait(mutexBase& mutex) { - const auto p_mutex = dynamic_cast(&mutex); - if (!p_mutex) { - throw valueError("Invalid mutex type for condition variable: must be pMutex"); - } + staticError>::asserts(); - const auto handle = static_cast(p_mutex->nativeHandle()); + const auto handle = static_cast(mutex.nativeHandle()); if (const int code = pthread_cond_wait(&this->cond_, handle); code != 0) { throw sysError("Failed to wait on condition variable (pthread_cond_wait returned " + printable::formatString(code) + ")"); } @@ -234,14 +485,11 @@ inline void original::pCondition::wait(mutexBase& mutex) inline bool original::pCondition::waitFor(mutexBase& mutex, const time::duration d) { - const auto p_mutex = dynamic_cast(&mutex); - if (!p_mutex) { - throw valueError("Invalid mutex type for condition variable: must be pMutex"); - } + staticError>::asserts(); const auto deadline = time::point::now() + d; const auto ts = deadline.toTimespec(); - const auto handle = static_cast(p_mutex->nativeHandle()); + const auto handle = static_cast(mutex.nativeHandle()); const int code = pthread_cond_timedwait(&this->cond_, handle, &ts); if (code == 0) return true; if (code == ETIMEDOUT) return false; @@ -269,5 +517,69 @@ inline original::pCondition::~pCondition() << code << ")" << std::endl; } } +#elif ORIGINAL_COMPILER_MSVC +inline original::wCondition::wCondition() : cond_{} +{ + InitializeConditionVariable(&this->cond_); +} + +inline void original::wCondition::wait(mutexBase& mutex) +{ + if (const auto handle = static_cast(mutex.nativeHandle()); + !SleepConditionVariableSRW(&this->cond_, handle, INFINITE, 0)) { + throw sysError("Failed to wait on condition variable (SleepConditionVariableSRW returned error " + + printable::formatString(GetLastError())); + } +} + +inline bool original::wCondition::waitFor(mutexBase& mutex, const time::duration d) +{ + const auto handle = static_cast(mutex.nativeHandle()); + if (const auto timeout_ms = d.toDWMilliseconds(); + !SleepConditionVariableSRW(&this->cond_, handle, timeout_ms, 0)) { + const DWORD error = GetLastError(); + if (error == ERROR_TIMEOUT) { + return false; + } + throw sysError("Failed to timed wait on condition variable (SleepConditionVariableSRW returned error " + + printable::formatString(error)); + } + return true; +} + +inline void original::wCondition::notify() +{ + WakeConditionVariable(&this->cond_); +} + +inline void original::wCondition::notifyAll() +{ + WakeAllConditionVariable(&this->cond_); +} + +inline original::wCondition::~wCondition() = default; +#endif + +inline original::condition::condition() = default; + +inline void original::condition::wait(mutexBase& mutex) +{ + this->cond_.wait(mutex); +} + +inline bool original::condition::waitFor(mutexBase& mutex, time::duration d) +{ + return this->cond_.waitFor(mutex, d); +} + +inline void original::condition::notify() +{ + this->cond_.notify(); +} + +inline void original::condition::notifyAll() +{ + this->cond_.notifyAll(); +} #endif //CONDITION_H diff --git a/src/vibrant/coroutines.h b/src/vibrant/coroutines.h index ed28af6..108d302 100644 --- a/src/vibrant/coroutines.h +++ b/src/vibrant/coroutines.h @@ -55,6 +55,14 @@ namespace original { */ template class generator { + public: + struct promise_type; + private: + using handle = std::coroutine_handle; ///< Coroutine handle type + + handle handle_; ///< Underlying coroutine handle + + public: /** * @struct promise_type * @brief Implements the coroutine promise interface for generator @@ -168,14 +176,6 @@ namespace original { bool operator==(const iterator& other) const; }; - using handle = std::coroutine_handle; ///< Coroutine handle type - - handle handle_; ///< Underlying coroutine handle - - public: - using promise_type = promise_type; ///< Promise type for coroutine protocol - using iterator = iterator; ///< Iterator type for range operations - generator(const generator&) = delete; ///< Copy constructor deleted generator& operator=(const generator&) = delete; ///< Copy assignment deleted diff --git a/src/vibrant/generators.h b/src/vibrant/generators.h index f07bd81..d350cfd 100644 --- a/src/vibrant/generators.h +++ b/src/vibrant/generators.h @@ -3,6 +3,7 @@ #include "coroutines.h" #include "couple.h" #include "sets.h" +#include "vector.h" namespace original { @@ -27,43 +28,83 @@ namespace original { coroutine::generator> enumerate(coroutine::generator gen); /** - * @brief Collects generator elements into a set. + * @brief Reduces a generator to a single value using an accumulator function. * @tparam TYPE The type of elements in the generator. - * @tparam SET The set type to collect into (default: hashSet). - * @param gen The generator to collect from. - * @return A set containing all unique elements from the generator. - * @details Transforms a generator sequence into a set container, removing duplicates - * and providing fast lookup capabilities. + * @tparam Callback The accumulator function type. + * @param gen The generator to reduce. + * @param init Initial value for the reduction. + * @param c Binary accumulator function that combines the current result with the next element. + * @return The final reduced value. + * @details Applies the accumulator function sequentially to all elements of the generator, + * starting with the initial value. The accumulator function should have the signature + * `TYPE(TYPE accumulator, TYPE element)`. * * Example: * @code - * auto vec = vector{1, 2, 2, 3, 3, 3}; + * auto vec = vector{1, 2, 3, 4}; * auto gen = vec.generator(); - * auto set = collect(gen); // {1, 2, 3} + * auto sum = reduce(gen, 0, [](int acc, int x) { return acc + x; }); // Returns 10 * @endcode */ - template> - requires ExtendsOf>>, SET> - SET collect(coroutine::generator gen); + template + TYPE reduce(coroutine::generator gen, TYPE init, Callback&& c); /** - * @brief Collects generator elements into a list container. + * @brief Finds the maximum element in a generator. * @tparam TYPE The type of elements in the generator. - * @tparam SERIAL The list container type (default: vector). - * @param gen The generator to collect from. - * @return A list container with all generator elements in order. - * @details Converts a generator sequence into a concrete list container, - * preserving element order and allowing random access. + * @param gen The generator to search. + * @param init Initial maximum value (typically the smallest possible value). + * @return The maximum element found in the generator. + * @details Compares all elements using the `maximum` function and returns the largest one. + * If the generator is empty, returns the initial value. * * Example: * @code - * auto gen = someContainer.generator(); - * auto vec = list(gen); // Creates vector with all elements + * auto vec = vector{5, 2, 8, 1}; + * auto gen = vec.generator(); + * auto max_val = maximum(gen, 0); // Returns 8 + * @endcode + */ + template + TYPE maximum(coroutine::generator gen, TYPE init); + + /** + * @brief Finds the minimum element in a generator. + * @tparam TYPE The type of elements in the generator. + * @param gen The generator to search. + * @param init Initial minimum value (typically the largest possible value). + * @return The minimum element found in the generator. + * @details Compares all elements using the `minimum` function and returns the smallest one. + * If the generator is empty, returns the initial value. + * + * Example: + * @code + * auto vec = vector{5, 2, 8, 1}; + * auto gen = vec.generator(); + * auto min_val = minimum(gen, 100); // Returns 1 * @endcode */ - template typename SERIAL = vector> - requires ExtendsOf>, SERIAL> - SERIAL list(coroutine::generator gen); + template + TYPE minimum(coroutine::generator gen, TYPE init); + + /** + * @brief Computes the sum of all elements in a generator. + * @tparam TYPE The type of elements in the generator. + * @param gen The generator to sum. + * @param init Initial sum value (typically 0 or identity element for addition). + * @return The sum of all elements in the generator. + * @details Adds all elements of the generator together using the `+` operator. + * If the generator is empty, returns the initial value. + * + * Example: + * @code + * auto vec = vector{1, 2, 3, 4}; + * auto gen = vec.generator(); + * auto total = summation(gen, 0); // Returns 10 + * @endcode + */ + template + TYPE summation(coroutine::generator gen, TYPE init); /** * @brief Transforms generator elements using a callable. @@ -453,6 +494,24 @@ namespace original { template friend auto find(F&& f); + + template + friend auto reduce(TYPE init, F&& c); + + template + friend auto maximum(TYPE init); + + template + friend auto minimum(TYPE init); + + template + friend auto summation(TYPE init); + + template + friend auto collect(); + + template typename SERIAL> + friend auto list(); }; /** @@ -644,6 +703,122 @@ namespace original { */ template auto find(F&& f); + + /** + * @brief Creates a reduce pipe operation. + * @tparam TYPE The element type. + * @tparam Callback The reduction function type. + * @param init Initial value for reduction. + * @param c Reduction function. + * @return A genPipe that reduces the generator. + * @details Factory function for creating reduce operations. + */ + template + auto reduce(TYPE init, Callback&& c); + + /** + * @brief Creates a maximum pipe operation. + * @tparam TYPE The element type. + * @param init Initial maximum value. + * @return A genPipe that finds the maximum element. + * @details Factory function for creating maximum operations. + */ + template + auto maximum(TYPE init); + + /** + * @brief Creates a minimum pipe operation. + * @tparam TYPE The element type. + * @param init Initial minimum value. + * @return A genPipe that finds the minimum element. + * @details Factory function for creating minimum operations. + */ + template + auto minimum(TYPE init); + + /** + * @brief Creates a summation pipe operation. + * @tparam TYPE The element type. + * @param init Initial sum value. + * @return A genPipe that sums all elements. + * @details Factory function for creating summation operations. + */ + template + auto summation(TYPE init); + + /** + * @brief Creates a collect pipe operation. + * @tparam TYPE The type of elements in the generator. + * @tparam SET The set type to collect into (default: hashSet). + * @return A genPipe that collects elements into a set. + * @details Factory function for creating collect operations. + */ + template> + auto collect(); + + /** + * @brief Creates a list pipe operation. + * @tparam SERIAL The list container type. + * @return A genPipe that collects elements into a list. + * @details Factory function for creating list operations. + */ + template typename SERIAL = vector> + auto list(); + + /** + * @brief Collects generator elements into a set. + * @tparam TYPE The type of elements in the generator. + * @tparam SET The set type to collect into (default: hashSet). + * @param gen The generator to collect from. + * @return A set containing all unique elements from the generator. + * @details Transforms a generator sequence into a set container, removing duplicates + * and providing fast lookup capabilities. + * + * Example: + * @code + * auto vec = vector{1, 2, 2, 3, 3, 3}; + * auto gen = vec.generator(); + * auto set = collect(gen); // {1, 2, 3} + * @endcode + */ + template> + requires original::ExtendsOf>>, SET> + SET collect(coroutine::generator gen) + { + SET set; + for (auto elem : gen) + { + set.add(elem); + } + return set; + } + + /** + * @brief Collects generator elements into a list container. + * @tparam TYPE The type of elements in the generator. + * @tparam SERIAL The list container type (default: vector). + * @param gen The generator to collect from. + * @return A list container with all generator elements in order. + * @details Converts a generator sequence into a concrete list container, + * preserving element order and allowing random access. + * + * Example: + * @code + * auto gen = someContainer.generator(); + * auto vec = list(gen); // Creates vector with all elements + * @endcode + */ + template typename SERIAL = vector> + requires original::ExtendsOf>, SERIAL> + SERIAL list(coroutine::generator gen) + { + SERIAL list; + for (auto elem : gen) + { + list.pushEnd(elem); + } + return list; + } } template @@ -658,28 +833,33 @@ original::enumerate(coroutine::generator gen) } } -template -requires original::ExtendsOf>>, SET> -SET original::collect(coroutine::generator gen) +template +TYPE original::reduce(coroutine::generator gen, TYPE init, Callback&& c) { - SET set; + TYPE result = std::move(init); for (auto elem : gen) { - set.add(elem); + result = c(std::move(result), std::forward(elem)); } - return set; + return result; } -template class SERIAL> -requires original::ExtendsOf>, SERIAL> -SERIAL original::list(coroutine::generator gen) +template +TYPE original::maximum(coroutine::generator gen, TYPE init) { - SERIAL list; - for (auto elem : gen) - { - list.pushEnd(elem); - } - return list; + return reduce(std::move(gen), std::move(init), [](TYPE max, TYPE cur) { return maximum(max, cur); }); +} + +template +TYPE original::minimum(coroutine::generator gen, TYPE init) +{ + return reduce(std::move(gen), std::move(init), [](TYPE min, TYPE cur) { return minimum(min, cur); }); +} + +template +TYPE original::summation(coroutine::generator gen, TYPE init) +{ + return reduce(std::move(gen), std::move(init), [](TYPE sum, TYPE cur) { return std::move(sum) + std::move(cur); }); } template @@ -1016,4 +1196,57 @@ auto original::find(F&& f) }}; } +template +auto original::reduce(TYPE init, Callback&& c) +{ + return genPipe{[c = std::forward(c), init = std::move(init)](coroutine::generator gen) mutable + { + return reduce(std::move(gen), std::move(init), std::move(c)); + }}; +} + +template +auto original::maximum(TYPE init) +{ + return genPipe{[init = std::move(init)](coroutine::generator gen) mutable + { + return maximum(std::move(gen), std::move(init)); + }}; +} + +template +auto original::minimum(TYPE init) +{ + return genPipe{[init = std::move(init)](coroutine::generator gen) mutable + { + return minimum(std::move(gen), std::move(init)); + }}; +} + +template +auto original::summation(TYPE init) +{ + return genPipe{[init = std::move(init)](coroutine::generator gen) mutable + { + return summation(std::move(gen), std::move(init)); + }}; +} + +template +auto original::collect() +{ + return genPipe{[](coroutine::generator gen) mutable + { + return collect(std::move(gen)); + }}; +} + +template