diff --git a/modules/atom.sysinfo/component.cpp b/modules/atom.sysinfo/component.cpp new file mode 100644 index 00000000..086ec297 --- /dev/null +++ b/modules/atom.sysinfo/component.cpp @@ -0,0 +1,91 @@ +#include "atom/components/component.hpp" +#include +#include "atom/components/registry.hpp" + +#include "atom/sysinfo/battery.hpp" +#include "atom/sysinfo/cpu.hpp" +#include "atom/sysinfo/disk.hpp" +#include "atom/sysinfo/gpu.hpp" +#include "atom/sysinfo/memory.hpp" +#include "atom/sysinfo/os.hpp" +#include "atom/sysinfo/wifi.hpp" + +#include "atom/log/loguru.hpp" + +using namespace atom::system; + +ATOM_MODULE(atom_io, [](Component &component) { + DLOG_F(INFO, "Loading module {}", component.getName()); + + // ------------------------------------------------------------ + // CPU + // ------------------------------------------------------------ + + component.def("cpu_usage", &getCurrentCpuUsage, "cpu", + "Get current CPU usage percentage"); + component.def("cpu_temperature", &getCurrentCpuTemperature, "cpu", + "Get current CPU temperature"); + component.def("cpu_model", &getCPUModel, "cpu", "Get CPU model name"); + component.def("cpu_identifier", &getProcessorIdentifier, "cpu", + "Get CPU identifier"); + component.def("cpu_frequency", &getProcessorFrequency, "cpu", + "Get current CPU frequency"); + component.def("physical_packages", &getNumberOfPhysicalPackages, "cpu", + "Get number of physical CPU packages"); + component.def("logical_cpus", &getNumberOfPhysicalCPUs, "cpu", + "Get number of logical CPUs"); + component.def("cache_sizes", &getCacheSizes, "cpu", "Get CPU cache sizes"); + + // ------------------------------------------------------------ + // Memory + // ------------------------------------------------------------ + + component.def("memory_usage", &getMemoryUsage, "memory", + "Get current memory usage percentage"); + component.def("total_memory", &getTotalMemorySize, "memory", + "Get total memory size"); + component.def("available_memory", &getAvailableMemorySize, "memory", + "Get available memory size"); + component.def("physical_memory_info", &getPhysicalMemoryInfo, "memory", + "Get physical memory slot info"); + component.def("virtual_memory_max", &getVirtualMemoryMax, "memory", + "Get virtual memory max size"); + component.def("virtual_memory_used", &getVirtualMemoryUsed, "memory", + "Get virtual memory used size"); + component.def("swap_memory_total", &getSwapMemoryTotal, "memory", + "Get swap memory total size"); + component.def("swap_memory_used", &getSwapMemoryUsed, "memory", + "Get swap memory used size"); + component.def("committed_memory", &getCommittedMemory, "memory", + "Get committed memory"); + component.def("uncommitted_memory", &getUncommittedMemory, "memory", + "Get uncommitted memory"); + + component.defType("memory_info"); + component.defType("memory_slot"); + component.def_v("memory_slot_type", &MemoryInfo::MemorySlot::type, "memory_slot", + "Get memory slot type"); + component.def("memory_slot_capacity", &MemoryInfo::MemorySlot::capacity, "memory_slot", + "Get memory slot capacity"); + component.def("memory_slot_clock_speed", &MemoryInfo::MemorySlot::clockSpeed, "memory_slot", + "Get memory slot clock speed"); + + + // ------------------------------------------------------------ + // Disk + // ------------------------------------------------------------ + + component.def("disk_usage", &getDiskUsage, "disk", + "Get current disk usage percentage"); + component.def("is_hotspot_connected", &isHotspotConnected, "wifi", + "Check if the hotspot is connected"); + component.def("wired_network", &getCurrentWiredNetwork, "wifi", + "Get current wired network"); + component.def("wifi_name", &getCurrentWifi, "wifi", + "Get current wifi name"); + component.def("current_ip", &getHostIPs, "network", + "Get current IP address"); + component.def("gpu_info", &getGPUInfo, "gpu", "Get GPU info"); + + DLOG_F(INFO, "Loaded module {}", component.getName()); +}); diff --git a/modules/lithium.pytools/tools/byte_convertor.py b/modules/lithium.pytools/tools/byte_convertor.py index 3eca919b..27fd009a 100644 --- a/modules/lithium.pytools/tools/byte_convertor.py +++ b/modules/lithium.pytools/tools/byte_convertor.py @@ -96,7 +96,7 @@ def convert_file_to_array( filename: str, start: int = 0, end: Optional[int] = None, - format: str = 'hex' + data_format: str = 'hex' ) -> str: """ Convert a binary file to a C-style array string. @@ -105,7 +105,7 @@ def convert_file_to_array( filename (str): Path to the binary file. start (int): Start byte for conversion. Defaults to 0. end (Optional[int]): End byte for conversion. Defaults to None (end of file). - format (str): Format of the array data ('hex', 'bin', 'dec'). Defaults to 'hex'. + data_format (str): Format of the array data ('hex', 'bin', 'dec'). Defaults to 'hex'. Returns: str: C-style array string. @@ -113,14 +113,14 @@ def convert_file_to_array( with open(filename, 'rb') as file: data = file.read()[start:end] - if format == 'hex': + if data_format == 'hex': return ', '.join(f'0x{b:02X}' for b in data) - elif format == 'bin': + elif data_format == 'bin': return ', '.join(f'0b{b:08b}' for b in data) - elif format == 'dec': + elif data_format == 'dec': return ', '.join(f'{b}' for b in data) else: - raise ValueError(f"Unsupported format: {format}") + raise ValueError(f"Unsupported format: {data_format}") def convert_array_to_file( @@ -156,7 +156,7 @@ def extract_array_data_from_header(header_filename: str) -> str: Raises: ValueError: If no array data found in the header file. """ - with open(header_filename, 'r') as file: + with open(header_filename, 'r', encoding='utf-8') as file: lines = file.readlines() for line in lines: @@ -204,7 +204,7 @@ def convert_to_header( array_type: str = "unsigned char", comment_style: str = "C", compress: bool = False, - format: str = 'hex', + data_format: str = 'hex', start: int = 0, end: Optional[int] = None, protect: bool = True, @@ -222,7 +222,7 @@ def convert_to_header( array_type (str): Data type of the array in the header file. Defaults to "unsigned char". comment_style (str): Comment style ("C" or "CPP"). Defaults to "C". compress (bool): Whether to compress the data. Defaults to False. - format (str): Format of the array data ("hex", "bin", "dec"). Defaults to "hex". + data_format (str): Format of the array data ("hex", "bin", "dec"). Defaults to 'hex'. start (int): Start byte for conversion. Defaults to 0. end (Optional[int]): End byte for conversion. Defaults to None. protect (bool): Whether to include #ifndef protection macros. Defaults to True. @@ -254,7 +254,7 @@ def convert_to_header( '.h', f'_part_{i}.h') if len(parts) > 1 else output_header array_data = ', '.join(f'0x{b:02X}' for b in part) - with open(part_header, 'w') as header_file: + with open(part_header, 'w', encoding='utf-8') as header_file: if protect: header_file.write(f'#ifndef {macro_name}\n') header_file.write(f'#define {macro_name}\n\n') @@ -270,18 +270,17 @@ def convert_to_header( header_file.write( f'const unsigned int {size_name}_{i} = sizeof({part_name});\n') if protect: - header_file.write(f'#endif\n') + header_file.write('#endif\n') if cpp_class and i == len(parts) - 1: header_file.write('\n') - header_file.write( - f'class {array_name.capitalize()}Wrapper {{\n') - header_file.write(f'public:\n') + header_file.write('class {array_name.capitalize()}Wrapper {\n') + header_file.write('public:\n') header_file.write( f' const {array_type}* data() const {{ return {array_name}; }}\n') header_file.write( f' unsigned int size() const {{ return {size_name}; }}\n') - header_file.write(f'}};\n') + header_file.write('};\n') def convert_to_file( @@ -332,7 +331,7 @@ def parse_args(args: List[str]) -> Tuple[str, str, Optional[str], dict]: "array_type": "unsigned char", "comment_style": "C", "compress": False, - "format": 'hex', + "data_format": 'hex', "start": 0, "end": None, "protect": True, @@ -353,7 +352,7 @@ def parse_args(args: List[str]) -> Tuple[str, str, Optional[str], dict]: elif args[i] == "--compress": options["compress"] = True elif args[i] == "--format" and i + 1 < len(args): - options["format"] = args[i + 1] + options["data_format"] = args[i + 1] elif args[i] == "--start" and i + 1 < len(args): options["start"] = int(args[i + 1]) elif args[i] == "--end" and i + 1 < len(args): @@ -413,7 +412,7 @@ def main() -> None: array_type=options["array_type"], comment_style=options["comment_style"], compress=options["compress"], - format=options["format"], + data_format=options["data_format"], start=options["start"], end=options["end"], protect=options["protect"], diff --git a/modules/lithium.pytools/tools/cmake_generator.py b/modules/lithium.pytools/tools/cmake_generator.py index 2fdc7c3b..00101588 100644 --- a/modules/lithium.pytools/tools/cmake_generator.py +++ b/modules/lithium.pytools/tools/cmake_generator.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -This script automates the generation of CMake build configuration files for C++ projects. It supports multi-directory -project structures, the creation of custom FindXXX.cmake files for third-party libraries, and the use of JSON configuration -files for specifying project settings. +This script automates the generation of CMake build configuration files for C++ projects. +It supports multi-directory project structures, the creation of custom FindXXX.cmake files +for third-party libraries, and the use of JSON configuration files for specifying project settings. Key features: 1. Multi-directory support with separate CMakeLists.txt for each subdirectory. @@ -116,8 +116,8 @@ def generate_cmake(config: ProjectConfig) -> str: # Dependencies (find_package) if config.dependencies: - for dep in config.dependencies: - cmake_template += f'find_package({dep} REQUIRED)\n' + for dependency in config.dependencies: + cmake_template += f'find_package({dependency} REQUIRED)\n' # Subdirectory handling for multi-directory support if config.subdirs: @@ -259,15 +259,15 @@ def parse_arguments(): if args.json: # Generate CMakeLists.txt from JSON configuration - config = generate_from_json(args.json) - cmake_content = generate_cmake(config) + project_config = generate_from_json(args.json) + cmake_content = generate_cmake(project_config) save_file(cmake_content) # Generate FindXXX.cmake files for each dependency specified in the JSON file - for dep in config.dependencies: - find_cmake_content = generate_find_cmake(dep) + for dependency in project_config.dependencies: + find_cmake_content = generate_find_cmake(dependency) save_file(find_cmake_content, directory="cmake", - filename=f"Find{dep}.cmake") + filename=f"Find{dependency}.cmake") if args.find_package: # Generate a single FindXXX.cmake file for the specified dependency diff --git a/modules/lithium.pytools/tools/core_runner.py b/modules/lithium.pytools/tools/core_runner.py new file mode 100644 index 00000000..6397250e --- /dev/null +++ b/modules/lithium.pytools/tools/core_runner.py @@ -0,0 +1,187 @@ +import subprocess +import os +import sys +from pathlib import Path +import argparse +from loguru import logger + + +def set_ulimit(unlimited: bool): + """ + Set the core dump size using ulimit. + + Parameters: + unlimited (bool): If True, set the core dump size to unlimited. Otherwise, set it to 0. + """ + size = "unlimited" if unlimited else "0" + try: + subprocess.run(["ulimit", "-c", size], shell=True, + check=True, executable='/bin/bash') + logger.info(f"Core dump size set to {size}.") + except subprocess.CalledProcessError as e: + logger.error(f"Error setting ulimit: {e}") + + +def set_core_pattern(core_pattern: str): + """ + Set the core pattern for core dump files. + + Parameters: + core_pattern (str): The pattern to use for core dump files. + + Note: + This function requires root privileges to modify /proc/sys/kernel/core_pattern. + """ + if os.geteuid() != 0: + logger.error( + "Setting core pattern requires root privileges. Please run as root.") + sys.exit(1) + + try: + with open("/proc/sys/kernel/core_pattern", "w", encoding="utf-8") as f: + f.write(core_pattern) + logger.info(f"Core pattern set to: {core_pattern}") + except PermissionError as e: + logger.error(f"Permission denied: { + e}. Please run as root to set core pattern.") + sys.exit(1) + + +def compile_cpp_program(source_file: str, output_file: str, compile_flags: list, cpp_standard: str): + """ + Compile the C++ program with optional flags and standard. + + Parameters: + source_file (str): The path to the C++ source file. + output_file (str): The name of the output executable file. + compile_flags (list): Additional flags for the g++ compiler. + cpp_standard (str): The C++ standard to use (e.g., c++11, c++14, c++17, c++20). + """ + flags = compile_flags + [f"-std={cpp_standard}"] + try: + subprocess.run(["g++", *flags, source_file, "-o", + output_file, "-g"], check=True) + logger.info(f"Compiled {source_file} to { + output_file} with flags: {' '.join(flags)}.") + except subprocess.CalledProcessError as e: + logger.error(f"Compilation failed: {e}") + sys.exit(1) + + +def run_cpp_program(executable: str, auto_analyze: bool, core_dir: str): + """ + Run the compiled C++ program and handle crashes. + + Parameters: + executable (str): The name of the executable file to run. + auto_analyze (bool): If True, automatically analyze the core dump if the program crashes. + core_dir (str): The directory to search for core dump files. + """ + try: + subprocess.run([f"./{executable}"], check=True) + logger.info(f"Program {executable} ran successfully without crashing.") + except subprocess.CalledProcessError as e: + logger.warning(f"Program crashed: {e}") + core_file = find_latest_core_file(core_dir) + if auto_analyze and core_file: + analyze_core_dump(executable, core_file) + elif not core_file: + logger.warning("No core dump file found.") + + +def find_latest_core_file(core_dir: str) -> Path: + """ + Find the latest core dump file in the specified directory. + + Parameters: + core_dir (str): The directory to search for core dump files. + + Returns: + Path: The path to the latest core dump file, or None if no core dump files are found. + """ + core_files = list(Path(core_dir).glob("core.*")) + if not core_files: + logger.warning("No core dump files found in the specified directory.") + return None + latest_core = max(core_files, key=os.path.getctime) + logger.info(f"Found core dump file: {latest_core}") + return latest_core + + +def analyze_core_dump(executable: str, core_file: Path, gdb_commands: list = ["-ex", "bt", "-ex", "quit"]): + """ + Analyze the core dump file using gdb with custom commands. + + Parameters: + executable (str): The name of the executable file. + core_file (Path): The path to the core dump file. + gdb_commands (list): A list of gdb commands to run for analysis. + """ + try: + result = subprocess.run( + ["gdb", executable, str(core_file), *gdb_commands], + text=True, + capture_output=True, + check=True + ) + logger.info("Core dump analysis:") + logger.info(result.stdout) + except subprocess.CalledProcessError as e: + logger.error(f"GDB analysis failed: {e}") + + +def configure_logging(log_file: str): + """ + Configure the loguru logger. + + Parameters: + log_file (str): The path to the log file. If empty, logs will only be written to stderr. + """ + logger.remove() # Remove the default logger + logger.add(sys.stderr, level="INFO") + if log_file: + # Rotate logs every 10 MB + logger.add(log_file, level="DEBUG", rotation="10 MB") + logger.info("Logging is configured.") + + +def main(): + """ + Main function to parse arguments and run the core dump and analysis tool. + """ + parser = argparse.ArgumentParser( + description="C++ Core Dump and Analysis Tool with Logging") + parser.add_argument("source", help="C++ source file to compile and run") + parser.add_argument("-o", "--output", default="a.out", + help="Output executable name") + parser.add_argument("-d", "--core-dir", default="/tmp", + help="Directory to search for core dumps") + parser.add_argument("-p", "--core-pattern", + default="/tmp/core.%e.%p", help="Core pattern for dump files") + parser.add_argument("-u", "--ulimit", action="store_true", + help="Set core dump size to unlimited") + parser.add_argument("-f", "--flags", nargs='*', default=[], + help="Additional flags for g++ compilation") + parser.add_argument("-s", "--std", default="c++17", + help="C++ standard to use (e.g., c++11, c++14, c++17, c++20)") + parser.add_argument("-g", "--gdb-commands", nargs='*', default=[ + "-ex", "bt", "-ex", "quit"], help="GDB commands for core dump analysis") + parser.add_argument("-a", "--auto-analyze", action="store_true", + help="Automatically analyze core dump if program crashes") + parser.add_argument("-l", "--log-file", default="", + help="Log file to write logs to") + + args = parser.parse_args() + + configure_logging(args.log_file) + + if args.ulimit: + set_ulimit(True) + set_core_pattern(args.core_pattern) + + compile_cpp_program(args.source, args.output, args.flags, args.std) + run_cpp_program(args.output, args.auto_analyze, args.core_dir) + + +if __name__ == "__main__": + main() diff --git a/modules/lithium.pytools/tools/generator.py b/modules/lithium.pytools/tools/generator.py index 5af16c87..837acb92 100644 --- a/modules/lithium.pytools/tools/generator.py +++ b/modules/lithium.pytools/tools/generator.py @@ -19,9 +19,10 @@ import json import logging import os -import sys import clang.cindex from clang.cindex import CursorKind +import argparse + def camel_to_snake(name): """ @@ -47,33 +48,48 @@ def camel_to_snake(name): return ''.join(snake_case) + class FunctionRegistry: + """ + Registry to store function information. + """ + def __init__(self): self.functions = [] self.total_function_count = 0 def add_function(self, function_info): + """ + Add a function to the registry. + Args: + function_info (dict): Information about the function. + """ self.functions.append(function_info) self.total_function_count += 1 def print_function_details(self): + """ + Print details of all functions in the registry. + """ for function in self.functions: print(f"Function Name: {function['name']}") for param in function['parameters']: print(f" Param: {param['name']} - {param['type']}") print(f"Total functions: {self.total_function_count}") + # 创建全局变量 function_registry = FunctionRegistry() + def parse_namespace(cursor) -> dict: - ''' + """ Parse a CursorKind.NAMESPACE cursor and return its information as a dictionary. Args: cursor (Cursor): The CursorKind.NAMESPACE cursor to parse. Returns: dict: A dictionary containing the namespace information. - ''' + """ namespace_info = { "type": "namespace", "name": cursor.spelling, @@ -85,6 +101,7 @@ def parse_namespace(cursor) -> dict: namespace_info["children"].append(child_info) return namespace_info + def parse_class(cursor): """ Parse a CursorKind.CLASS_DECL cursor and return its information as a dictionary. @@ -96,7 +113,10 @@ def parse_class(cursor): class_info = { "type": "class", "name": cursor.spelling, - "base_classes": [c.spelling for c in cursor.get_children() if c.kind == CursorKind.CXX_BASE_SPECIFIER], # type: ignore + "base_classes": [ + c.spelling for c in cursor.get_children() + if c.kind == CursorKind.CXX_BASE_SPECIFIER + ], "constructors": [], "destructor": None, "member_variables": [], @@ -104,11 +124,11 @@ def parse_class(cursor): } for child in cursor.get_children(): - if child.kind == CursorKind.CONSTRUCTOR: # type: ignore + if child.kind == CursorKind.CONSTRUCTOR: class_info["constructors"].append(parse_function(child)) - elif child.kind == CursorKind.DESTRUCTOR: # type: ignore + elif child.kind == CursorKind.DESTRUCTOR: class_info["destructor"] = parse_function(child) - elif child.kind == CursorKind.FIELD_DECL: # type: ignore + elif child.kind == CursorKind.FIELD_DECL: class_info["member_variables"].append(parse_variable(child)) else: child_info = parse_cursor(child) @@ -116,7 +136,15 @@ def parse_class(cursor): class_info["children"].append(child_info) return class_info + def parse_struct(cursor): + """ + Parse a CursorKind.STRUCT_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.STRUCT_DECL cursor to parse. + Returns: + dict: A dictionary containing the struct information. + """ struct_info = { "type": "struct", "name": cursor.spelling, @@ -124,7 +152,7 @@ def parse_struct(cursor): "children": [] } for child in cursor.get_children(): - if child.kind == CursorKind.FIELD_DECL: # type: ignore + if child.kind == CursorKind.FIELD_DECL: struct_info["member_variables"].append(parse_variable(child)) else: child_info = parse_cursor(child) @@ -132,8 +160,19 @@ def parse_struct(cursor): struct_info["children"].append(child_info) return struct_info + def parse_function(cursor): - attributes = [attr.spelling for attr in cursor.get_children() if attr.kind == CursorKind.ANNOTATE_ATTR] # type: ignore + """ + Parse a CursorKind.FUNCTION_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.FUNCTION_DECL cursor to parse. + Returns: + dict: A dictionary containing the function information. + """ + attributes = [ + attr.spelling for attr in cursor.get_children() + if attr.kind == CursorKind.ANNOTATE_ATTR + ] function_info = { "type": "function", "name": cursor.spelling, @@ -142,14 +181,22 @@ def parse_function(cursor): "attributes": attributes # 新增属性标签字段 } for child in cursor.get_children(): - if child.kind == CursorKind.PARM_DECL: # type: ignore + if child.kind == CursorKind.PARM_DECL: function_info["parameters"].append({ "name": child.spelling, "type": child.type.spelling }) return function_info + def parse_template(cursor): + """ + Parse a CursorKind.TEMPLATE_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.TEMPLATE_DECL cursor to parse. + Returns: + dict: A dictionary containing the template information. + """ template_info = { "type": "template", "name": cursor.spelling, @@ -157,7 +204,7 @@ def parse_template(cursor): "children": [] } for child in cursor.get_children(): - if child.kind == CursorKind.TEMPLATE_TYPE_PARAMETER: # type: ignore + if child.kind == CursorKind.TEMPLATE_TYPE_PARAMETER: template_info["template_parameters"].append(child.spelling) else: child_info = parse_cursor(child) @@ -165,21 +212,37 @@ def parse_template(cursor): template_info["children"].append(child_info) return template_info + def parse_enum(cursor): + """ + Parse a CursorKind.ENUM_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.ENUM_DECL cursor to parse. + Returns: + dict: A dictionary containing the enum information. + """ enum_info = { "type": "enum", "name": cursor.spelling, "constants": [] } for child in cursor.get_children(): - if child.kind == CursorKind.ENUM_CONSTANT_DECL: # type: ignore + if child.kind == CursorKind.ENUM_CONSTANT_DECL: enum_info["constants"].append({ "name": child.spelling, "value": child.enum_value }) return enum_info + def parse_macro(cursor): + """ + Parse a CursorKind.MACRO_DEFINITION cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.MACRO_DEFINITION cursor to parse. + Returns: + dict: A dictionary containing the macro information. + """ macro_info = { "type": "macro", "name": cursor.spelling, @@ -187,7 +250,15 @@ def parse_macro(cursor): } return macro_info + def parse_variable(cursor): + """ + Parse a CursorKind.VAR_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.VAR_DECL cursor to parse. + Returns: + dict: A dictionary containing the variable information. + """ variable_info = { "type": "variable", "name": cursor.spelling, @@ -195,7 +266,15 @@ def parse_variable(cursor): } return variable_info + def parse_typedef(cursor): + """ + Parse a CursorKind.TYPEDEF_DECL cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.TYPEDEF_DECL cursor to parse. + Returns: + dict: A dictionary containing the typedef information. + """ typedef_info = { "type": "typedef", "name": cursor.spelling, @@ -203,6 +282,7 @@ def parse_typedef(cursor): } return typedef_info + def parse_union(cursor): """ Parse a CursorKind.UNION_DECL cursor and return its information as a dictionary. @@ -222,34 +302,67 @@ def parse_union(cursor): union_info["children"].append(child_info) return union_info + def parse_documentation(cursor): + """ + Parse documentation comments from a cursor. + Args: + cursor (Cursor): The cursor to parse documentation from. + Returns: + str: The parsed documentation string. + """ docstring = "" for token in cursor.get_tokens(): - if token.kind == clang.cindex.TokenKind.COMMENT: # type: ignore + if token.kind == clang.cindex.TokenKind.COMMENT: docstring += token.spelling.strip('/**/ \t\n') + "\n" return docstring.strip() + def parse_namespace_alias(cursor): + """ + Parse a CursorKind.NAMESPACE_ALIAS cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.NAMESPACE_ALIAS cursor to parse. + Returns: + dict: A dictionary containing the namespace alias information. + """ return { "type": "namespace_alias", "name": cursor.spelling, "aliased_namespace": cursor.referenced.spelling # 获取别名指向的命名空间 } + def parse_using_declaration(cursor): + """ + Parse a CursorKind.USING_DECLARATION cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.USING_DECLARATION cursor to parse. + Returns: + dict: A dictionary containing the using declaration information. + """ return { "type": "using_declaration", "name": cursor.spelling, "used_name": cursor.referenced.spelling # 获取using声明使用的名称 } + def parse_template_type_parameter(cursor): + """ + Parse a CursorKind.TEMPLATE_TYPE_PARAMETER cursor and return its information as a dictionary. + Args: + cursor (Cursor): The CursorKind.TEMPLATE_TYPE_PARAMETER cursor to parse. + Returns: + dict: A dictionary containing the template type parameter information. + """ return { "type": "template_type_parameter", "name": cursor.spelling, - "default_type": cursor.default_type.spelling if cursor.default_type else None # 获取模板类型参数的默认类型 + "default_type": cursor.default_type.spelling if cursor.default_type else None # 获取模板类型参数的默认类型 } + def parse_cursor(cursor): """ Parse a Cursor and return its information as a dictionary. @@ -265,7 +378,10 @@ def parse_cursor(cursor): return parse_class(cursor) elif cursor.kind == CursorKind.STRUCT_DECL: return parse_struct(cursor) - elif cursor.kind in [CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.FUNCTION_TEMPLATE]: + elif cursor.kind in [ + CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, + CursorKind.FUNCTION_TEMPLATE + ]: return parse_function(cursor) elif cursor.kind == CursorKind.ENUM_DECL: return parse_enum(cursor) @@ -290,10 +406,16 @@ def parse_cursor(cursor): return parse_template_type_parameter(cursor) return None except Exception as e: - logging.error(f"Failed to parse cursor at {cursor.location}: {str(e)}") + logging.error("Failed to parse cursor at %s: %s", + cursor.location, str(e)) return None + class ParentVisitor: + """ + Visitor to record parent-child relationships in the AST. + """ + def __init__(self): self.parent_map = {} @@ -351,6 +473,7 @@ def print_path(self, cursor): for node in path: print(f"{node.spelling or node.kind} ({node.kind})") + def find_cursor_by_spelling(cursor, spelling): """ Find a cursor by its spelling. @@ -370,6 +493,7 @@ def find_cursor_by_spelling(cursor, spelling): return found return None + def find_cursor_by_kind(cursor, kind): """ Find a cursor by its kind. @@ -388,6 +512,7 @@ def find_cursor_by_kind(cursor, kind): result.extend(find_cursor_by_kind(child, kind)) return result + def print_ast(cursor, level=0): """ Print the AST starting from the given cursor. @@ -400,6 +525,7 @@ def print_ast(cursor, level=0): for child in cursor.get_children(): print_ast(child, level + 1) + def parse_cpp_file(file_path): """ Parse a C++ header file (.hpp) and return its AST information as a dictionary. @@ -424,6 +550,7 @@ def parse_cpp_file(file_path): return ast_info + def parse_hpp_files(directory): """ Parse all C++ header files (.hpp) in a directory and return their AST information as a list of dictionaries. @@ -437,7 +564,7 @@ def parse_hpp_files(directory): ast_info_list = [] - for root, dirs, files in os.walk(directory): + for root, _, files in os.walk(directory): for file in files: if file.endswith(".hpp"): file_path = os.path.join(root, file) @@ -446,6 +573,7 @@ def parse_hpp_files(directory): return ast_info_list + def generate_ast_json(directory, output_file): """ Generate an AST JSON file from a directory of C++ header files. @@ -461,6 +589,7 @@ def generate_ast_json(directory, output_file): print(f"AST information written to file: {output_file}") + def generate_pybind11_bindings(ast_info_list, bindings_file): """ Generate pybind11 bindings from the AST information. @@ -468,25 +597,35 @@ def generate_pybind11_bindings(ast_info_list, bindings_file): ast_info_list (list): A list of dictionaries containing the AST information. bindings_file (str): The path to the output pybind11 bindings file. """ - bindings = ['#include ', '#include ', 'namespace py = pybind11;', 'PYBIND11_MODULE(module_name, m) {'] + bindings = [ + '#include ', + '#include ', + 'namespace py = pybind11;', + 'PYBIND11_MODULE(module_name, m) {' + ] def generate_function_binding(function_info, namespace=None): if namespace: function_name = f"{namespace}::{function_info['name']}" else: function_name = function_info['name'] - bindings.append(f' m.def("{camel_to_snake(function_info["name"])}", &{function_name});') + bindings.append( + f' m.def("{camel_to_snake(function_info["name"])}", &{ + function_name});' + ) def generate_class_binding(class_info, namespace=None): if namespace: class_name = f"{namespace}::{class_info['name']}" else: class_name = class_info['name'] - bindings.append(f' py::class_<{class_name}>(m, "{class_info["name"]}")') + bindings.append(f' py::class_<{ + class_name}>(m, "{class_info["name"]}")') # Add constructors for constructor in class_info["constructors"]: - parameter_types = ", ".join([param["type"] for param in constructor["parameters"]]) + parameter_types = ", ".join( + [param["type"] for param in constructor["parameters"]]) if parameter_types: bindings.append(f' .def(py::init<{parameter_types}>())') else: @@ -494,23 +633,28 @@ def generate_class_binding(class_info, namespace=None): # Add destructor if available if class_info["destructor"]: - bindings.append(f' .def("__del__", &{class_name}::{class_info["destructor"]["name"]})') + bindings.append(f' .def("__del__", &{class_name}::{ + class_info["destructor"]["name"]})') # Add member variables for member_variable in class_info["member_variables"]: - bindings.append(f' .def_readwrite("{member_variable["name"]}", &{class_name}::{member_variable["name"]})') + bindings.append(f' .def_readwrite("{member_variable["name"]}", &{ + class_name}::{member_variable["name"]})') # Add methods for child in class_info['children']: if child['type'] == 'function': if namespace: - method_name = f"{namespace}::{class_info['name']}::{child['name']}" + method_name = f"{namespace}::{ + class_info['name']}::{child['name']}" else: method_name = f"{class_info['name']}::{child['name']}" if len(child['parameters']) > 0: - bindings.append(f' .def("{child["name"]}", py::overload_cast<{", ".join([param["type"] for param in child["parameters"]])}>(&{method_name}))') + bindings.append(f' .def("{child["name"]}", py::overload_cast<{", ".join( + [param["type"] for param in child["parameters"]])}>(&{method_name}))') else: - bindings.append(f' .def("{child["name"]}", &{method_name})') + bindings.append( + f' .def("{child["name"]}", &{method_name})') # Add enumerations for child in class_info['children']: @@ -524,13 +668,16 @@ def generate_enum_binding(enum_info, enclosing_class=None): enum_name = f"{enclosing_class}::{enum_info['name']}" else: enum_name = enum_info['name'] - bindings.append(f' py::enum_<{enum_name}>(m, "{enum_info["name"]}")') + bindings.append(f' py::enum_<{ + enum_name}>(m, "{enum_info["name"]}")') for constant in enum_info['constants']: - bindings.append(f' .value("{constant["name"]}", {enum_name}::{constant["name"]})') + bindings.append(f' .value("{constant["name"]}", { + enum_name}::{constant["name"]})') bindings.append(' .export_values();') def generate_namespace_binding(namespace_info): - bindings.append(f' py::module_ {namespace_info["name"]} = m.def_submodule("{namespace_info["name"]}");') + bindings.append(f' py::module_ { + namespace_info["name"]} = m.def_submodule("{namespace_info["name"]}");') for child in namespace_info['children']: if child['type'] == 'function': generate_function_binding(child, namespace_info['name']) @@ -557,9 +704,9 @@ def generate_namespace_binding(namespace_info): file.write('\n'.join(bindings)) print(f"pybind11 bindings written to file: {bindings_file}") -import argparse -parser = argparse.ArgumentParser(description="Generate AST and pybind11 bindings for C++ header files.") +parser = argparse.ArgumentParser( + description="Generate AST and pybind11 bindings for C++ header files.") parser.add_argument('directory', help='Directory containing C++ header files') parser.add_argument('output_file', help='Output JSON file for AST') parser.add_argument('bindings_file', help='Output file for pybind11 bindings') diff --git a/modules/lithium.pytools/tools/html.py b/modules/lithium.pytools/tools/html.py index 970705cb..2913466e 100644 --- a/modules/lithium.pytools/tools/html.py +++ b/modules/lithium.pytools/tools/html.py @@ -1,9 +1,17 @@ +""" +This module provides classes and functions to generate HTML documents from JSON structures. +""" + import json import re import argparse class Tag: + """ + Represents an HTML tag. + """ + def __init__(self, name, is_single=False, attrs=None, contents=None): """ Initialize a new HTML tag. @@ -93,8 +101,7 @@ def render(self, context=None): for c in self.contents) if self.is_single: return f'<{self.name}{attrs} />' - else: - return f'<{self.name}{attrs}>{inner_html}' + return f'<{self.name}{attrs}>{inner_html}' def render_str(self, text, context): """ @@ -108,7 +115,11 @@ def render_str(self, text, context): str: The rendered string. """ if context is not None and isinstance(text, str): - return re.sub(r'\{\{(\w+)\}\}', lambda m: str(context.get(m.group(1), m.group(0))), text) + return re.sub( + r'\{\{(\w+)\}\}', + lambda m: str(context.get(m.group(1), m.group(0))), + text + ) return text def __str__(self): @@ -122,6 +133,10 @@ def __str__(self): class HTMLDocument: + """ + Represents an HTML document. + """ + def __init__(self, title=""): """ Initialize a new HTML document. @@ -147,15 +162,28 @@ def enable_bootstrap(self, version="5.2.3"): version (str): The version of Bootstrap to use. """ if not self.bootstrap_enabled: - link_tag = Tag("link", is_single=True, attrs={ - "rel": "stylesheet", - "href": f"https://stackpath.bootstrapcdn.com/bootstrap/{version}/css/bootstrap.min.css" - }) + link_tag = Tag( + "link", + is_single=True, + attrs={ + "rel": "stylesheet", + "href": ( + "https://stackpath.bootstrapcdn.com/bootstrap/" + f"{version}/css/bootstrap.min.css" + ) + } + ) self.head.add_content(link_tag) - script_tag_js = Tag("script", attrs={ - "src": f"https://stackpath.bootstrapcdn.com/bootstrap/{version}/js/bootstrap.bundle.min.js" - }) + script_tag_js = Tag( + "script", + attrs={ + "src": ( + "https://stackpath.bootstrapcdn.com/bootstrap/" + f"{version}/js/bootstrap.bundle.min.js" + ) + } + ) self.body.add_content(script_tag_js) self.bootstrap_enabled = True @@ -242,26 +270,41 @@ def main(): Main function to parse command line arguments and generate HTML document. """ parser = argparse.ArgumentParser( - description="Generate HTML from JSON structure.") + description="Generate HTML from JSON structure." + ) parser.add_argument( - "json_file", help="Path to the JSON file containing the HTML structure.") + "json_file", + help="Path to the JSON file containing the HTML structure." + ) parser.add_argument( - "--context", help="Path to the JSON file containing the context for template rendering.", default=None) + "--context", + help="Path to the JSON file containing the context for template rendering.", + default=None + ) parser.add_argument( - "--output", help="Path to the output HTML file.", default="output.html") + "--output", + help="Path to the output HTML file.", + default="output.html" + ) parser.add_argument( - "--title", help="Title of the HTML document.", default="Generated Page") + "--title", + help="Title of the HTML document.", + default="Generated Page" + ) parser.add_argument( - "--enable-bootstrap", help="Enable Bootstrap in the HTML document.", action="store_true") + "--enable-bootstrap", + help="Enable Bootstrap in the HTML document.", + action="store_true" + ) args = parser.parse_args() - with open(args.json_file, 'r') as f: + with open(args.json_file, 'r', encoding='utf-8') as f: json_data = json.load(f) context = None if args.context: - with open(args.context, 'r') as f: + with open(args.context, 'r', encoding='utf-8') as f: context = json.load(f) doc = HTMLDocument(args.title) @@ -271,7 +314,7 @@ def main(): generated_html = generate_html_from_json(json_data, context) doc.add_tag(generated_html) - with open(args.output, 'w') as f: + with open(args.output, 'w', encoding='utf-8') as f: f.write(str(doc)) print(f"HTML document generated and saved to {args.output}") diff --git a/modules/lithium.pytools/tools/model.py b/modules/lithium.pytools/tools/model.py new file mode 100644 index 00000000..60f9fed5 --- /dev/null +++ b/modules/lithium.pytools/tools/model.py @@ -0,0 +1,232 @@ +""" +This module provides classes and functions to generate responses using LLaMA and Ollama models. +""" + +import os +import hashlib +import logging +import asyncio +from typing import Optional +from transformers import AutoModelForCausalLM, AutoTokenizer + +# 配置日志 +logging.basicConfig(level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s') + +# 定义缓存目录 +CACHE_DIR = "./cache" +if not os.path.exists(CACHE_DIR): + os.makedirs(CACHE_DIR) + + +class BaseModel: + """ + Abstract base class for models. + """ + + def generate_response(self, prompt: str, max_length: int = 50) -> str: + """ + Generate a response for the given prompt. + + Args: + prompt (str): The input prompt. + max_length (int): The maximum length of the generated response. + + Returns: + str: The generated response. + """ + raise NotImplementedError + + async def generate_response_async(self, prompt: str, max_length: int = 50) -> str: + """ + Asynchronously generate a response for the given prompt. + + Args: + prompt (str): The input prompt. + max_length (int): The maximum length of the generated response. + + Returns: + str: The generated response. + """ + raise NotImplementedError + + +class LLaMAModel(BaseModel): + """ + LLaMA model class. + """ + + def __init__(self, model_name: str = "meta-llama/Llama-3b"): + logging.info("Loading LLaMA model %s...", model_name) + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + self.model = AutoModelForCausalLM.from_pretrained(model_name) + + def generate_response(self, prompt: str, max_length: int = 50) -> str: + cache_key = self._get_cache_key(prompt, max_length) + cached_response = self._load_from_cache(cache_key) + + if cached_response: + logging.info("Loaded response from cache.") + return cached_response + + logging.info("Generating response for prompt: %s", prompt) + inputs = self.tokenizer(prompt, return_tensors="pt") + outputs = self.model.generate( + inputs["input_ids"], max_length=max_length) + response = self.tokenizer.decode(outputs[0], skip_special_tokens=True) + + self._save_to_cache(cache_key, response) + return response + + def _get_cache_key(self, prompt: str, max_length: int) -> str: + key = f"{prompt}_{max_length}" + return hashlib.md5(key.encode()).hexdigest() + + def _save_to_cache(self, key: str, response: str): + with open(f"{CACHE_DIR}/{key}.txt", "w", encoding="utf-8") as f: + f.write(response) + + def _load_from_cache(self, key: str) -> Optional[str]: + file_path = f"{CACHE_DIR}/{key}.txt" + if os.path.exists(file_path): + with open(file_path, "r", encoding="utf-8") as f: + return f.read() + return None + + async def generate_response_async(self, prompt: str, max_length: int = 50) -> str: + loop = asyncio.get_event_loop() + response = await loop.run_in_executor(None, self.generate_response, prompt, max_length) + return response + + +class OllamaModel(BaseModel): + """ + Ollama model class. + """ + + def __init__(self, model_name: str = "ollama/ollama-3b"): + logging.info("Loading Ollama model %s...", model_name) + # 模拟Ollama模型的加载过程 + # 假设有类似接口 + # self.model = OllamaModelAPI.load(model_name) # 如果Ollama有自己的API,使用此处加载 + + def generate_response(self, prompt: str, max_length: int = 50) -> str: + logging.info("Generating response with Ollama for prompt: %s", prompt) + # 假设有类似接口生成响应 + response = f"Ollama Response to: {prompt} (Simulated)" + return response + + async def generate_response_async(self, prompt: str, max_length: int = 50) -> str: + logging.info( + "Generating async response with Ollama for prompt: %s", prompt) + # 模拟异步调用 Ollama 模型 + await asyncio.sleep(1) # 假设异步调用需要一定的时间 + return self.generate_response(prompt, max_length) + + +class ModelManager: + """ + Model manager class to handle switching between different models. + """ + + def __init__(self): + self.models = { + "llama": LLaMAModel(), + "ollama": OllamaModel() + } + self.active_model = "llama" # 默认使用LLaMA模型 + + def set_active_model(self, model_name: str): + """ + Set the active model. + + Args: + model_name (str): The name of the model to set as active. + """ + if model_name not in self.models: + raise ValueError(f"Model {model_name} is not supported.") + self.active_model = model_name + logging.info("Switched to %s model.", model_name) + + def generate_response(self, prompt: str, max_length: int = 50) -> str: + """ + Generate a response using the active model. + + Args: + prompt (str): The input prompt. + max_length (int): The maximum length of the generated response. + + Returns: + str: The generated response. + """ + model = self.models[self.active_model] + return model.generate_response(prompt, max_length) + + async def generate_response_async(self, prompt: str, max_length: int = 50) -> str: + """ + Asynchronously generate a response using the active model. + + Args: + prompt (str): The input prompt. + max_length (int): The maximum length of the generated response. + + Returns: + str: The generated response. + """ + model = self.models[self.active_model] + return await model.generate_response_async(prompt, max_length) + + +def cli_interface(): + import argparse + parser = argparse.ArgumentParser( + description="Generate text using LLaMA or Ollama model.") + parser.add_argument("--prompt", type=str, + help="Input prompt for text generation", required=True) + parser.add_argument("--max_length", type=int, default=50, + help="Maximum length of generated text") + parser.add_argument("--model", type=str, choices=[ + "llama", "ollama"], default="llama", help="Choose model: LLaMA or Ollama") + parser.add_argument("--async_mode", action="store_true", + help="Generate response asynchronously") # 修改了参数名 + parser.add_argument("--output", type=str, + help="File to save the generated response") + parser.add_argument("--log_level", type=str, choices=[ + "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], default="INFO", help="Set the logging level") + + args = parser.parse_args() + + # 设置日志级别 + logging.getLogger().setLevel(args.log_level) + + model_manager = ModelManager() + model_manager.set_active_model(args.model) + + if args.async_mode: # 修改了参数名 + response = asyncio.run(model_manager.generate_response_async( + args.prompt, args.max_length)) + else: + response = model_manager.generate_response( + args.prompt, args.max_length) + + if args.output: + with open(args.output, "w", encoding="utf-8") as f: + f.write(response) + logging.info("Response saved to %s", args.output) + else: + print(f"Response: {response}") + + +async def main_async(): + """ + Example of asynchronous call to generate a response. + """ + model_manager = ModelManager() + model_manager.set_active_model("ollama") # 切换到 Ollama 模型 + prompt = "What are the latest trends in AI research?" + response = await model_manager.generate_response_async(prompt) + print(f"Async Response: {response}") + + +if __name__ == "__main__": + cli_interface() diff --git a/modules/lithium.pytools/tools/package.py b/modules/lithium.pytools/tools/package.py index 4625e277..6568258f 100644 --- a/modules/lithium.pytools/tools/package.py +++ b/modules/lithium.pytools/tools/package.py @@ -48,7 +48,7 @@ def run_command(command: list) -> str: SystemExit: If the command returns a non-zero exit code. """ result = subprocess.run(command, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, text=True) + stderr=subprocess.PIPE, text=True, check=False) if result.returncode != 0: print(f"Error: {result.stderr}") sys.exit(result.returncode) @@ -119,7 +119,7 @@ def list_available_versions(package_name: str) -> list: requests.RequestException: If there's an error while fetching data from PyPI. """ try: - response = requests.get(f"https://pypi.org/pypi/{package_name}/json") + response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=5) response.raise_for_status() data = response.json() versions = sorted(data['releases'].keys(), @@ -202,7 +202,7 @@ def freeze_installed_packages(output_file: str = "requirements.txt"): """ command = [sys.executable, "-m", "pip", "freeze"] output = run_command(command) - with open(output_file, "w") as f: + with open(output_file, "w", encoding="utf-8") as f: f.write(output) print(f"Requirements written to {output_file}") diff --git a/src/atom/function/abi.hpp b/src/atom/function/abi.hpp index e721e9f1..ad2dd00c 100644 --- a/src/atom/function/abi.hpp +++ b/src/atom/function/abi.hpp @@ -9,8 +9,7 @@ #ifndef ATOM_META_ABI_HPP #define ATOM_META_ABI_HPP -#include -#include +#include #include #include #include @@ -34,6 +33,9 @@ #endif namespace atom::meta { + +constexpr std::size_t BUFFER_SIZE = 1024; + class DemangleHelper { public: template @@ -85,11 +87,11 @@ class DemangleHelper { private: static auto demangleInternal(std::string_view mangled_name) -> std::string { #ifdef _MSC_VER - char buffer[1024]; - DWORD length = UnDecorateSymbolName(mangled_name.data(), buffer, - sizeof(buffer), UNDNAME_COMPLETE); + std::array buffer; + DWORD length = UnDecorateSymbolName(mangled_name.data(), buffer.data(), + buffer.size(), UNDNAME_COMPLETE); - return (length > 0) ? std::string(buffer, length) + return (length > 0) ? std::string(buffer.data(), length) : std::string(mangled_name); #else int status = -1; @@ -215,4 +217,4 @@ class DemangleHelper { }; } // namespace atom::meta -#endif // ATOM_META_ABI_HPP +#endif // ATOM_META_ABI_HPP \ No newline at end of file diff --git a/src/atom/function/any.hpp b/src/atom/function/any.hpp index ad13795f..04362c8c 100644 --- a/src/atom/function/any.hpp +++ b/src/atom/function/any.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "macro.hpp" @@ -34,7 +33,7 @@ class BoxedValue { struct VoidType {}; private: - struct Data { + struct ATOM_ALIGNAS(128) Data { std::any mObj; TypeInfo mTypeInfo; std::shared_ptr>> mAttrs; @@ -67,7 +66,7 @@ class BoxedValue { mConstDataPtr(std::is_const_v> ? &obj : nullptr) {} - } ATOM_ALIGNAS(128); + }; std::shared_ptr m_data_; mutable std::shared_mutex m_mutex_; @@ -76,15 +75,15 @@ class BoxedValue { // Constructor template requires(!std::same_as>) - explicit BoxedValue(T&& t, bool t_return_value = false, + explicit BoxedValue(T&& value, bool return_value = false, bool readonly = false) : m_data_(std::make_shared( - std::forward(t), + std::forward(value), std::is_reference_v || std::is_same_v< std::decay_t, std::reference_wrapper>>, - t_return_value, readonly)) { + return_value, readonly)) { if constexpr (std::is_same_v< std::decay_t, std::reference_wrapper>>) { @@ -138,16 +137,16 @@ class BoxedValue { template requires(!std::same_as>) - auto operator=(T&& t) -> BoxedValue& { + auto operator=(T&& value) -> BoxedValue& { std::unique_lock lock(m_mutex_); - m_data_->mObj = std::forward(t); + m_data_->mObj = std::forward(value); m_data_->mTypeInfo = userType(); return *this; } template - BoxedValue(const T& t) - : m_data_(std::make_shared(t, false, false, true)) {} + explicit BoxedValue(const T& value) + : m_data_(std::make_shared(value, false, false, true)) {} // Swap function void swap(BoxedValue& rhs) noexcept { @@ -158,7 +157,7 @@ class BoxedValue { } // Check if the value is undefined (VoidType) - [[nodiscard]] bool isUndef() const noexcept { + [[nodiscard]] auto isUndef() const noexcept -> bool { std::shared_lock lock(m_mutex_); if (m_data_ == nullptr) { return true; @@ -167,22 +166,23 @@ class BoxedValue { !m_data_->mObj.has_value(); } - [[nodiscard]] bool isConst() const noexcept { + [[nodiscard]] auto isConst() const noexcept -> bool { std::shared_lock lock(m_mutex_); return m_data_->mTypeInfo.isConst(); } - [[nodiscard]] bool isType(const TypeInfo& ti) const noexcept { + [[nodiscard]] auto isType(const TypeInfo& type_info) const noexcept + -> bool { std::shared_lock lock(m_mutex_); - return m_data_->mTypeInfo == ti; + return m_data_->mTypeInfo == type_info; } - [[nodiscard]] bool isRef() const noexcept { + [[nodiscard]] auto isRef() const noexcept -> bool { std::shared_lock lock(m_mutex_); return m_data_->mIsRef; } - [[nodiscard]] bool isReturnValue() const noexcept { + [[nodiscard]] auto isReturnValue() const noexcept -> bool { std::shared_lock lock(m_mutex_); return m_data_->mReturnValue; } @@ -192,28 +192,29 @@ class BoxedValue { m_data_->mReturnValue = false; } - [[nodiscard]] bool isReadonly() const noexcept { + [[nodiscard]] auto isReadonly() const noexcept -> bool { std::shared_lock lock(m_mutex_); return m_data_->mReadonly; } - [[nodiscard]] bool isConstDataPtr() const noexcept { + [[nodiscard]] auto isConstDataPtr() const noexcept -> bool { std::shared_lock lock(m_mutex_); return m_data_->mConstDataPtr != nullptr; } - [[nodiscard]] const std::any& get() const noexcept { + [[nodiscard]] auto get() const noexcept -> const std::any& { std::shared_lock lock(m_mutex_); return m_data_->mObj; } - [[nodiscard]] const TypeInfo& getTypeInfo() const noexcept { + [[nodiscard]] auto getTypeInfo() const noexcept -> const TypeInfo& { std::shared_lock lock(m_mutex_); return m_data_->mTypeInfo; } // Attribute handling - BoxedValue& setAttr(const std::string& name, const BoxedValue& value) { + auto setAttr(const std::string& name, + const BoxedValue& value) -> BoxedValue& { std::unique_lock lock(m_mutex_); if (!m_data_->mAttrs) { m_data_->mAttrs = std::make_shared< @@ -223,18 +224,18 @@ class BoxedValue { return *this; } - [[nodiscard]] BoxedValue getAttr(const std::string& name) const { + [[nodiscard]] auto getAttr(const std::string& name) const -> BoxedValue { std::shared_lock lock(m_mutex_); if (m_data_->mAttrs) { - if (auto it = m_data_->mAttrs->find(name); - it != m_data_->mAttrs->end()) { - return BoxedValue(it->second); + if (auto iter = m_data_->mAttrs->find(name); + iter != m_data_->mAttrs->end()) { + return BoxedValue(iter->second); } } return {}; // Undefined BoxedValue } - [[nodiscard]] bool hasAttr(const std::string& name) const { + [[nodiscard]] auto hasAttr(const std::string& name) const -> bool { std::shared_lock lock(m_mutex_); return m_data_->mAttrs && m_data_->mAttrs->find(name) != m_data_->mAttrs->end(); @@ -247,7 +248,7 @@ class BoxedValue { } } - [[nodiscard]] std::vector listAttrs() const { + [[nodiscard]] auto listAttrs() const -> std::vector { std::shared_lock lock(m_mutex_); std::vector attrs; if (m_data_->mAttrs) { @@ -259,19 +260,19 @@ class BoxedValue { return attrs; } - [[nodiscard]] bool isNull() const noexcept { + [[nodiscard]] auto isNull() const noexcept -> bool { std::shared_lock lock(m_mutex_); return !m_data_->mObj.has_value(); } - [[nodiscard]] void* getPtr() const noexcept { + [[nodiscard]] auto getPtr() const noexcept -> void* { std::shared_lock lock(m_mutex_); return const_cast(m_data_->mConstDataPtr); } // Try casting the contained value to a specific type template - [[nodiscard]] std::optional tryCast() const noexcept { + [[nodiscard]] auto tryCast() const noexcept -> std::optional { std::shared_lock lock(m_mutex_); try { if constexpr (std::is_reference_v) { @@ -299,7 +300,7 @@ class BoxedValue { // Check if the contained value can be cast to a specific type template - [[nodiscard]] bool canCast() const noexcept { + [[nodiscard]] auto canCast() const noexcept -> bool { std::shared_lock lock(m_mutex_); try { if constexpr (std::is_reference_v) { @@ -316,7 +317,7 @@ class BoxedValue { } // Debug string representation - [[nodiscard]] std::string debugString() const { + [[nodiscard]] auto debugString() const -> std::string { std::ostringstream oss; oss << "BoxedValue<" << m_data_->mTypeInfo.name() << ">: "; std::shared_lock lock(m_mutex_); @@ -331,40 +332,43 @@ class BoxedValue { } return oss.str(); } + + // Destructor + ~BoxedValue() = default; }; // Helper functions to create BoxedValue instances template -auto var(T&& t) -> BoxedValue { +auto var(T&& value) -> BoxedValue { using DecayedType = std::decay_t; constexpr bool IS_REF_WRAPPER = std::is_same_v>>; - return BoxedValue(std::forward(t), IS_REF_WRAPPER, false); + return BoxedValue(std::forward(value), IS_REF_WRAPPER, false); } template -auto constVar(const T& t) -> BoxedValue { +auto constVar(const T& value) -> BoxedValue { using DecayedType = std::decay_t; constexpr bool IS_REF_WRAPPER = std::is_same_v>>; - return BoxedValue(std::cref(t), IS_REF_WRAPPER, true); + return BoxedValue(std::cref(value), IS_REF_WRAPPER, true); } inline auto voidVar() -> BoxedValue { return {}; } // Factory functions for creating BoxedValues template -auto makeBoxedValue(T&& t, bool is_return_value = false, +auto makeBoxedValue(T&& value, bool is_return_value = false, bool readonly = false) -> BoxedValue { if constexpr (std::is_reference_v) { - return BoxedValue(std::ref(t), is_return_value, readonly); + return BoxedValue(std::ref(value), is_return_value, readonly); } else { - return BoxedValue(std::forward(t), is_return_value, readonly); + return BoxedValue(std::forward(value), is_return_value, readonly); } } } // namespace atom::meta -#endif // ATOM_META_ANY_HPP +#endif // ATOM_META_ANY_HPP \ No newline at end of file diff --git a/src/atom/function/anymeta.hpp b/src/atom/function/anymeta.hpp index baca9ea3..bd4f1420 100644 --- a/src/atom/function/anymeta.hpp +++ b/src/atom/function/anymeta.hpp @@ -33,14 +33,14 @@ class TypeMetadata { using EventCallback = std::function&)>; - struct Property { + struct ATOM_ALIGNAS(64) Property { GetterFunction getter; SetterFunction setter; - } ATOM_ALIGNAS(64); + }; - struct Event { + struct ATOM_ALIGNAS(32) Event { std::vector listeners; - } ATOM_ALIGNAS(32); + }; private: std::unordered_map> @@ -82,8 +82,10 @@ class TypeMetadata { // Fire event and notify listeners void fireEvent(BoxedValue& obj, const std::string& event_name, const std::vector& args) const { - if (auto it = m_events_.find(event_name); it != m_events_.end()) { - for (const auto& listener : it->second.listeners) { + if (auto eventIter = m_events_.find(event_name); + eventIter != m_events_.end()) { +#pragma unroll + for (const auto& listener : eventIter->second.listeners) { listener(obj, args); } } else { @@ -94,8 +96,9 @@ class TypeMetadata { // Retrieve all overloaded methods by name [[nodiscard]] auto getMethods(const std::string& name) const -> std::optional*> { - if (auto it = m_methods_.find(name); it != m_methods_.end()) { - return &it->second; + if (auto methodIter = m_methods_.find(name); + methodIter != m_methods_.end()) { + return &methodIter->second; } return std::nullopt; } @@ -103,8 +106,9 @@ class TypeMetadata { // Retrieve property by name [[nodiscard]] auto getProperty(const std::string& name) const -> std::optional { - if (auto it = m_properties_.find(name); it != m_properties_.end()) { - return it->second; + if (auto propertyIter = m_properties_.find(name); + propertyIter != m_properties_.end()) { + return propertyIter->second; } return std::nullopt; } @@ -113,10 +117,10 @@ class TypeMetadata { [[nodiscard]] auto getConstructor(const std::string& type_name, size_t index = 0) const -> std::optional { - if (auto it = m_constructors_.find(type_name); - it != m_constructors_.end()) { - if (index < it->second.size()) { - return it->second[index]; + if (auto constructorIter = m_constructors_.find(type_name); + constructorIter != m_constructors_.end()) { + if (index < constructorIter->second.size()) { + return constructorIter->second[index]; } } return std::nullopt; @@ -125,8 +129,9 @@ class TypeMetadata { // Retrieve event by name [[nodiscard]] auto getEvent(const std::string& name) const -> std::optional { - if (auto it = m_events_.find(name); it != m_events_.end()) { - return &it->second; + if (auto eventIter = m_events_.find(name); + eventIter != m_events_.end()) { + return &eventIter->second; } return std::nullopt; } @@ -154,8 +159,9 @@ class TypeRegistry { [[nodiscard]] auto getMetadata(const std::string& name) const -> std::optional { std::shared_lock lock(m_mutex_); - if (auto it = m_registry_.find(name); it != m_registry_.end()) { - return it->second; + if (auto registryIter = m_registry_.find(name); + registryIter != m_registry_.end()) { + return registryIter->second; } return std::nullopt; } @@ -168,6 +174,7 @@ inline auto callMethod(BoxedValue& obj, const std::string& method_name, TypeRegistry::instance().getMetadata(obj.getTypeInfo().name()); metadata) { if (auto methods = metadata->getMethods(method_name); methods) { +#pragma unroll for (const auto& method : **methods) { // TODO: FIX ME - 参数类型匹配逻辑: // 确保传入的参数与方法期望的参数类型一致 @@ -283,4 +290,4 @@ class TypeRegistrar { } // namespace atom::meta -#endif // ATOM_META_ANYMETA_HPP +#endif // ATOM_META_ANYMETA_HPP \ No newline at end of file diff --git a/src/atom/function/bind_first.hpp b/src/atom/function/bind_first.hpp index fcc74b58..26414d8a 100644 --- a/src/atom/function/bind_first.hpp +++ b/src/atom/function/bind_first.hpp @@ -15,23 +15,23 @@ namespace atom::meta { template -constexpr auto getPointer(T *t) noexcept -> T * { - return t; +constexpr auto getPointer(T *ptr) noexcept -> T * { + return ptr; } template -auto getPointer(const std::reference_wrapper &t) noexcept -> T * { - return &t.get(); +auto getPointer(const std::reference_wrapper &ref) noexcept -> T * { + return &ref.get(); } template -constexpr auto getPointer(const T &t) noexcept -> const T * { - return &t; +constexpr auto getPointer(const T &ref) noexcept -> const T * { + return &ref; } template -constexpr auto removeConstPointer(const T *t) noexcept -> T * { - return const_cast(t); +constexpr auto removeConstPointer(const T *ptr) noexcept -> T * { + return const_cast(ptr); } template @@ -41,10 +41,10 @@ template concept nothrow_invocable = std::is_nothrow_invocable_v; template -constexpr bool is_invocable_v = invocable; +constexpr bool IS_INVOCABLE_V = invocable; template -constexpr bool is_nothrow_invocable_v = std::is_nothrow_invocable_v; +constexpr bool IS_NOTHROW_INVOCABLE_V = std::is_nothrow_invocable_v; template constexpr auto bindFirst(Ret (*func)(P1, Param...), O &&object) @@ -85,12 +85,13 @@ auto bindFirst(const std::function &func, O &&object) template -constexpr auto bindFirst(const F &fo, O &&object, +constexpr auto bindFirst(const F &funcObj, O &&object, Ret (Class::*func)(P1, Param...) const) requires invocable { - return [fo, object = std::forward(object), func](Param... param) -> Ret { - return (fo.*func)(object, std::forward(param)...); + return [funcObj, object = std::forward(object), + func](Param... param) -> Ret { + return (funcObj.*func)(object, std::forward(param)...); }; } @@ -113,4 +114,4 @@ constexpr auto bindFirst(F &&func, O &&object) } } // namespace atom::meta -#endif // ATOM_META_BIND_FIRST_HPP +#endif // ATOM_META_BIND_FIRST_HPP \ No newline at end of file diff --git a/src/atom/function/concept.hpp b/src/atom/function/concept.hpp index 9829a8cf..84514c0d 100644 --- a/src/atom/function/concept.hpp +++ b/src/atom/function/concept.hpp @@ -27,28 +27,30 @@ // Checks if a type can be invoked with specified argument types template -concept Invocable = requires(F f, Args&&... args) { - { std::invoke(f, std::forward(args)...) }; +concept Invocable = requires(F func, Args&&... args) { + { std::invoke(func, std::forward(args)...) }; }; // Checks if a type can be invoked with specified argument types and returns a // result convertible to R template -concept InvocableR = requires(F f, Args&&... args) { - { std::invoke(f, std::forward(args)...) } -> std::convertible_to; +concept InvocableR = requires(F func, Args&&... args) { + { + std::invoke(func, std::forward(args)...) + } -> std::convertible_to; }; // Similar to Invocable but checks for noexcept template -concept NothrowInvocable = requires(F f, Args&&... args) { - { std::invoke(f, std::forward(args)...) } noexcept; +concept NothrowInvocable = requires(F func, Args&&... args) { + { std::invoke(func, std::forward(args)...) } noexcept; }; // Similar to InvocableR but checks for noexcept template -concept NothrowInvocableR = requires(F f, Args&&... args) { +concept NothrowInvocableR = requires(F func, Args&&... args) { { - std::invoke(f, std::forward(args)...) + std::invoke(func, std::forward(args)...) } noexcept -> std::convertible_to; }; @@ -57,7 +59,7 @@ template concept FunctionPointer = std::is_function_v>; template -concept Callable = requires(T t) { +concept Callable = requires(T obj) { { std::function{std::declval()} }; }; @@ -68,8 +70,8 @@ concept CallableReturns = std::is_invocable_r_v; // Checks if a callable type can be invoked with a given set of arguments and is // noexcept template -concept CallableNoexcept = requires(T t, Args&&... args) { - { t(std::forward(args)...) } noexcept; +concept CallableNoexcept = requires(T obj, Args&&... args) { + { obj(std::forward(args)...) } noexcept; }; // Checks if a type is a std::function of any signature @@ -85,57 +87,57 @@ concept StdFunction = requires { // ----------------------------------------------------------------------------- template -concept Relocatable = requires(T t) { +concept Relocatable = requires(T obj) { { std::is_nothrow_move_constructible_v } -> std::convertible_to; { std::is_nothrow_move_assignable_v } -> std::convertible_to; }; template -concept DefaultConstructible = requires(T t) { +concept DefaultConstructible = requires(T obj) { { T() } -> std::same_as; }; template -concept CopyConstructible = requires(T t) { - { T(t) } -> std::same_as; +concept CopyConstructible = requires(T obj) { + { T(obj) } -> std::same_as; }; template -concept CopyAssignable = requires(T t) { - { t = t } -> std::same_as; +concept CopyAssignable = requires(T obj) { + { obj = obj } -> std::same_as; }; template -concept MoveAssignable = requires(T t) { - { t = std::move(t) } -> std::same_as; +concept MoveAssignable = requires(T obj) { + { obj = std::move(obj) } -> std::same_as; }; template -concept EqualityComparable = requires(T t) { - { t == t } -> std::convertible_to; - { t != t } -> std::convertible_to; +concept EqualityComparable = requires(T obj) { + { obj == obj } -> std::convertible_to; + { obj != obj } -> std::convertible_to; }; template -concept LessThanComparable = requires(T t) { - { t < t } -> std::convertible_to; +concept LessThanComparable = requires(T obj) { + { obj < obj } -> std::convertible_to; }; template -concept Hashable = requires(T t) { - { std::hash{}(t) } -> std::convertible_to; +concept Hashable = requires(T obj) { + { std::hash{}(obj) } -> std::convertible_to; }; template -concept Swappable = requires(T t) { std::swap(t, t); }; +concept Swappable = requires(T obj) { std::swap(obj, obj); }; template concept Copyable = std::is_copy_constructible_v && std::is_copy_assignable_v; template -concept Destructible = requires(T t) { - { t.~T() } -> std::same_as; +concept Destructible = requires(T obj) { + { obj.~T() } -> std::same_as; }; // ----------------------------------------------------------------------------- @@ -169,7 +171,7 @@ concept Number = Arithmetic || Integral || FloatingPoint; #include // Checks if a type is a complex number (in header) template -concept ComplexNumber = requires(T x) { +concept ComplexNumber = requires(T obj) { typename T::value_type; requires std::is_same_v>; }; @@ -214,11 +216,11 @@ concept NotAssociativeOrSequenceContainer = !NotSequenceContainer; template -concept String = NotAssociativeOrSequenceContainer && requires(T x) { - { x.size() } -> std::convertible_to; - { x.empty() } -> std::convertible_to; - { x.begin() } -> std::convertible_to; - { x.end() } -> std::convertible_to; +concept String = NotAssociativeOrSequenceContainer && requires(T obj) { + { obj.size() } -> std::convertible_to; + { obj.empty() } -> std::convertible_to; + { obj.begin() } -> std::convertible_to; + { obj.end() } -> std::convertible_to; }; template @@ -234,19 +236,19 @@ concept Pointer = std::is_pointer_v; // Checks if a type is a std::unique_ptr of any type template -concept UniquePointer = requires(T x) { +concept UniquePointer = requires(T obj) { requires std::is_same_v>; }; // Checks if a type is a std::shared_ptr of any type template -concept SharedPointer = requires(T x) { +concept SharedPointer = requires(T obj) { requires std::is_same_v>; }; // Checks if a type is a std::weak_ptr of any type template -concept WeakPointer = requires(T x) { +concept WeakPointer = requires(T obj) { requires std::is_same_v>; }; @@ -286,35 +288,35 @@ concept TriviallyCopyable = // Checks if a type supports begin() and end() template -concept Iterable = requires(T x) { - { x.begin() } -> std::forward_iterator; - { x.end() } -> std::forward_iterator; +concept Iterable = requires(T obj) { + { obj.begin() } -> std::forward_iterator; + { obj.end() } -> std::forward_iterator; }; // Checks if a type is a standard container with size, begin, and end template -concept Container = requires(T x) { - { x.size() } -> std::convertible_to; +concept Container = requires(T obj) { + { obj.size() } -> std::convertible_to; requires Iterable; }; template -concept StringContainer = requires(T t) { +concept StringContainer = requires(T obj) { typename T::value_type; String || Char; - { t.push_back(std::declval()) }; + { obj.push_back(std::declval()) }; }; template -concept NumberContainer = requires(T t) { +concept NumberContainer = requires(T obj) { typename T::value_type; Number; - { t.push_back(std::declval()) }; + { obj.push_back(std::declval()) }; }; // Checks if a type is an associative container like map or set template -concept AssociativeContainer = requires(T x) { +concept AssociativeContainer = requires(T obj) { typename T::key_type; typename T::mapped_type; requires Container; @@ -322,13 +324,13 @@ concept AssociativeContainer = requires(T x) { // Concept to check if a type is an iterator template -concept Iterator = requires(T it) { +concept Iterator = requires(T iter) { { - *it + *iter } -> std::convertible_to::value_type>; - { ++it } -> std::same_as; - { it++ } -> std::convertible_to; + { ++iter } -> std::same_as; + { iter++ } -> std::convertible_to; }; #endif -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/conversion.hpp b/src/atom/function/conversion.hpp index 4fdba1ca..62af7454 100644 --- a/src/atom/function/conversion.hpp +++ b/src/atom/function/conversion.hpp @@ -1,11 +1,3 @@ -/*! - * \file conversion.hpp - * \brief C++ Type Conversion - * \author Max Qian - * \date 2024-03-01 - * \copyright Copyright (C) 2023-2024 Max Qian - */ - #ifndef ATOM_META_CONVERSION_HPP #define ATOM_META_CONVERSION_HPP @@ -39,7 +31,7 @@ class TypeConversionBase { public: ATOM_NODISCARD virtual auto convert(const std::any& from) const -> std::any = 0; - ATOM_NODISCARD virtual auto convertDown(const std::any& to) const + ATOM_NODISCARD virtual auto convertDown(const std::any& toAny) const -> std::any = 0; ATOM_NODISCARD virtual auto to() const ATOM_NOEXCEPT -> const TypeInfo& { @@ -49,18 +41,27 @@ class TypeConversionBase { return fromType; } + ATOM_NODISCARD auto getFromType() const ATOM_NOEXCEPT -> const TypeInfo& { + return fromType; + } + ATOM_NODISCARD virtual auto bidir() const ATOM_NOEXCEPT -> bool { return true; } virtual ~TypeConversionBase() = default; - TypeInfo toType; - TypeInfo fromType; + TypeConversionBase(const TypeConversionBase&) = default; + TypeConversionBase& operator=(const TypeConversionBase&) = default; + TypeConversionBase(TypeConversionBase&&) = default; + TypeConversionBase& operator=(TypeConversionBase&&) = default; protected: - TypeConversionBase(const TypeInfo& to, const TypeInfo& from) - : toType(to), fromType(from) {} + TypeConversionBase(const TypeInfo& toTypeInfo, const TypeInfo& fromTypeInfo) + : toType(toTypeInfo), fromType(fromTypeInfo) {} + + TypeInfo toType; + TypeInfo fromType; }; template @@ -92,18 +93,18 @@ class StaticConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { // Pointer types static conversion (downcasting) try { if constexpr (std::is_pointer_v && std::is_pointer_v) { - auto toPtr = std::any_cast(to); + auto toPtr = std::any_cast(toAny); return std::any(static_cast(toPtr)); } // Reference types static conversion (downcasting) else if constexpr (std::is_reference_v && std::is_reference_v) { - auto& toRef = std::any_cast(to); + auto& toRef = std::any_cast(toAny); return std::any(static_cast(toRef)); } else { @@ -150,11 +151,11 @@ class DynamicConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { // Pointer types dynamic conversion if constexpr (std::is_pointer_v && std::is_pointer_v) { - auto toPtr = std::any_cast(to); + auto toPtr = std::any_cast(toAny); auto convertedPtr = dynamic_cast(toPtr); if (!convertedPtr && toPtr != nullptr) { throw std::bad_cast(); @@ -165,7 +166,7 @@ class DynamicConversion : public TypeConversionBase { else if constexpr (std::is_reference_v && std::is_reference_v) { try { - auto& toRef = std::any_cast(to); + auto& toRef = std::any_cast(toAny); return std::any(dynamic_cast(toRef)); } catch (const std::bad_cast&) { THROW_CONVERSION_ERROR("Failed to convert ", toType.name(), @@ -220,10 +221,10 @@ class VectorConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { try { - const auto& toVec = std::any_cast&>(to); + const auto& toVec = std::any_cast&>(toAny); std::vector fromVec; fromVec.reserve(toVec.size()); @@ -254,7 +255,7 @@ class MapConversion : public TypeConversionBase { : TypeConversionBase(userType>(), userType>()) {} - ATOM_NODISCARD auto convert(const std::any& from) const + [[nodiscard]] auto convert(const std::any& from) const -> std::any override { try { const auto& fromMap = std::any_cast&>(from); @@ -277,10 +278,10 @@ class MapConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { try { - const auto& toMap = std::any_cast&>(to); + const auto& toMap = std::any_cast&>(toAny); MapType fromMap; for (const auto& [key, value] : toMap) { @@ -308,7 +309,7 @@ class SequenceConversion : public TypeConversionBase { : TypeConversionBase(userType>(), userType>()) {} - ATOM_NODISCARD auto convert(const std::any& from) const + [[nodiscard]] auto convert(const std::any& from) const -> std::any override { try { const auto& fromSeq = std::any_cast&>(from); @@ -331,10 +332,10 @@ class SequenceConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { try { - const auto& toSeq = std::any_cast&>(to); + const auto& toSeq = std::any_cast&>(toAny); SeqType fromSeq; for (const auto& elem : toSeq) { @@ -362,7 +363,7 @@ class SetConversion : public TypeConversionBase { : TypeConversionBase(userType>(), userType>()) {} - ATOM_NODISCARD auto convert(const std::any& from) const + [[nodiscard]] auto convert(const std::any& from) const -> std::any override { try { const auto& fromSet = std::any_cast&>(from); @@ -384,10 +385,10 @@ class SetConversion : public TypeConversionBase { } } - ATOM_NODISCARD auto convertDown(const std::any& to) const + ATOM_NODISCARD auto convertDown(const std::any& toAny) const -> std::any override { try { - const auto& toSet = std::any_cast&>(to); + const auto& toSet = std::any_cast&>(toAny); SetType fromSet; for (const auto& elem : toSet) { @@ -417,12 +418,12 @@ class TypeConversions { } void addConversion(const std::shared_ptr& conversion) { - auto key = conversion->fromType; + auto key = conversion->getFromType(); conversions_[key].push_back(conversion); } template - auto convert(const std::any& from) const -> std::any { + [[nodiscard]] auto convert(const std::any& from) const -> std::any { auto fromType = userType(); auto toType = userType(); @@ -441,10 +442,11 @@ class TypeConversions { THROW_CONVERSION_ERROR(fromType.name(), toType.name()); } - auto canConvert(const TypeInfo& from, const TypeInfo& to) const -> bool { - if (conversions_.contains(from)) { - for (const auto& conv : conversions_.at(from)) { - if (conv->toType == to) { + [[nodiscard]] auto canConvert(const TypeInfo& fromTypeInfo, + const TypeInfo& toTypeInfo) const -> bool { + if (conversions_.contains(fromTypeInfo)) { + for (const auto& conv : conversions_.at(fromTypeInfo)) { + if (conv->to() == toTypeInfo) { return true; } } diff --git a/src/atom/function/decorate.hpp b/src/atom/function/decorate.hpp index 4960eb53..760b671e 100644 --- a/src/atom/function/decorate.hpp +++ b/src/atom/function/decorate.hpp @@ -19,12 +19,9 @@ #include #include -#include "func_traits.hpp" // Ensure this is implemented properly -#include "macro.hpp" +#include "func_traits.hpp" namespace atom::meta { - -// Concept for checking if a type is callable, adjusted for better utility template concept Callable = requires(F&& func, Args&&... args) { { @@ -77,22 +74,22 @@ struct decorator { using CallbackType = std::conditional_t, std::function, std::function>; - FuncType func; private: + FuncType func_; std::function before_; CallbackType callback_; std::function after_; public: - explicit decorator(FuncType func) : func(std::move(func)) {} + explicit decorator(FuncType func) : func_(std::move(func)) {} template > auto withHooks( Before&& before, Callback&& callback = CallbackType(), After&& after = [](auto) {}) const -> decorator { - decorator copy(func); + decorator copy(func_); copy.before_ = std::forward(before); copy.callback_ = std::forward(callback); copy.after_ = std::forward(after); @@ -108,12 +105,12 @@ struct decorator { auto start = std::chrono::high_resolution_clock::now(); if constexpr (std::is_void_v) { - std::invoke(func, std::get(args)...); + std::invoke(func_, std::get(args)...); if (callback_) { callback_(); } } else { - auto result = std::invoke(func, std::get(args)...); + auto result = std::invoke(func_, std::get(args)...); if (callback_) { callback_(result); } @@ -136,7 +133,7 @@ struct decorator { auto tupleArgs = std::make_tuple(std::forward(args)...); return callImpl(tupleArgs, std::make_index_sequence{}); } -} ATOM_ALIGNAS(128); +}; template auto makeDecorator(F&& func) { @@ -153,6 +150,7 @@ struct LoopDecorator : public decorator { using ReturnType = decltype(this->func(args...)); std::optional result; +#pragma unroll for (int i = 0; i < loopCount; ++i) { if constexpr (std::is_void_v) { Base::operator()(std::forward(args)...); @@ -168,9 +166,9 @@ struct LoopDecorator : public decorator { }; template -auto makeLoopDecorator(FuncType&& f) { +auto makeLoopDecorator(FuncType&& func) { return LoopDecorator>( - std::forward(f)); + std::forward(func)); } template @@ -189,12 +187,12 @@ struct ConditionCheckDecorator : public decorator { return ReturnType{}; } } -} __attribute__((aligned(1))); +}; template -auto makeConditionCheckDecorator(FuncType&& f) { +auto makeConditionCheckDecorator(FuncType&& func) { return ConditionCheckDecorator>( - std::forward(f)); + std::forward(func)); } class DecoratorError : public std::exception { @@ -214,6 +212,18 @@ class BaseDecorator { virtual auto operator()(FuncType func, Args... args) -> R = 0; // Changed from Args&&... virtual ~BaseDecorator() = default; + + // Define copy constructor + BaseDecorator(const BaseDecorator& other) = default; + + // Define copy assignment operator + auto operator=(const BaseDecorator& other) -> BaseDecorator& = default; + + // Define move constructor + BaseDecorator(BaseDecorator&& other) noexcept = default; + + // Define move assignment operator + auto operator=(BaseDecorator&& other) noexcept -> BaseDecorator& = default; }; template @@ -241,6 +251,7 @@ class DecorateStepper { try { FuncType currentFunction = baseFunction_; +#pragma unroll for (auto it = decorators_.rbegin(); it != decorators_.rend(); ++it) { auto& decorator = *it; @@ -274,4 +285,4 @@ auto makeDecorateStepper(Func&& func) { } // namespace atom::meta -#endif // ATOM_META_DECORATE_HPP +#endif // ATOM_META_DECORATE_HPP \ No newline at end of file diff --git a/src/atom/function/field_count.hpp b/src/atom/function/field_count.hpp index ff4f3d0a..20356503 100644 --- a/src/atom/function/field_count.hpp +++ b/src/atom/function/field_count.hpp @@ -24,19 +24,19 @@ struct Any { * \return An instance of type T. */ template - consteval operator T() const noexcept; + explicit consteval operator T() const noexcept; }; /*! * \brief Checks if a type T is constructible with braces. * \tparam T The type to check. * \tparam I The index sequence. - * \param[in] std::index_sequence The index sequence. + * \param[in] indices The index sequence. * \return True if T is constructible with braces, false otherwise. */ template -consteval auto isBracesConstructible(std::index_sequence) noexcept - -> bool { +consteval auto isBracesConstructible( + std::index_sequence /*indices*/) noexcept -> bool { return requires { T{((void)I, std::declval())...}; }; } @@ -94,4 +94,4 @@ consteval auto fieldCountOf() noexcept -> std::size_t { } // namespace atom::meta -#endif // ATOM_META_FIELD_COUNT_HPP +#endif // ATOM_META_FIELD_COUNT_HPP \ No newline at end of file diff --git a/src/atom/function/global_ptr.cpp b/src/atom/function/global_ptr.cpp index 30cfc669..cfec2887 100644 --- a/src/atom/function/global_ptr.cpp +++ b/src/atom/function/global_ptr.cpp @@ -19,42 +19,42 @@ auto GlobalSharedPtrManager::getInstance() -> GlobalSharedPtrManager & { } void GlobalSharedPtrManager::removeSharedPtr(const std::string &key) { - std::unique_lock lock(mtx); - sharedPtrMap.erase(key); + std::unique_lock lock(mutex_); + shared_ptr_map_.erase(key); } void GlobalSharedPtrManager::removeExpiredWeakPtrs() { - std::unique_lock lock(mtx); - auto it = sharedPtrMap.begin(); - while (it != sharedPtrMap.end()) { + std::unique_lock lock(mutex_); + auto iter = shared_ptr_map_.begin(); + while (iter != shared_ptr_map_.end()) { try { - if (std::any_cast>(it->second).expired()) { - it = sharedPtrMap.erase(it); + if (std::any_cast>(iter->second).expired()) { + iter = shared_ptr_map_.erase(iter); } else { - ++it; + ++iter; } } catch (const std::bad_any_cast &) { - ++it; + ++iter; } } } void GlobalSharedPtrManager::clearAll() { - std::unique_lock lock(mtx); - sharedPtrMap.clear(); + std::unique_lock lock(mutex_); + shared_ptr_map_.clear(); } auto GlobalSharedPtrManager::size() const -> size_t { - std::shared_lock lock(mtx); - return sharedPtrMap.size(); + std::shared_lock lock(mutex_); + return shared_ptr_map_.size(); } void GlobalSharedPtrManager::printSharedPtrMap() const { - std::shared_lock lock(mtx); + std::shared_lock lock(mutex_); #if ENABLE_DEBUG std::cout << "GlobalSharedPtrManager:\n"; - for (const auto &pair : sharedPtrMap) { + for (const auto &pair : shared_ptr_map_) { std::cout << " " << pair.first << "\n"; } #endif -} +} \ No newline at end of file diff --git a/src/atom/function/global_ptr.hpp b/src/atom/function/global_ptr.hpp index 2a83b15a..c0dffb46 100644 --- a/src/atom/function/global_ptr.hpp +++ b/src/atom/function/global_ptr.hpp @@ -200,24 +200,24 @@ class GlobalSharedPtrManager : public NonCopyable { private: #if ENABLE_FASTHASH - emhash8::HashMap sharedPtrMap; + emhash8::HashMap shared_ptr_map_; #else std::unordered_map - sharedPtrMap; /**< The map that stores the shared pointers and weak + shared_ptr_map_; /**< The map that stores the shared pointers and weak pointers. */ #endif - mutable std::shared_mutex mtx; /**< The mutex used for thread-safe access to - the shared pointer map. */ + mutable std::shared_mutex mutex_; /**< The mutex used for thread-safe access + to the shared pointer map. */ }; template auto GlobalSharedPtrManager::getSharedPtr(const std::string &key) -> std::optional> { - std::shared_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::shared_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - return std::any_cast>(it->second); + return std::any_cast>(iter->second); } catch (const std::bad_any_cast &) { return std::nullopt; } @@ -228,20 +228,20 @@ auto GlobalSharedPtrManager::getSharedPtr(const std::string &key) template auto GlobalSharedPtrManager::getOrCreateSharedPtr( const std::string &key, CreatorFunc creator) -> std::shared_ptr { - std::unique_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::unique_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - return std::any_cast>(it->second); + return std::any_cast>(iter->second); } catch (const std::bad_any_cast &) { // Key exists but the stored type does not match, replace it auto ptr = creator(); - sharedPtrMap[key] = ptr; + shared_ptr_map_[key] = ptr; return ptr; } } else { auto ptr = creator(); - sharedPtrMap[key] = ptr; + shared_ptr_map_[key] = ptr; return ptr; } } @@ -249,11 +249,11 @@ auto GlobalSharedPtrManager::getOrCreateSharedPtr( template auto GlobalSharedPtrManager::getWeakPtr(const std::string &key) -> std::weak_ptr { - std::shared_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::shared_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - return std::any_cast>(it->second); + return std::any_cast>(iter->second); } catch (const std::bad_any_cast &) { return std::weak_ptr(); } @@ -264,25 +264,25 @@ auto GlobalSharedPtrManager::getWeakPtr(const std::string &key) template void GlobalSharedPtrManager::addSharedPtr(const std::string &key, std::shared_ptr sharedPtr) { - std::unique_lock lock(mtx); - sharedPtrMap[key] = std::move(sharedPtr); + std::unique_lock lock(mutex_); + shared_ptr_map_[key] = std::move(sharedPtr); } template void GlobalSharedPtrManager::addWeakPtr(const std::string &key, const std::weak_ptr &weakPtr) { - std::unique_lock lock(mtx); - sharedPtrMap[key] = weakPtr; + std::unique_lock lock(mutex_); + shared_ptr_map_[key] = weakPtr; } template auto GlobalSharedPtrManager::getSharedPtrFromWeakPtr(const std::string &key) -> std::shared_ptr { - std::shared_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::shared_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - return std::any_cast>(it->second).lock(); + return std::any_cast>(iter->second).lock(); } catch (const std::bad_any_cast &) { return std::shared_ptr(); } @@ -291,13 +291,14 @@ auto GlobalSharedPtrManager::getSharedPtrFromWeakPtr(const std::string &key) } template -std::weak_ptr GlobalSharedPtrManager::getWeakPtrFromSharedPtr( - const std::string &key) { - std::shared_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { +auto GlobalSharedPtrManager::getWeakPtrFromSharedPtr(const std::string &key) + -> std::weak_ptr { + std::shared_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - return std::weak_ptr(std::any_cast>(it->second)); + return std::weak_ptr( + std::any_cast>(iter->second)); } catch (const std::bad_any_cast &) { return std::weak_ptr(); } @@ -308,13 +309,13 @@ std::weak_ptr GlobalSharedPtrManager::getWeakPtrFromSharedPtr( template void GlobalSharedPtrManager::addDeleter( const std::string &key, const std::function &deleter) { - std::unique_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::unique_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - auto ptr = std::any_cast>(it->second); + auto ptr = std::any_cast>(iter->second); ptr.reset(ptr.get(), deleter); - sharedPtrMap[key] = ptr; + shared_ptr_map_[key] = ptr; } catch (const std::bad_any_cast &) { // Ignore if the stored type does not match } @@ -323,17 +324,18 @@ void GlobalSharedPtrManager::addDeleter( template void GlobalSharedPtrManager::deleteObject(const std::string &key, T *ptr) { - std::unique_lock lock(mtx); - auto it = sharedPtrMap.find(key); - if (it != sharedPtrMap.end()) { + std::unique_lock lock(mutex_); + auto iter = shared_ptr_map_.find(key); + if (iter != shared_ptr_map_.end()) { try { - auto deleter = std::any_cast>(it->second); + auto deleter = + std::any_cast>(iter->second); deleter(ptr); } catch (const std::bad_any_cast &) { delete ptr; // Use default delete if no custom deleter is found } - sharedPtrMap.erase(it); + shared_ptr_map_.erase(iter); } } -#endif // ATOM_META_GLOBAL_PTR_HPP +#endif // ATOM_META_GLOBAL_PTR_HPP \ No newline at end of file diff --git a/src/atom/function/god.hpp b/src/atom/function/god.hpp index f4af7bc7..5e069368 100644 --- a/src/atom/function/god.hpp +++ b/src/atom/function/god.hpp @@ -25,431 +25,451 @@ ATOM_INLINE void blessNoBugs() {} * \brief Casts a value from one type to another. * \tparam To The type to cast to. * \tparam From The type to cast from. - * \param f The value to cast. + * \param fromValue The value to cast. * \return The casted value. */ template -constexpr auto cast(From&& f) -> To { - return static_cast(std::forward(f)); +constexpr auto cast(From&& fromValue) -> To { + return static_cast(std::forward(fromValue)); } /*! * \brief Aligns a value up to the nearest multiple of A. - * \tparam A The alignment value, must be a power of 2. - * \tparam X The type of the value to align. - * \param x The value to align. + * \tparam Alignment The alignment value, must be a power of 2. + * \tparam ValueType The type of the value to align. + * \param value The value to align. * \return The aligned value. */ -template -constexpr auto alignUp(X x) -> X { - static_assert((A & (A - 1)) == 0, "A must be power of 2"); - return (x + static_cast(A - 1)) & ~static_cast(A - 1); +template +constexpr auto alignUp(ValueType value) -> ValueType { + static_assert((Alignment & (Alignment - 1)) == 0, + "Alignment must be power of 2"); + return (value + static_cast(Alignment - 1)) & + ~static_cast(Alignment - 1); } /*! * \brief Aligns a pointer up to the nearest multiple of A. - * \tparam A The alignment value, must be a power of 2. - * \tparam X The type of the pointer to align. - * \param x The pointer to align. + * \tparam Alignment The alignment value, must be a power of 2. + * \tparam PointerType The type of the pointer to align. + * \param pointer The pointer to align. * \return The aligned pointer. */ -template -constexpr auto alignUp(X* x) -> X* { - return reinterpret_cast(alignUp(reinterpret_cast(x))); +template +constexpr auto alignUp(PointerType* pointer) -> PointerType* { + return reinterpret_cast( + alignUp(reinterpret_cast(pointer))); } /*! - * \brief Aligns a value up to the nearest multiple of a. - * \tparam X The type of the value to align. - * \tparam A The type of the alignment value, must be integral. - * \param x The value to align. - * \param a The alignment value. + * \brief Aligns a value up to the nearest multiple of alignment. + * \tparam ValueType The type of the value to align. + * \tparam AlignmentType The type of the alignment value, must be integral. + * \param value The value to align. + * \param alignment The alignment value. * \return The aligned value. */ -template -constexpr auto alignUp(X x, A a) -> X { - static_assert(std::is_integral::value, "A must be integral type"); - return (x + static_cast(a - 1)) & ~static_cast(a - 1); +template +constexpr auto alignUp(ValueType value, AlignmentType alignment) -> ValueType { + static_assert(std::is_integral::value, + "Alignment must be integral type"); + return (value + static_cast(alignment - 1)) & + ~static_cast(alignment - 1); } /*! - * \brief Aligns a pointer up to the nearest multiple of a. - * \tparam X The type of the pointer to align. - * \tparam A The type of the alignment value, must be integral. - * \param x The pointer to align. - * \param a The alignment value. + * \brief Aligns a pointer up to the nearest multiple of alignment. + * \tparam PointerType The type of the pointer to align. + * \tparam AlignmentType The type of the alignment value, must be integral. + * \param pointer The pointer to align. + * \param alignment The alignment value. * \return The aligned pointer. */ -template -constexpr auto alignUp(X* x, A a) -> X* { - return reinterpret_cast(alignUp(reinterpret_cast(x), a)); +template +constexpr auto alignUp(PointerType* pointer, + AlignmentType alignment) -> PointerType* { + return reinterpret_cast( + alignUp(reinterpret_cast(pointer), alignment)); } /*! * \brief Aligns a value down to the nearest multiple of A. - * \tparam A The alignment value, must be a power of 2. - * \tparam X The type of the value to align. - * \param x The value to align. + * \tparam Alignment The alignment value, must be a power of 2. + * \tparam ValueType The type of the value to align. + * \param value The value to align. * \return The aligned value. */ -template -constexpr auto alignDown(X x) -> X { - static_assert((A & (A - 1)) == 0, "A must be power of 2"); - return x & ~static_cast(A - 1); +template +constexpr auto alignDown(ValueType value) -> ValueType { + static_assert((Alignment & (Alignment - 1)) == 0, + "Alignment must be power of 2"); + return value & ~static_cast(Alignment - 1); } /*! * \brief Aligns a pointer down to the nearest multiple of A. - * \tparam A The alignment value, must be a power of 2. - * \tparam X The type of the pointer to align. - * \param x The pointer to align. + * \tparam Alignment The alignment value, must be a power of 2. + * \tparam PointerType The type of the pointer to align. + * \param pointer The pointer to align. * \return The aligned pointer. */ -template -constexpr auto alignDown(X* x) -> X* { - return reinterpret_cast(alignDown(reinterpret_cast(x))); +template +constexpr auto alignDown(PointerType* pointer) -> PointerType* { + return reinterpret_cast( + alignDown(reinterpret_cast(pointer))); } /*! - * \brief Aligns a value down to the nearest multiple of a. - * \tparam X The type of the value to align. - * \tparam A The type of the alignment value, must be integral. - * \param x The value to align. - * \param a The alignment value. + * \brief Aligns a value down to the nearest multiple of alignment. + * \tparam ValueType The type of the value to align. + * \tparam AlignmentType The type of the alignment value, must be integral. + * \param value The value to align. + * \param alignment The alignment value. * \return The aligned value. */ -template -constexpr auto alignDown(X x, A a) -> X { - static_assert(std::is_integral::value, "A must be integral type"); - return x & ~static_cast(a - 1); +template +constexpr auto alignDown(ValueType value, + AlignmentType alignment) -> ValueType { + static_assert(std::is_integral::value, + "Alignment must be integral type"); + return value & ~static_cast(alignment - 1); } /*! - * \brief Aligns a pointer down to the nearest multiple of a. - * \tparam X The type of the pointer to align. - * \tparam A The type of the alignment value, must be integral. - * \param x The pointer to align. - * \param a The alignment value. + * \brief Aligns a pointer down to the nearest multiple of alignment. + * \tparam PointerType The type of the pointer to align. + * \tparam AlignmentType The type of the alignment value, must be integral. + * \param pointer The pointer to align. + * \param alignment The alignment value. * \return The aligned pointer. */ -template -constexpr auto alignDown(X* x, A a) -> X* { - return reinterpret_cast(alignDown(reinterpret_cast(x), a)); +template +constexpr auto alignDown(PointerType* pointer, + AlignmentType alignment) -> PointerType* { + return reinterpret_cast( + alignDown(reinterpret_cast(pointer), alignment)); } /*! * \brief Computes the base-2 logarithm of an integral value. - * \tparam T The type of the value, must be integral. - * \param x The value to compute the logarithm of. + * \tparam IntegralType The type of the value, must be integral. + * \param value The value to compute the logarithm of. * \return The base-2 logarithm of the value. */ -template -constexpr auto log2(T x) -> T { - static_assert(std::is_integral::value, "T must be integral type"); - return x <= 1 ? 0 : 1 + atom::meta::log2(x >> 1); +template +constexpr auto log2(IntegralType value) -> IntegralType { + static_assert(std::is_integral::value, + "IntegralType must be integral type"); + return value <= 1 ? 0 : 1 + atom::meta::log2(value >> 1); } /*! * \brief Computes the number of blocks of size N needed to cover a value. - * \tparam N The block size, must be a power of 2. - * \tparam X The type of the value. - * \param x The value to compute the number of blocks for. + * \tparam BlockSize The block size, must be a power of 2. + * \tparam ValueType The type of the value. + * \param value The value to compute the number of blocks for. * \return The number of blocks needed to cover the value. */ -template -constexpr auto nb(X x) -> X { - static_assert((N & (N - 1)) == 0, "N must be power of 2"); - return (x >> atom::meta::log2(static_cast(N))) + - !!(x & static_cast(N - 1)); +template +constexpr auto nb(ValueType value) -> ValueType { + static_assert((BlockSize & (BlockSize - 1)) == 0, + "BlockSize must be power of 2"); + return (value >> atom::meta::log2(static_cast(BlockSize))) + + !!(value & static_cast(BlockSize - 1)); } /*! * \brief Compares two values for equality. - * \tparam T The type of the values. - * \param p Pointer to the first value. - * \param q Pointer to the second value. + * \tparam ValueType The type of the values. + * \param first Pointer to the first value. + * \param second Pointer to the second value. * \return True if the values are equal, false otherwise. */ -template -ATOM_INLINE auto eq(const void* p, const void* q) -> bool { - return *reinterpret_cast(p) == *reinterpret_cast(q); +template +ATOM_INLINE auto eq(const void* first, const void* second) -> bool { + return *reinterpret_cast(first) == + *reinterpret_cast(second); } /*! * \brief Copies N bytes from src to dst. - * \tparam N The number of bytes to copy. - * \param dst Pointer to the destination. - * \param src Pointer to the source. - */ -template -ATOM_INLINE void copy(void* dst, const void* src) { - if constexpr (N > 0) { - std::memcpy(dst, src, N); + * \tparam NumBytes The number of bytes to copy. + * \param destination Pointer to the destination. + * \param source Pointer to the source. + */ +template +ATOM_INLINE void copy(void* destination, const void* source) { + if constexpr (NumBytes > 0) { + std::memcpy(destination, source, NumBytes); } } /*! - * \brief Specialization of copy for N = 0, does nothing. + * \brief Specialization of copy for NumBytes = 0, does nothing. */ template <> ATOM_INLINE void copy<0>(void*, const void*) {} /*! - * \brief Swaps the value pointed to by p with v. - * \tparam T The type of the value pointed to by p. - * \tparam V The type of the value v. - * \param p Pointer to the value to swap. - * \param v The value to swap with. - * \return The original value pointed to by p. + * \brief Swaps the value pointed to by pointer with value. + * \tparam PointerType The type of the value pointed to by pointer. + * \tparam ValueType The type of the value. + * \param pointer Pointer to the value to swap. + * \param value The value to swap with. + * \return The original value pointed to by pointer. */ -template -ATOM_INLINE auto swap(T* p, V v) -> T { - T x = *p; - *p = static_cast(v); - return x; +template +ATOM_INLINE auto swap(PointerType* pointer, ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer = static_cast(value); + return originalValue; } /*! - * \brief Adds v to the value pointed to by p and returns the original value. - * \tparam T The type of the value pointed to by p. - * \tparam V The type of the value v. - * \param p Pointer to the value to add to. - * \param v The value to add. - * \return The original value pointed to by p. + * \brief Adds value to the value pointed to by pointer and returns the original + * value. \tparam PointerType The type of the value pointed to by pointer. + * \tparam ValueType The type of the value. + * \param pointer Pointer to the value to add to. + * \param value The value to add. + * \return The original value pointed to by pointer. */ -template -ATOM_INLINE auto fetchAdd(T* p, V v) -> T { - T x = *p; - *p += v; - return x; +template +ATOM_INLINE auto fetchAdd(PointerType* pointer, + ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer += value; + return originalValue; } /*! - * \brief Subtracts v from the value pointed to by p and returns the original - * value. \tparam T The type of the value pointed to by p. \tparam V The type of - * the value v. \param p Pointer to the value to subtract from. \param v The - * value to subtract. \return The original value pointed to by p. + * \brief Subtracts value from the value pointed to by pointer and returns the + * original value. \tparam PointerType The type of the value pointed to by + * pointer. \tparam ValueType The type of the value. \param pointer Pointer to + * the value to subtract from. \param value The value to subtract. \return The + * original value pointed to by pointer. */ -template -ATOM_INLINE auto fetchSub(T* p, V v) -> T { - T x = *p; - *p -= v; - return x; +template +ATOM_INLINE auto fetchSub(PointerType* pointer, + ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer -= value; + return originalValue; } /*! - * \brief Performs a bitwise AND between the value pointed to by p and v, and - * returns the original value. \tparam T The type of the value pointed to by p. - * \tparam V The type of the value v. - * \param p Pointer to the value to AND. - * \param v The value to AND with. - * \return The original value pointed to by p. + * \brief Performs a bitwise AND between the value pointed to by pointer and + * value, and returns the original value. \tparam PointerType The type of the + * value pointed to by pointer. \tparam ValueType The type of the value. \param + * pointer Pointer to the value to AND. \param value The value to AND with. + * \return The original value pointed to by pointer. */ -template -ATOM_INLINE auto fetchAnd(T* p, V v) -> T { - T x = *p; - *p &= static_cast(v); - return x; +template +ATOM_INLINE auto fetchAnd(PointerType* pointer, + ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer &= static_cast(value); + return originalValue; } /*! - * \brief Performs a bitwise OR between the value pointed to by p and v, and - * returns the original value. \tparam T The type of the value pointed to by p. - * \tparam V The type of the value v. - * \param p Pointer to the value to OR. - * \param v The value to OR with. - * \return The original value pointed to by p. + * \brief Performs a bitwise OR between the value pointed to by pointer and + * value, and returns the original value. \tparam PointerType The type of the + * value pointed to by pointer. \tparam ValueType The type of the value. \param + * pointer Pointer to the value to OR. \param value The value to OR with. + * \return The original value pointed to by pointer. */ -template -ATOM_INLINE auto fetchOr(T* p, V v) -> T { - T x = *p; - *p |= static_cast(v); - return x; +template +ATOM_INLINE auto fetchOr(PointerType* pointer, ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer |= static_cast(value); + return originalValue; } /*! - * \brief Performs a bitwise XOR between the value pointed to by p and v, and - * returns the original value. \tparam T The type of the value pointed to by p. - * \tparam V The type of the value v. - * \param p Pointer to the value to XOR. - * \param v The value to XOR with. - * \return The original value pointed to by p. + * \brief Performs a bitwise XOR between the value pointed to by pointer and + * value, and returns the original value. \tparam PointerType The type of the + * value pointed to by pointer. \tparam ValueType The type of the value. \param + * pointer Pointer to the value to XOR. \param value The value to XOR with. + * \return The original value pointed to by pointer. */ -template -ATOM_INLINE auto fetchXor(T* p, V v) -> T { - T x = *p; - *p ^= static_cast(v); - return x; +template +ATOM_INLINE auto fetchXor(PointerType* pointer, + ValueType value) -> PointerType { + PointerType originalValue = *pointer; + *pointer ^= static_cast(value); + return originalValue; } /*! * \brief Alias for std::enable_if_t. - * \tparam C The condition. - * \tparam T The type to enable if the condition is true. + * \tparam Condition The condition. + * \tparam Type The type to enable if the condition is true. */ -template -using if_t = std::enable_if_t; +template +using if_t = std::enable_if_t; /*! * \brief Alias for std::remove_reference_t. - * \tparam T The type to remove reference from. + * \tparam Type The type to remove reference from. */ -template -using rmRefT = std::remove_reference_t; +template +using rmRefT = std::remove_reference_t; /*! * \brief Alias for std::remove_cv_t. - * \tparam T The type to remove const and volatile qualifiers from. + * \tparam Type The type to remove const and volatile qualifiers from. */ -template -using rmCvT = std::remove_cv_t; +template +using rmCvT = std::remove_cv_t; /*! * \brief Alias for removing both const, volatile qualifiers and reference. - * \tparam T The type to remove const, volatile qualifiers and reference from. + * \tparam Type The type to remove const, volatile qualifiers and reference + * from. */ -template -using rmCvRefT = rmCvT>; +template +using rmCvRefT = rmCvT>; /*! * \brief Alias for std::remove_extent_t. - * \tparam T The type to remove extent from. + * \tparam Type The type to remove extent from. */ -template -using rmArrT = std::remove_extent_t; +template +using rmArrT = std::remove_extent_t; /*! * \brief Alias for std::add_const_t. - * \tparam T The type to add const qualifier to. + * \tparam Type The type to add const qualifier to. */ -template -using constT = std::add_const_t; +template +using constT = std::add_const_t; /*! * \brief Alias for adding const qualifier and lvalue reference. - * \tparam T The type to add const qualifier and lvalue reference to. + * \tparam Type The type to add const qualifier and lvalue reference to. */ -template -using constRefT = std::add_lvalue_reference_t>>; +template +using constRefT = std::add_lvalue_reference_t>>; namespace detail { /*! * \brief Helper struct to check if all types are the same. - * \tparam T The types to check. + * \tparam Types The types to check. */ -template -struct isSame { - static constexpr bool value = false; +template +struct IsSame { + static constexpr bool K_VALUE = false; }; /*! - * \brief Specialization of isSame for two or more types. - * \tparam T The first type. - * \tparam U The second type. - * \tparam X The remaining types. + * \brief Specialization of IsSame for two or more types. + * \tparam FirstType The first type. + * \tparam SecondType The second type. + * \tparam RemainingTypes The remaining types. */ -template -struct isSame { - static constexpr bool value = - std::is_same_v || isSame::value; +template +struct IsSame { + static constexpr bool K_VALUE = + std::is_same_v || + IsSame::kValue; }; } // namespace detail /*! * \brief Checks if all types are the same. - * \tparam T The first type. - * \tparam U The second type. - * \tparam X The remaining types. + * \tparam FirstType The first type. + * \tparam SecondType The second type. + * \tparam RemainingTypes The remaining types. * \return True if all types are the same, false otherwise. */ -template +template constexpr auto isSame() -> bool { - return detail::isSame::value; + return detail::IsSame::kValue; } /*! * \brief Checks if a type is a reference. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is a reference, false otherwise. */ -template +template constexpr auto isRef() -> bool { - return std::is_reference_v; + return std::is_reference_v; } /*! * \brief Checks if a type is an array. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is an array, false otherwise. */ -template +template constexpr auto isArray() -> bool { - return std::is_array_v; + return std::is_array_v; } /*! * \brief Checks if a type is a class. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is a class, false otherwise. */ -template +template constexpr auto isClass() -> bool { - return std::is_class_v; + return std::is_class_v; } /*! * \brief Checks if a type is a scalar. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is a scalar, false otherwise. */ -template +template constexpr auto isScalar() -> bool { - return std::is_scalar_v; + return std::is_scalar_v; } /*! * \brief Checks if a type is trivially copyable. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is trivially copyable, false otherwise. */ -template +template constexpr auto isTriviallyCopyable() -> bool { - return std::is_trivially_copyable_v; + return std::is_trivially_copyable_v; } /*! * \brief Checks if a type is trivially destructible. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type is trivially destructible, false otherwise. */ -template +template constexpr auto isTriviallyDestructible() -> bool { - return std::is_trivially_destructible_v; + return std::is_trivially_destructible_v; } /*! * \brief Checks if a type is a base of another type. - * \tparam B The base type. - * \tparam D The derived type. - * \return True if B is a base of D, false otherwise. + * \tparam BaseType The base type. + * \tparam DerivedType The derived type. + * \return True if BaseType is a base of DerivedType, false otherwise. */ -template +template constexpr auto isBaseOf() -> bool { - return std::is_base_of_v; + return std::is_base_of_v; } /*! * \brief Checks if a type has a virtual destructor. - * \tparam T The type to check. + * \tparam Type The type to check. * \return True if the type has a virtual destructor, false otherwise. */ -template +template constexpr auto hasVirtualDestructor() -> bool { - return std::has_virtual_destructor_v; + return std::has_virtual_destructor_v; } } // namespace atom::meta -#endif +#endif // ATOM_META_GOD_HPP \ No newline at end of file diff --git a/src/atom/function/invoke.hpp b/src/atom/function/invoke.hpp index 86d1d27c..5317cea2 100644 --- a/src/atom/function/invoke.hpp +++ b/src/atom/function/invoke.hpp @@ -102,8 +102,8 @@ auto delayStaticMemInvoke(R (*func)(Args...), T *obj) { * \return A lambda that, when called, returns the member variable. */ template -auto delayMemberVarInvoke(M T::*m, T *obj) { - return [m, obj]() -> decltype(auto) { return (obj->*m); }; +auto delayMemberVarInvoke(M T::*memberVar, T *obj) { + return [memberVar, obj]() -> decltype(auto) { return (obj->*memberVar); }; } /*! @@ -195,7 +195,7 @@ auto safeTryCatchOrDefault( template requires Invocable auto safeTryCatchWithCustomHandler( - Func &&func, std::function handler, + Func &&func, const std::function &handler, Args &&...args) { try { return std::invoke(std::forward(func), diff --git a/src/atom/function/overload.hpp b/src/atom/function/overload.hpp index 9ee53bcd..a3267ffc 100644 --- a/src/atom/function/overload.hpp +++ b/src/atom/function/overload.hpp @@ -136,9 +136,9 @@ template constexpr auto overload_cast = OverloadCast{}; template -constexpr std::decay_t decay_copy(T&& value) noexcept( - std::is_nothrow_convertible_v>) { - return std::forward(value); // 将值转发并转换为衰减类型 +constexpr auto decayCopy(T&& value) noexcept( + std::is_nothrow_convertible_v>) -> std::decay_t { + return std::forward(value); } } // namespace atom::meta diff --git a/src/atom/function/property.hpp b/src/atom/function/property.hpp index 65505c5f..769faedf 100644 --- a/src/atom/function/property.hpp +++ b/src/atom/function/property.hpp @@ -19,12 +19,33 @@ class Property { public: Property() = default; - Property(std::function get) : getter_(std::move(get)) {} + explicit Property(std::function get) : getter_(std::move(get)) {} Property(std::function get, std::function set) : getter_(std::move(get)), setter_(std::move(set)) {} - Property(const T& defaultValue) : value_(defaultValue) {} + explicit Property(const T& defaultValue) : value_(defaultValue) {} + + // Destructor + ~Property() = default; + + // Copy constructor + Property(const Property& other) + : value_(other.value_), + getter_(other.getter_), + setter_(other.setter_), + onChange_(other.onChange_) {} + + // Copy assignment operator + auto operator=(const Property& other) -> Property& { + if (this != &other) { + value_ = other.value_; + getter_ = other.getter_; + setter_ = other.setter_; + onChange_ = other.onChange_; + } + return *this; + } // Move constructor Property(Property&& other) noexcept @@ -44,7 +65,7 @@ class Property { return *this; } - operator T() const { + explicit operator T() const { if (getter_) { return getter_(); } @@ -84,10 +105,10 @@ class Property { } // Friend function for stream output - friend auto operator<<(std::ostream& os, + friend auto operator<<(std::ostream& outputStream, const Property& prop) -> std::ostream& { - os << static_cast(prop); - return os; + outputStream << static_cast(prop); + return outputStream; } // Comparison operators @@ -155,4 +176,4 @@ public: \ Property(Name) = Property( \ nullptr, [this](const Type& value) { Name##_ = value; }); -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/proxy.hpp b/src/atom/function/proxy.hpp index b9b37ff5..6b695e69 100644 --- a/src/atom/function/proxy.hpp +++ b/src/atom/function/proxy.hpp @@ -20,8 +20,9 @@ #include #include #include "atom/async/async.hpp" -#include "atom/macro.hpp" -#include "function/type_info.hpp" + +#include "macro.hpp" + #if ENABLE_DEBUG #include #endif @@ -33,23 +34,47 @@ #include "atom/function/proxy_params.hpp" namespace atom::meta { -struct FunctionInfo { - std::string returnType; - std::vector argumentTypes; - std::string hash; +struct ATOM_ALIGNAS(128) FunctionInfo { +private: + std::string returnType_; + std::vector argumentTypes_; + std::string hash_; + +public: FunctionInfo() = default; void logFunctionInfo() const { #if ENABLE_DEBUG - std::cout << "Function return type: " << returnType << "\n"; - for (size_t i = 0; i < argumentTypes.size(); ++i) { - std::cout << "Argument " << i + 1 << ": Type = " << argumentTypes[i] - << std::endl; + std::cout << "Function return type: " << returnType_ << "\n"; + for (size_t i = 0; i < argumentTypes_.size(); ++i) { + std::cout << "Argument " << i + 1 + << ": Type = " << argumentTypes_[i] << std::endl; } #endif } -} ATOM_ALIGNAS(128); + + [[nodiscard]] auto getReturnType() const -> const std::string & { + return returnType_; + } + + [[nodiscard]] auto getArgumentTypes() const + -> const std::vector & { + return argumentTypes_; + } + + [[nodiscard]] auto getHash() const -> const std::string & { return hash_; } + + void setReturnType(const std::string &returnType) { + returnType_ = returnType; + } + + void addArgumentType(const std::string &argumentType) { + argumentTypes_.push_back(argumentType); + } + + void setHash(const std::string &hash) { hash_ = hash; } +}; // TODO: FIX ME - any cast can not cover all cases template @@ -113,7 +138,7 @@ class BaseProxyFunction { std::decay_t func_; using Traits = FunctionTraits; - static constexpr std::size_t N = Traits::arity; + static constexpr std::size_t ARITY = Traits::arity; FunctionInfo info_; public: @@ -127,32 +152,31 @@ class BaseProxyFunction { protected: void collectFunctionInfo() { // Collect return type information - info_.returnType = - DemangleHelper::demangleType(); + info_.setReturnType( + DemangleHelper::demangleType()); // Collect argument types information - collectArgumentTypes(std::make_index_sequence{}); + collectArgumentTypes(std::make_index_sequence{}); } template void collectArgumentTypes(std::index_sequence /*unused*/) { - (info_.argumentTypes.push_back( - DemangleHelper::demangleType< - typename Traits::template argument_t>()), + (info_.addArgumentType(DemangleHelper::demangleType< + typename Traits::template argument_t>()), ...); } void calcFuncInfoHash() { // 仅根据参数类型进行区分,返回值不支持,具体是因为在dispatch时不知道返回值的类型 - if (!info_.argumentTypes.empty()) { - info_.hash = std::to_string( - atom::algorithm::computeHash(info_.argumentTypes)); + if (!info_.getArgumentTypes().empty()) { + info_.setHash(std::to_string( + atom::algorithm::computeHash(info_.getArgumentTypes()))); } } void logArgumentTypes() const { #if ENABLE_DEBUG - std::cout << "Function Arity: " << N << "\n"; + std::cout << "Function Arity: " << arity << "\n"; info_.logFunctionInfo(); #endif } @@ -228,7 +252,7 @@ template class ProxyFunction : public BaseProxyFunction { using Base = BaseProxyFunction; using Traits = typename Base::Traits; - static constexpr std::size_t N = Base::N; + static constexpr std::size_t arity = Base::arity; public: explicit ProxyFunction(Func &&func) : Base(std::move(func)) {} @@ -236,32 +260,32 @@ class ProxyFunction : public BaseProxyFunction { auto operator()(const std::vector &args) -> std::any { this->logArgumentTypes(); if constexpr (Traits::is_member_function) { - if (args.size() != N + 1) { + if (args.size() != arity + 1) { THROW_EXCEPTION("Incorrect number of arguments"); } return this->callMemberFunction(args, - std::make_index_sequence()); + std::make_index_sequence()); } else { #if ENABLE_DEBUG - std::cout << "Function Arity: " << N << "\n"; + std::cout << "Function Arity: " << arity << "\n"; std::cout << "Arguments size: " << args.size() << "\n"; #endif - if (args.size() != N) { + if (args.size() != arity) { THROW_EXCEPTION("Incorrect number of arguments"); } - return this->callFunction(args, std::make_index_sequence()); + return this->callFunction(args, std::make_index_sequence()); } } auto operator()(const FunctionParams ¶ms) -> std::any { this->logArgumentTypes(); if constexpr (Traits::is_member_function) { - if (params.size() != N + 1) { + if (params.size() != arity + 1) { THROW_EXCEPTION("Incorrect number of arguments"); } return this->callMemberFunction(params.toVector()); } else { - if (params.size() != N) { + if (params.size() != arity) { THROW_EXCEPTION("Incorrect number of arguments"); } return this->callFunction(params.toVector()); @@ -273,7 +297,7 @@ template class TimerProxyFunction : public BaseProxyFunction { using Base = BaseProxyFunction; using Traits = typename Base::Traits; - static constexpr std::size_t N = Base::N; + static constexpr std::size_t arity = Base::arity; public: explicit TimerProxyFunction(Func &&func) : Base(std::move(func)) {} @@ -282,19 +306,19 @@ class TimerProxyFunction : public BaseProxyFunction { std::chrono::milliseconds timeout) -> std::any { this->logArgumentTypes(); if constexpr (Traits::is_member_function) { - if (args.size() != N + 1) { + if (args.size() != arity + 1) { THROW_EXCEPTION( "Incorrect number of arguments for member function"); } return this->callMemberFunctionWithTimeout( - args, timeout, std::make_index_sequence()); + args, timeout, std::make_index_sequence()); } else { - if (args.size() != N) { + if (args.size() != arity) { THROW_EXCEPTION( "Incorrect number of arguments for non-member function"); } - return this->callFunctionWithTimeout(args, timeout, - std::make_index_sequence()); + return this->callFunctionWithTimeout( + args, timeout, std::make_index_sequence()); } } @@ -369,4 +393,4 @@ class TimerProxyFunction : public BaseProxyFunction { }; } // namespace atom::meta -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/proxy_params.hpp b/src/atom/function/proxy_params.hpp index 1ae3e9a5..ae18f6a4 100644 --- a/src/atom/function/proxy_params.hpp +++ b/src/atom/function/proxy_params.hpp @@ -20,7 +20,7 @@ class FunctionParams { public: - explicit FunctionParams(const std::any& bv) : params_{bv} {} + explicit FunctionParams(const std::any& value) : params_{value} {} // Generalizing to accept any range of std::any template @@ -71,12 +71,14 @@ class FunctionParams { if (start > end || end > params_.size()) { THROW_OUT_OF_RANGE("Invalid slice range"); } - return FunctionParams(std::vector(params_.begin() + start, - params_.begin() + end)); + using DifferenceType = std::make_signed_t; + return FunctionParams(std::vector( + params_.begin() + static_cast(start), + params_.begin() + static_cast(end))); } template - [[nodiscard]] FunctionParams filter(Predicate pred) const { + [[nodiscard]] auto filter(Predicate pred) const -> FunctionParams { std::vector filtered; std::ranges::copy_if(params_, std::back_inserter(filtered), pred); return FunctionParams(filtered); @@ -93,4 +95,4 @@ class FunctionParams { std::vector params_; }; -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/signature.hpp b/src/atom/function/signature.hpp index 1bad61bd..d529bf05 100644 --- a/src/atom/function/signature.hpp +++ b/src/atom/function/signature.hpp @@ -10,7 +10,6 @@ #define ATOM_META_SIGNATURE_HPP #include -#include #include #include #include @@ -19,19 +18,33 @@ #include "macro.hpp" namespace atom::meta { -struct FunctionSignature { +struct alignas(128) FunctionSignature { +public: constexpr FunctionSignature( std::string_view name, std::array, 2> parameters, std::optional returnType) - : name(name), - parameters(std::move(parameters)), - returnType(returnType) {} + : name_(name), + parameters_(std::move(parameters)), + returnType_(returnType) {} - std::string_view name; - std::array, 2> parameters; - std::optional returnType; -} ATOM_ALIGNAS(128); + [[nodiscard]] auto getName() const -> std::string_view { return name_; } + + [[nodiscard]] auto getParameters() const + -> const std::array, 2>& { + return parameters_; + } + + [[nodiscard]] auto getReturnType() const + -> std::optional { + return returnType_; + } + +private: + std::string_view name_; + std::array, 2> parameters_; + std::optional returnType_; +}; constexpr auto parseFunctionDefinition( const std::string_view DEFINITION) noexcept @@ -71,6 +84,7 @@ constexpr auto parseFunctionDefinition( while (paramStart < params.size() && paramIndex < parameters.size()) { size_t paramEnd = params.size(); int bracketCount = 0; +#pragma unroll for (size_t i = paramStart; i < params.size(); ++i) { if (params[i] == ',' && bracketCount == 0) { paramEnd = i; @@ -106,4 +120,4 @@ constexpr auto parseFunctionDefinition( } // namespace atom::meta -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/stepper.hpp b/src/atom/function/stepper.hpp index a30ff0d7..9415db2f 100644 --- a/src/atom/function/stepper.hpp +++ b/src/atom/function/stepper.hpp @@ -9,8 +9,10 @@ #ifndef ATOM_META_SEQUENCE_HPP #define ATOM_META_SEQUENCE_HPP +#include +#include + #include "atom/error/exception.hpp" -#include "proxy.hpp" namespace atom::meta { class FunctionSequence { @@ -18,22 +20,23 @@ class FunctionSequence { using FunctionType = std::function&)>; // Register a function to be part of the sequence - void register_function(FunctionType func) { - functions.emplace_back(std::move(func)); + void registerFunction(FunctionType func) { + functions_.emplace_back(std::move(func)); } // Run the last function with each set of arguments provided - std::vector run( - const std::vector>& args_batch) { + auto run(const std::vector>& argsBatch) + -> std::vector { std::vector results; - if (functions.empty()) { + if (functions_.empty()) { THROW_EXCEPTION("No functions registered in the sequence"); return results; } try { - auto& func = functions.back(); - for (const auto& args : args_batch) { + auto& func = functions_.back(); + results.reserve(argsBatch.size()); + for (const auto& args : argsBatch) { results.emplace_back(func(args)); } } catch (const std::exception& e) { @@ -44,31 +47,33 @@ class FunctionSequence { // Run all functions with each set of arguments provided and return the // results of each function - std::vector> run_all( - const std::vector>& args_batch) { - std::vector> results_batch; - if (functions.empty()) { + auto runAll(const std::vector>& argsBatch) + -> std::vector> { + std::vector> resultsBatch; + if (functions_.empty()) { THROW_EXCEPTION("No functions registered in the sequence"); - return results_batch; + return resultsBatch; } try { - for (const auto& args : args_batch) { + resultsBatch.reserve(argsBatch.size()); + for (const auto& args : argsBatch) { std::vector results; - for (const auto& func : functions) { + results.reserve(functions_.size()); + for (const auto& func : functions_) { results.emplace_back(func(args)); } - results_batch.emplace_back(std::move(results)); + resultsBatch.emplace_back(std::move(results)); } } catch (const std::exception& e) { THROW_EXCEPTION(std::string("Exception caught: ") + e.what()); } - return results_batch; + return resultsBatch; } private: - std::vector functions; + std::vector functions_; }; } // namespace atom::meta -#endif // ATOM_META_SEQUENCE_HPP +#endif // ATOM_META_SEQUENCE_HPP \ No newline at end of file diff --git a/src/atom/function/template_traits.hpp b/src/atom/function/template_traits.hpp index 73e60687..b34fc0f6 100644 --- a/src/atom/function/template_traits.hpp +++ b/src/atom/function/template_traits.hpp @@ -18,6 +18,10 @@ #include "abi.hpp" +#ifdef _WIN32 +#undef max +#endif + namespace atom::meta { // Identity template diff --git a/src/atom/function/type_caster.hpp b/src/atom/function/type_caster.hpp index 22f2205b..d21ca9a8 100644 --- a/src/atom/function/type_caster.hpp +++ b/src/atom/function/type_caster.hpp @@ -10,14 +10,10 @@ #define ATOM_META_TYPE_CASTER_HPP #include -#include #include #include #include -#include #include -#include -#include #include #if ENABLE_FASTHASH @@ -94,6 +90,7 @@ class TypeCaster { auto path = findConversionPath(srcInfo.value(), destInfo); std::any result = input[i]; +#pragma unroll for (size_t j = 0; j < path.size() - 1; ++j) { result = conversions_.at(path[j]).at(path[j + 1])(result); } @@ -105,6 +102,7 @@ class TypeCaster { auto getRegisteredTypes() const -> std::vector { std::vector typeNames; typeNames.reserve(type_name_map_.size()); +#pragma unroll for (const auto& [name, info] : type_name_map_) { typeNames.push_back(name); } @@ -119,30 +117,33 @@ class TypeCaster { } template - void registerEnumValue(const std::string& enumName, - const std::string& strValue, EnumType enumValue) { - if (!m_enumMaps_.contains(enumName)) { - m_enumMaps_[enumName] = std::unordered_map(); + void registerEnumValue(const std::string& enum_name, + const std::string& string_value, + EnumType enum_value) { + if (!m_enumMaps_.contains(enum_name)) { + m_enumMaps_[enum_name] = + std::unordered_map(); } auto& enumMap = std::any_cast&>( - m_enumMaps_[enumName]); + m_enumMaps_[enum_name]); - enumMap[strValue] = enumValue; + enumMap[string_value] = enum_value; } template - const std::unordered_map& getEnumMap( - const std::string& enumName) const { + auto getEnumMap(const std::string& enum_name) const + -> const std::unordered_map& { return std::any_cast&>( - m_enumMaps_.at(enumName)); + m_enumMaps_.at(enum_name)); } template auto enumToString(EnumType value, - const std::string& enumName) -> std::string { - const auto& enumMap = getEnumMap(enumName); + const std::string& enum_name) -> std::string { + const auto& enumMap = getEnumMap(enum_name); +#pragma unroll for (const auto& [key, enumValue] : enumMap) { if (enumValue == value) { return key; @@ -152,11 +153,12 @@ class TypeCaster { } template - EnumType stringToEnum(const std::string& str, const std::string& enumName) { - const auto& enumMap = getEnumMap(enumName); - auto it = enumMap.find(str); - if (it != enumMap.end()) { - return it->second; + auto stringToEnum(const std::string& string_value, + const std::string& enum_name) -> EnumType { + const auto& enumMap = getEnumMap(enum_name); + auto iterator = enumMap.find(string_value); + if (iterator != enumMap.end()) { + return iterator->second; } THROW_INVALID_ARGUMENT("Invalid enum string"); } @@ -206,6 +208,7 @@ class TypeCaster { auto findIt = conversions_.find(last); if (findIt != conversions_.end()) { +#pragma unroll for (const auto& [next_type, _] : findIt->second) { if (visited.insert(next_type).second) { auto newPath = currentPath; @@ -230,8 +233,8 @@ class TypeCaster { static auto getTypeInfo(const std::string& name) -> std::optional { auto& registry = detail::getTypeRegistry(); - if (auto it = registry.find(name); it != registry.end()) { - return it->second; + if (auto iterator = registry.find(name); iterator != registry.end()) { + return iterator->second; } return std::nullopt; } @@ -239,4 +242,4 @@ class TypeCaster { } // namespace atom::meta -#endif // ATOM_META_TYPE_CASTER_HPP +#endif // ATOM_META_TYPE_CASTER_HPP \ No newline at end of file diff --git a/src/atom/function/type_info.hpp b/src/atom/function/type_info.hpp index c8c20ce1..dc082e35 100644 --- a/src/atom/function/type_info.hpp +++ b/src/atom/function/type_info.hpp @@ -6,12 +6,12 @@ * \copyright Copyright (C) 2023-2024 Max Qian */ -#ifndef ATOM_META_TypeInfo_HPP -#define ATOM_META_TypeInfo_HPP +#ifndef ATOM_META_TYPE_INFO_HPP +#define ATOM_META_TYPE_INFO_HPP #include #include -#include // For std::hash +#include #include #include #include @@ -19,62 +19,52 @@ #include #include "abi.hpp" +#include "concept.hpp" + #include "atom/macro.hpp" namespace atom::meta { + +// Constants for bitset size +constexpr std::size_t K_FLAG_BITSET_SIZE = 13; + // Helper to remove cv-qualifiers, references, and pointers template using BareType = std::remove_cv_t>>; template -struct is_shared_ptr : std::false_type {}; - -template -struct is_shared_ptr> : std::true_type {}; - -// 检测是否为std::unique_ptr -template -struct is_unique_ptr : std::false_type {}; +struct PointerType {}; template -struct is_unique_ptr> : std::true_type {}; - -template -constexpr bool is_pointer_like_v = - std::is_pointer_v || is_shared_ptr::value || is_unique_ptr::value; - -template -struct pointer_type {}; - -template -struct pointer_type { +struct PointerType { using type = T; }; template -struct pointer_type> { +struct PointerType> { using type = T; }; template -struct pointer_type> { +struct PointerType> { using type = T; }; template -constexpr bool is_arithmetic_pointer_v = - std::is_arithmetic_v::type>; +constexpr bool K_IS_ARITHMETIC_POINTER_V = + std::is_arithmetic_v::type>; /// \brief Compile time deduced information about a type class TypeInfo { public: - using Flags = std::bitset<13>; // Using bitset for flags + using Flags = std::bitset; // Using bitset for flags + /// \brief Construct a new Type Info object - ATOM_CONSTEXPR TypeInfo(Flags flags, const std::type_info *t_ti, - const std::type_info *t_bare_ti) ATOM_NOEXCEPT - : mTypeInfo_(t_ti), - mBareTypeInfo_(t_bare_ti), + ATOM_CONSTEXPR TypeInfo(Flags flags, const std::type_info *typeInfo, + const std::type_info *bareTypeInfo) ATOM_NOEXCEPT + : mTypeInfo_(typeInfo), + mBareTypeInfo_(bareTypeInfo), mFlags_(flags) {} ATOM_CONSTEXPR TypeInfo() ATOM_NOEXCEPT = default; @@ -85,10 +75,12 @@ class TypeInfo { Flags flags; flags.set(IS_CONST_FLAG, std::is_const_v>); flags.set(IS_REFERENCE_FLAG, std::is_reference_v); - flags.set(IS_POINTER_FLAG, is_pointer_like_v); + flags.set(IS_POINTER_FLAG, Pointer || Pointer || + SmartPointer || SmartPointer); flags.set(IS_VOID_FLAG, std::is_void_v); - if constexpr (is_pointer_like_v) { - flags.set(IS_ARITHMETIC_FLAG, is_arithmetic_pointer_v); + if constexpr (Pointer || Pointer || SmartPointer || + SmartPointer) { + flags.set(IS_ARITHMETIC_FLAG, K_IS_ARITHMETIC_POINTER_V); } else { flags.set(IS_ARITHMETIC_FLAG, std::is_arithmetic_v); } @@ -105,35 +97,37 @@ class TypeInfo { } template - static auto fromInstance(const T &) ATOM_NOEXCEPT -> TypeInfo { + static auto fromInstance(const T & /*instance*/) ATOM_NOEXCEPT -> TypeInfo { return fromType(); } - auto operator<(const TypeInfo &ti) const ATOM_NOEXCEPT->bool { - return mTypeInfo_->before(*ti.mTypeInfo_); + auto operator<(const TypeInfo &otherTypeInfo) const ATOM_NOEXCEPT->bool { + return mTypeInfo_->before(*otherTypeInfo.mTypeInfo_); } - ATOM_CONSTEXPR auto operator!=(const TypeInfo &ti) const + ATOM_CONSTEXPR auto operator!=(const TypeInfo &otherTypeInfo) const ATOM_NOEXCEPT->bool { - return !(*this == ti); + return !(*this == otherTypeInfo); } - ATOM_CONSTEXPR auto operator==(const TypeInfo &ti) const + ATOM_CONSTEXPR auto operator==(const TypeInfo &otherTypeInfo) const ATOM_NOEXCEPT->bool { - return ti.mTypeInfo_ == mTypeInfo_ && *ti.mTypeInfo_ == *mTypeInfo_ && - ti.mBareTypeInfo_ == mBareTypeInfo_ && - *ti.mBareTypeInfo_ == *mBareTypeInfo_ && ti.mFlags_ == mFlags_; + return otherTypeInfo.mTypeInfo_ == mTypeInfo_ && + *otherTypeInfo.mTypeInfo_ == *mTypeInfo_ && + otherTypeInfo.mBareTypeInfo_ == mBareTypeInfo_ && + *otherTypeInfo.mBareTypeInfo_ == *mBareTypeInfo_ && + otherTypeInfo.mFlags_ == mFlags_; } - ATOM_NODISCARD ATOM_CONSTEXPR auto bareEqual(const TypeInfo &ti) const - ATOM_NOEXCEPT -> bool { - return ti.mBareTypeInfo_ == mBareTypeInfo_ || - *ti.mBareTypeInfo_ == *mBareTypeInfo_; + ATOM_NODISCARD ATOM_CONSTEXPR auto bareEqual( + const TypeInfo &otherTypeInfo) const ATOM_NOEXCEPT -> bool { + return otherTypeInfo.mBareTypeInfo_ == mBareTypeInfo_ || + *otherTypeInfo.mBareTypeInfo_ == *mBareTypeInfo_; } - ATOM_NODISCARD auto bareEqualTypeInfo(const std::type_info &ti) const - ATOM_NOEXCEPT -> bool { - return !isUndef() && (*mBareTypeInfo_) == ti; + ATOM_NODISCARD auto bareEqualTypeInfo( + const std::type_info &otherTypeInfo) const ATOM_NOEXCEPT -> bool { + return !isUndef() && (*mBareTypeInfo_) == otherTypeInfo; } ATOM_NODISCARD auto name() const ATOM_NOEXCEPT -> std::string { @@ -284,8 +278,8 @@ struct TypeRegistrar { }; } // namespace detail -ATOM_INLINE void registerType(const std::string &type_name, TypeInfo TypeInfo) { - detail::getTypeRegistry()[type_name] = TypeInfo; +ATOM_INLINE void registerType(const std::string &type_name, TypeInfo typeInfo) { + detail::getTypeRegistry()[type_name] = typeInfo; } template @@ -318,9 +312,9 @@ struct hash { return 0; } return std::hash{}(typeInfo.bareTypeInfo()) ^ - (std::hash{}(typeInfo.name()) << 2) ^ - (std::hash{}(typeInfo.bareName()) << 3); + (std::hash{}(typeInfo.name()) << 2U) ^ + (std::hash{}(typeInfo.bareName()) << 3U); } }; } // namespace std -#endif +#endif \ No newline at end of file diff --git a/src/atom/function/vany.hpp b/src/atom/function/vany.hpp index f8d87473..e9e05294 100644 --- a/src/atom/function/vany.hpp +++ b/src/atom/function/vany.hpp @@ -1,6 +1,7 @@ #ifndef ATOM_META_VANY_HPP #define ATOM_META_VANY_HPP +#include #include #include #include @@ -11,20 +12,20 @@ #include "macro.hpp" template -struct is_iterable : std::false_type {}; +struct IsIterable : std::false_type {}; template -struct is_iterable())), - decltype(std::end(std::declval()))>> +struct IsIterable())), + decltype(std::end(std::declval()))>> : std::true_type {}; template -inline constexpr bool is_iterable_v = is_iterable::value; +inline constexpr bool K_IS_ITERABLE_V = IsIterable::value; namespace atom::meta { class Any { private: - struct VTable { + struct ATOM_ALIGNAS(64) VTable { void (*destroy)(void*); void (*copy)(const void*, void*); void (*move)(void*, void*); @@ -33,7 +34,7 @@ class Any { size_t (*size)(); void (*invoke)(const void*, const std::function&); void (*foreach)(const void*, const std::function&); - } ATOM_ALIGNAS(128); + }; template static auto defaultToString(const void* ptr) -> std::string { @@ -50,16 +51,17 @@ class Any { template static void defaultInvoke(const void* ptr, - const std::function& f) { - f(ptr); + const std::function& func) { + func(ptr); } template static void defaultForeach(const void* ptr, - const std::function& f) { - if constexpr (is_iterable_v) { + const std::function& func) { + if constexpr (K_IS_ITERABLE_V) { +#pragma unroll for (const auto& item : *static_cast(ptr)) { - f(Any(item)); + func(Any(item)); } } else { THROW_INVALID_ARGUMENT("Type is not iterable"); @@ -67,7 +69,7 @@ class Any { } template - static constexpr VTable VTABLE = { + static constexpr VTable K_V_TABLE = { [](void* ptr) { static_cast(ptr)->~T(); }, [](const void* src, void* dst) { new (dst) T(*static_cast(src)); @@ -81,25 +83,25 @@ class Any { &defaultInvoke, &defaultForeach}; - static constexpr size_t SMALL_OBJECT_SIZE = 3 * sizeof(void*); + static constexpr size_t kSmallObjectSize = 3 * sizeof(void*); union { - alignas(std::max_align_t) char storage[SMALL_OBJECT_SIZE]; + alignas(std::max_align_t) std::array storage; void* ptr; }; const VTable* vptr_ = nullptr; bool is_small_ = true; template - static constexpr bool IS_SMALL_OBJECT = - sizeof(T) <= SMALL_OBJECT_SIZE && + static constexpr bool kIsSmallObject = + sizeof(T) <= kSmallObjectSize && std::is_nothrow_move_constructible_v; auto getPtr() -> void* { - return is_small_ ? static_cast(storage) : ptr; + return is_small_ ? static_cast(storage.data()) : ptr; } [[nodiscard]] auto getPtr() const -> const void* { - return is_small_ ? static_cast(storage) : ptr; + return is_small_ ? static_cast(storage.data()) : ptr; } template @@ -113,21 +115,21 @@ class Any { } public: - Any() noexcept = default; + Any() noexcept : storage{} {} Any(const Any& other) : vptr_(other.vptr_), is_small_(other.is_small_) { if (vptr_ != nullptr) { if (is_small_) { - vptr_->copy(other.getPtr(), storage); + vptr_->copy(other.getPtr(), storage.data()); } else { - ptr = malloc(vptr_->size()); + ptr = std::malloc(vptr_->size()); if (ptr == nullptr) { throw std::bad_alloc(); } try { vptr_->copy(other.getPtr(), ptr); } catch (...) { - free(ptr); + std::free(ptr); throw; } } @@ -137,7 +139,7 @@ class Any { Any(Any&& other) noexcept : vptr_(other.vptr_), is_small_(other.is_small_) { if (vptr_ != nullptr) { if (is_small_) { - vptr_->move(other.storage, storage); + vptr_->move(other.storage.data(), storage.data()); } else { ptr = other.ptr; other.ptr = nullptr; @@ -147,20 +149,20 @@ class Any { } template - Any(T&& value) { + explicit Any(T&& value) { using ValueType = std::remove_cvref_t; - if constexpr (IS_SMALL_OBJECT) { - new (storage) ValueType(std::forward(value)); + if constexpr (kIsSmallObject) { + new (storage.data()) ValueType(std::forward(value)); is_small_ = true; } else { - ptr = malloc(sizeof(ValueType)); + ptr = std::malloc(sizeof(ValueType)); if (!ptr) { throw std::bad_alloc(); } new (ptr) ValueType(std::forward(value)); is_small_ = false; } - vptr_ = &VTABLE; + vptr_ = &K_V_TABLE; } ~Any() { reset(); } @@ -182,7 +184,8 @@ class Any { ptr = other.ptr; other.ptr = nullptr; } else { - std::memcpy(storage, other.storage, SMALL_OBJECT_SIZE); + std::memcpy(storage.data(), other.storage.data(), + kSmallObjectSize); } other.vptr_ = nullptr; } @@ -206,7 +209,7 @@ class Any { if (vptr_ != nullptr) { vptr_->destroy(getPtr()); if (!is_small_) { - free(ptr); + std::free(ptr); } vptr_ = nullptr; is_small_ = true; @@ -252,20 +255,20 @@ class Any { return vptr_->toString(getPtr()); } - void invoke(const std::function& f) const { + void invoke(const std::function& func) const { if (!hasValue()) { THROW_INVALID_ARGUMENT("Cannot invoke on empty Any"); } - vptr_->invoke(getPtr(), f); + vptr_->invoke(getPtr(), func); } - void foreach (const std::function& f) const { + void foreach (const std::function& func) const { if (!hasValue()) { THROW_INVALID_ARGUMENT("Cannot iterate over empty Any"); } - vptr_->foreach (getPtr(), f); + vptr_->foreach (getPtr(), func); } }; } // namespace atom::meta -#endif +#endif \ No newline at end of file diff --git a/src/atom/io/glob.hpp b/src/atom/io/glob.hpp index a9df197a..071774b2 100644 --- a/src/atom/io/glob.hpp +++ b/src/atom/io/glob.hpp @@ -1,104 +1,101 @@ #pragma once #include +#include #include #include #include #include #include -#include "error/exception.hpp" -#include "macro.hpp" -#ifdef GLOB_USE_GHC_FILESYSTEM -#include -#else -#include -#endif +#include "atom/error/exception.hpp" -namespace glob { +#include "macro.hpp" + +namespace atom::io { -#ifdef GLOB_USE_GHC_FILESYSTEM -namespace fs = ghc::filesystem; -#else namespace fs = std::filesystem; -#endif namespace { ATOM_INLINE auto stringReplace(std::string &str, const std::string &from, - const std::string &to) -> bool { + const std::string &toStr) -> bool { std::size_t startPos = str.find(from); if (startPos == std::string::npos) { return false; } - str.replace(startPos, from.length(), to); + str.replace(startPos, from.length(), toStr); return true; } ATOM_INLINE auto translate(const std::string &pattern) -> std::string { - std::size_t i = 0; - std::size_t n = pattern.size(); + std::size_t index = 0; + std::size_t patternSize = pattern.size(); std::string resultString; - while (i < n) { - auto c = pattern[i]; - i += 1; - if (c == '*') { + while (index < patternSize) { + auto currentChar = pattern[index]; + index += 1; + if (currentChar == '*') { resultString += ".*"; - } else if (c == '?') { + } else if (currentChar == '?') { resultString += "."; - } else if (c == '[') { - auto j = i; - if (j < n && pattern[j] == '!') { - j += 1; + } else if (currentChar == '[') { + auto innerIndex = index; + if (innerIndex < patternSize && pattern[innerIndex] == '!') { + innerIndex += 1; } - if (j < n && pattern[j] == ']') { - j += 1; + if (innerIndex < patternSize && pattern[innerIndex] == ']') { + innerIndex += 1; } - while (j < n && pattern[j] != ']') { - j += 1; + while (innerIndex < patternSize && pattern[innerIndex] != ']') { + innerIndex += 1; } - if (j >= n) { + if (innerIndex >= patternSize) { resultString += "\\["; } else { - auto stuff = - std::string(pattern.begin() + i, pattern.begin() + j); + auto stuff = std::string(pattern.begin() + index, + pattern.begin() + innerIndex); +#if USE_ABSL + if (!absl::StrContains(stuff, "--")) { +#else if (stuff.find("--") == std::string::npos) { +#endif stringReplace(stuff, std::string{"\\"}, std::string{R"(\\)"}); } else { std::vector chunks; - std::size_t k = 0; - if (pattern[i] == '!') { - k = i + 2; + std::size_t chunkIndex = 0; + if (pattern[index] == '!') { + chunkIndex = index + 2; } else { - k = i + 1; + chunkIndex = index + 1; } while (true) { - k = pattern.find("-", k, j); - if (k == std::string::npos) { + chunkIndex = pattern.find("-", chunkIndex, innerIndex); + if (chunkIndex == std::string::npos) { break; } - chunks.emplace_back(pattern.begin() + i, - pattern.begin() + k); - i = k + 1; - k = k + 3; + chunks.emplace_back(pattern.begin() + index, + pattern.begin() + chunkIndex); + index = chunkIndex + 1; + chunkIndex = chunkIndex + 3; } - chunks.emplace_back(pattern.begin() + i, - pattern.begin() + j); + chunks.emplace_back(pattern.begin() + index, + pattern.begin() + innerIndex); // Escape backslashes and hyphens for set difference (--). // Hyphens that create ranges shouldn't be escaped. bool first = false; - for (auto &s : chunks) { - stringReplace(s, std::string{"\\"}, + for (auto &chunk : chunks) { + stringReplace(chunk, std::string{"\\"}, std::string{R"(\\)"}); - stringReplace(s, std::string{"-"}, + stringReplace(chunk, std::string{"-"}, std::string{R"(\-)"}); if (first) { - stuff += s; + stuff += chunk; first = false; } else { - stuff += "-" + s; + stuff += "-" + chunk; } } } @@ -111,13 +108,13 @@ ATOM_INLINE auto translate(const std::string &pattern) -> std::string { std::regex(std::string{R"([&~|])"}), // pattern std::string{R"(\\\1)"}); // repl stuff = result; - i = j + 1; + index = innerIndex + 1; if (stuff[0] == '!') { stuff = "^" + std::string(stuff.begin() + 1, stuff.end()); } else if (stuff[0] == '^' || stuff[0] == '[') { stuff = "\\\\" + stuff; } - resultString = resultString + "[" + stuff + "]"; + resultString += "[" + stuff + "]"; } } else { // SPECIAL_CHARS @@ -129,17 +126,22 @@ ATOM_INLINE auto translate(const std::string &pattern) -> std::string { "()[]{}?*+-|^$\\.&~# \t\n\r\v\f"; static std::map specialCharactersMap; if (specialCharactersMap.empty()) { - for (auto &sc : specialCharacters) { - specialCharactersMap.insert( - std::make_pair(static_cast(sc), - std::string{"\\"} + std::string(1, sc))); + for (auto &specialChar : specialCharacters) { + specialCharactersMap.insert(std::make_pair( + static_cast(specialChar), + std::string{"\\"} + std::string(1, specialChar))); } } - if (specialCharacters.find(c) != std::string::npos) { - resultString += specialCharactersMap[static_cast(c)]; +#if USE_ABSL + if (absl::StrContains(specialCharacters, currentChar)) { +#else + if (specialCharacters.find(currentChar) != std::string::npos) { +#endif + resultString += + specialCharactersMap[static_cast(currentChar)]; } else { - resultString += c; + resultString += currentChar; } } } @@ -178,17 +180,21 @@ ATOM_INLINE auto expandTilde(fs::path path) -> fs::path { #else const char *homeVariable = "USER"; #endif - const char *home = std::getenv(homeVariable); + char *home = nullptr; + size_t len = 0; + _dupenv_s(&home, &len, homeVariable); if (home == nullptr) { THROW_INVALID_ARGUMENT( "error: Unable to expand `~` - HOME environment variable not set."); } - std::string s = path.string(); - if (s[0] == '~') { - s = std::string(home) + s.substr(1, s.size() - 1); - return fs::path(s); + std::string pathStr = path.string(); + if (pathStr[0] == '~') { + pathStr = std::string(home) + pathStr.substr(1, pathStr.size() - 1); + free(home); + return fs::path(pathStr); } else { + free(home); return path; } } @@ -243,11 +249,11 @@ ATOM_INLINE auto rlistdir(const fs::path &dirname, bool dironly) -> std::vector { std::vector result; auto names = iterDirectory(dirname, dironly); - for (auto &x : names) { - if (!isHidden(x.string())) { - result.push_back(x); - for (auto &y : rlistdir(x, dironly)) { - result.push_back(y); + for (auto &name : names) { + if (!isHidden(name.string())) { + result.push_back(name); + for (auto &subName : rlistdir(name, dironly)) { + result.push_back(subName); } } } @@ -276,16 +282,9 @@ ATOM_INLINE auto glob1(const fs::path &dirname, const std::string &pattern, // std::cout << "In glob1\n"; auto names = iterDirectory(dirname, dironly); std::vector filteredNames; - for (auto &n : names) { - if (!isHidden(n.string())) { - filteredNames.push_back(n.filename()); - // if (n.is_relative()) { - // // std::cout << "Filtered (Relative): " << n << "\n"; - // filtered_names.push_back(fs::relative(n)); - // } else { - // // std::cout << "Filtered (Absolute): " << n << "\n"; - // filtered_names.push_back(n.filename()); - // } + for (auto &name : names) { + if (!isHidden(name.string())) { + filteredNames.push_back(name.filename()); } } return filter(filteredNames, pattern); @@ -364,11 +363,11 @@ ATOM_INLINE auto glob(const std::string &pathname, bool recursive = false, globInDir = glob0; } - for (auto &d : dirs) { - for (auto &name : globInDir(d, BASENAME.string(), dironly)) { + for (auto &dir : dirs) { + for (auto &name : globInDir(dir, BASENAME.string(), dironly)) { fs::path subresult = name; if (name.parent_path().empty()) { - subresult = d / name; + subresult = dir / name; } result.push_back(subresult); } @@ -379,16 +378,18 @@ ATOM_INLINE auto glob(const std::string &pathname, bool recursive = false, } // namespace -static ATOM_INLINE std::vector glob(const std::string &pathname) { +static ATOM_INLINE auto glob(const std::string &pathname) + -> std::vector { return glob(pathname, false); } -static ATOM_INLINE std::vector rglob(const std::string &pathname) { +static ATOM_INLINE auto rglob(const std::string &pathname) + -> std::vector { return glob(pathname, true); } -static ATOM_INLINE std::vector glob( - const std::vector &pathnames) { +static ATOM_INLINE auto glob(const std::vector &pathnames) + -> std::vector { std::vector result; for (const auto &pathname : pathnames) { for (auto &match : glob(pathname, false)) { @@ -398,8 +399,8 @@ static ATOM_INLINE std::vector glob( return result; } -static ATOM_INLINE std::vector rglob( - const std::vector &pathnames) { +static ATOM_INLINE auto rglob(const std::vector &pathnames) + -> std::vector { std::vector result; for (const auto &pathname : pathnames) { for (auto &match : glob(pathname, true)) { @@ -409,14 +410,14 @@ static ATOM_INLINE std::vector rglob( return result; } -static ATOM_INLINE std::vector glob( - const std::initializer_list &pathnames) { +static ATOM_INLINE auto glob(const std::initializer_list + &pathnames) -> std::vector { return glob(std::vector(pathnames)); } -static ATOM_INLINE std::vector rglob( - const std::initializer_list &pathnames) { +static ATOM_INLINE auto rglob(const std::initializer_list + &pathnames) -> std::vector { return rglob(std::vector(pathnames)); } -} // namespace glob +} // namespace atom::io \ No newline at end of file diff --git a/src/atom/io/io.cpp b/src/atom/io/io.cpp index 02e734de..938d8e74 100644 --- a/src/atom/io/io.cpp +++ b/src/atom/io/io.cpp @@ -25,6 +25,7 @@ Description: IO #include "atom/log/loguru.hpp" #include "atom/type/json.hpp" +#include "atom/utils/string.hpp" #ifdef __linux #include @@ -36,7 +37,6 @@ using json = nlohmann::json; #ifdef _WIN32 #include -const std::string PATH_SEPARATOR = "\\"; const std::regex FOLDER_NAME_REGEX(R"(^[^\/?*:;{}\\]+[^\\]*$)"); const std::regex FILE_NAME_REGEX("^[^\\/:*?\"<>|]+$"); #else @@ -44,6 +44,10 @@ const std::regex FOLDER_NAME_REGEX("^[^/]+$"); const std::regex FILE_NAME_REGEX("^[^/]+$"); #endif +#ifdef _MSC_VER +#undef min +#endif + #define ATOM_IO_CHECK_ARGUMENT(value) \ if ((value).empty()) { \ LOG_F(ERROR, "{}: Invalid argument: {}", __func__, value); \ diff --git a/src/atom/io/pushd.cpp b/src/atom/io/pushd.cpp index 43deee12..2cae450e 100644 --- a/src/atom/io/pushd.cpp +++ b/src/atom/io/pushd.cpp @@ -1,22 +1,27 @@ #include "pushd.hpp" + +#include #include -#include -#include #include -#include +#include +#include +#include #include +#include "atom/log/loguru.hpp" + // Private implementation class class DirectoryStackImpl { public: - std::stack dir_stack; + std::stack dirStack; - std::vector get_stack_contents() const { - std::stack temp_stack = dir_stack; + [[nodiscard]] auto getStackContents() const + -> std::vector { + std::stack tempStack = dirStack; std::vector contents; - while (!temp_stack.empty()) { - contents.push_back(temp_stack.top()); - temp_stack.pop(); + while (!tempStack.empty()) { + contents.push_back(tempStack.top()); + tempStack.pop(); } std::reverse(contents.begin(), contents.end()); return contents; @@ -25,149 +30,166 @@ class DirectoryStackImpl { // DirectoryStack public interface methods -DirectoryStack::DirectoryStack() : impl(new DirectoryStackImpl) {} +DirectoryStack::DirectoryStack() + : impl_(std::make_unique()) {} + +DirectoryStack::~DirectoryStack() = default; -DirectoryStack::~DirectoryStack() { - delete impl; +DirectoryStack::DirectoryStack(const DirectoryStack& other) + : impl_(std::make_unique(*other.impl_)) {} + +auto DirectoryStack::operator=(const DirectoryStack& other) -> DirectoryStack& { + if (this != &other) { + impl_ = std::make_unique(*other.impl_); + } + return *this; } -void DirectoryStack::pushd(const std::filesystem::path& new_dir) { +DirectoryStack::DirectoryStack(DirectoryStack&& other) noexcept = default; + +auto DirectoryStack::operator=(DirectoryStack&& other) noexcept + -> DirectoryStack& = default; + +void DirectoryStack::pushd(const std::filesystem::path& newDir) { try { - std::filesystem::path current_dir = std::filesystem::current_path(); - impl->dir_stack.push(current_dir); - std::filesystem::current_path(new_dir); - std::cout << "Changed directory to: " << std::filesystem::current_path() << '\n'; + std::filesystem::path currentDir = std::filesystem::current_path(); + impl_->dirStack.push(currentDir); + std::filesystem::current_path(newDir); + LOG_F(INFO, "Changed directory to: {}", + std::filesystem::current_path().string()); } catch (const std::filesystem::filesystem_error& e) { - std::cerr << "Error: " << e.what() << '\n'; + LOG_F(ERROR, "Error: {}", e.what()); } } void DirectoryStack::popd() { - if (impl->dir_stack.empty()) { - std::cerr << "Directory stack is empty, cannot pop.\n"; + if (impl_->dirStack.empty()) { + LOG_F(ERROR, "Directory stack is empty, cannot pop."); return; } try { - std::filesystem::path previous_dir = impl->dir_stack.top(); - impl->dir_stack.pop(); - std::filesystem::current_path(previous_dir); - std::cout << "Changed back to directory: " << std::filesystem::current_path() << '\n'; + std::filesystem::path previousDir = impl_->dirStack.top(); + impl_->dirStack.pop(); + std::filesystem::current_path(previousDir); + LOG_F(INFO, "Changed back to directory: {}", + std::filesystem::current_path().string()); } catch (const std::filesystem::filesystem_error& e) { - std::cerr << "Error: " << e.what() << '\n'; + LOG_F(ERROR, "Error: {}", e.what()); } } void DirectoryStack::peek() const { - if (impl->dir_stack.empty()) { - std::cerr << "Directory stack is empty.\n"; + if (impl_->dirStack.empty()) { + LOG_F(ERROR, "Directory stack is empty."); return; } - std::cout << "Top directory in stack: " << impl->dir_stack.top() << '\n'; + LOG_F(INFO, "Top directory in stack: {}", impl_->dirStack.top().string()); } void DirectoryStack::dirs() const { - std::cout << "Current Directory Stack:\n"; - auto contents = impl->get_stack_contents(); + LOG_F(INFO, "Current Directory Stack:"); + auto contents = impl_->getStackContents(); for (size_t i = 0; i < contents.size(); ++i) { - std::cout << i << ": " << contents[i] << '\n'; + LOG_F(INFO, "%zu: {}", i, contents[i].string()); } } void DirectoryStack::clear() { - while (!impl->dir_stack.empty()) { - impl->dir_stack.pop(); + while (!impl_->dirStack.empty()) { + impl_->dirStack.pop(); } - std::cout << "Directory stack cleared.\n"; + LOG_F(INFO, "Directory stack cleared."); } void DirectoryStack::swap(size_t index1, size_t index2) { - auto contents = impl->get_stack_contents(); + auto contents = impl_->getStackContents(); if (index1 >= contents.size() || index2 >= contents.size()) { - std::cerr << "Invalid indices for swap operation.\n"; + LOG_F(ERROR, "Invalid indices for swap operation."); return; } std::swap(contents[index1], contents[index2]); - std::stack new_stack; - for (auto it = contents.rbegin(); it != contents.rend(); ++it) { - new_stack.push(*it); + std::stack newStack; + for (const auto& dir : contents) { + newStack.push(dir); } - impl->dir_stack = new_stack; + impl_->dirStack = std::move(newStack); - std::cout << "Swapped directories at indices " << index1 << " and " << index2 << ".\n"; + LOG_F(INFO, "Swapped directories at indices %zu and %zu.", index1, index2); dirs(); // Display the updated stack } void DirectoryStack::remove(size_t index) { - auto contents = impl->get_stack_contents(); + auto contents = impl_->getStackContents(); if (index >= contents.size()) { - std::cerr << "Invalid index for remove operation.\n"; + LOG_F(ERROR, "Invalid index for remove operation."); return; } - contents.erase(contents.begin() + index); + contents.erase( + contents.begin() + + static_cast::difference_type>( + index)); - std::stack new_stack; - for (auto it = contents.rbegin(); it != contents.rend(); ++it) { - new_stack.push(*it); + std::stack newStack; + for (const auto& dir : contents) { + newStack.push(dir); } - impl->dir_stack = new_stack; + impl_->dirStack = std::move(newStack); - std::cout << "Removed directory at index " << index << ".\n"; + LOG_F(INFO, "Removed directory at index %zu.", index); dirs(); // Display the updated stack } -void DirectoryStack::goto_index(size_t index) { - auto contents = impl->get_stack_contents(); +void DirectoryStack::gotoIndex(size_t index) { + auto contents = impl_->getStackContents(); if (index >= contents.size()) { - std::cerr << "Invalid index for goto operation.\n"; + LOG_F(ERROR, "Invalid index for goto operation."); return; } try { std::filesystem::current_path(contents[index]); - std::cout << "Changed to directory: " << std::filesystem::current_path() << '\n'; + LOG_F(INFO, "Changed to directory: {}", + std::filesystem::current_path().string()); } catch (const std::filesystem::filesystem_error& e) { - std::cerr << "Error: " << e.what() << '\n'; + LOG_F(ERROR, "Error: {}", e.what()); } } -void DirectoryStack::save_stack_to_file(const std::string& filename) const { +void DirectoryStack::saveStackToFile(const std::string& filename) const { std::ofstream file(filename); if (!file) { - std::cerr << "Error: Unable to open file for writing.\n"; + LOG_F(ERROR, "Error: Unable to open file for writing."); return; } - auto contents = impl->get_stack_contents(); + auto contents = impl_->getStackContents(); for (const auto& dir : contents) { file << dir.string() << '\n'; } file.close(); - std::cout << "Directory stack saved to " << filename << ".\n"; + LOG_F(INFO, "Directory stack saved to {}.", filename); } -void DirectoryStack::load_stack_from_file(const std::string& filename) { +void DirectoryStack::loadStackFromFile(const std::string& filename) { std::ifstream file(filename); if (!file) { - std::cerr << "Error: Unable to open file for reading.\n"; + LOG_F(ERROR, "Error: Unable to open file for reading."); return; } clear(); // Clear the current stack std::string line; while (std::getline(file, line)) { - impl->dir_stack.push(line); + impl_->dirStack.emplace(line); } file.close(); - std::cout << "Directory stack loaded from " << filename << ".\n"; + LOG_F(INFO, "Directory stack loaded from {}.", filename); dirs(); // Display the updated stack } -size_t DirectoryStack::size() const { - return impl->dir_stack.size(); -} +auto DirectoryStack::size() const -> size_t { return impl_->dirStack.size(); } -bool DirectoryStack::is_empty() const { - return impl->dir_stack.empty(); -} +auto DirectoryStack::isEmpty() const -> bool { return impl_->dirStack.empty(); } -void DirectoryStack::show_current_directory() const { - std::cout << "Current Directory: " << std::filesystem::current_path() << '\n'; -} +void DirectoryStack::showCurrentDirectory() const { + LOG_F(INFO, "Current Directory: {}", + std::filesystem::current_path().string()); +} \ No newline at end of file diff --git a/src/atom/io/pushd.hpp b/src/atom/io/pushd.hpp index 245719ea..cc0e6d13 100644 --- a/src/atom/io/pushd.hpp +++ b/src/atom/io/pushd.hpp @@ -2,7 +2,7 @@ #define DIRECTORYSTACK_H #include -#include +#include #include // Forward declaration of the implementation class @@ -10,7 +10,8 @@ class DirectoryStackImpl; /** * @class DirectoryStack - * @brief A class for managing a stack of directory paths, allowing push, pop, and various operations on the stack. + * @brief A class for managing a stack of directory paths, allowing push, pop, + * and various operations on the stack. */ class DirectoryStack { public: @@ -25,7 +26,28 @@ class DirectoryStack { ~DirectoryStack(); /** - * @brief Push the current directory onto the stack and change to the specified directory. + * @brief Copy constructor + */ + DirectoryStack(const DirectoryStack& other); + + /** + * @brief Copy assignment operator + */ + auto operator=(const DirectoryStack& other) -> DirectoryStack&; + + /** + * @brief Move constructor + */ + DirectoryStack(DirectoryStack&& other) noexcept; + + /** + * @brief Move assignment operator + */ + auto operator=(DirectoryStack&& other) noexcept -> DirectoryStack&; + + /** + * @brief Push the current directory onto the stack and change to the + * specified directory. * @param new_dir The directory to change to. */ void pushd(const std::filesystem::path& new_dir); @@ -67,39 +89,40 @@ class DirectoryStack { * @brief Change to the directory at the specified index in the stack. * @param index The index of the directory to change to. */ - void goto_index(size_t index); + void gotoIndex(size_t index); /** * @brief Save the directory stack to a file. * @param filename The name of the file to save the stack to. */ - void save_stack_to_file(const std::string& filename) const; + void saveStackToFile(const std::string& filename) const; /** * @brief Load the directory stack from a file. * @param filename The name of the file to load the stack from. */ - void load_stack_from_file(const std::string& filename); + void loadStackFromFile(const std::string& filename); /** * @brief Get the size of the directory stack. * @return The number of directories in the stack. */ - size_t size() const; + [[nodiscard]] auto size() const -> size_t; /** * @brief Check if the directory stack is empty. * @return True if the stack is empty, false otherwise. */ - bool is_empty() const; + [[nodiscard]] auto isEmpty() const -> bool; /** * @brief Show the current directory path. */ - void show_current_directory() const; + void showCurrentDirectory() const; private: - DirectoryStackImpl* impl; ///< Pointer to the implementation. + std::unique_ptr + impl_; ///< Pointer to the implementation. }; -#endif // DIRECTORYSTACK_H +#endif // DIRECTORYSTACK_H \ No newline at end of file diff --git a/src/atom/search/lru.hpp b/src/atom/search/lru.hpp index fbb9afe3..c04babce 100644 --- a/src/atom/search/lru.hpp +++ b/src/atom/search/lru.hpp @@ -61,7 +61,7 @@ class ThreadSafeLRUCache { * @return An optional containing the value if found and not expired, * otherwise std::nullopt. */ - std::optional get(const Key& key); + auto get(const Key& key) -> std::optional; /** * @brief Inserts or updates a value in the cache. @@ -92,7 +92,7 @@ class ThreadSafeLRUCache { * * @return A vector containing all keys currently in the cache. */ - std::vector keys() const; + auto keys() const -> std::vector; /** * @brief Removes and returns the least recently used item. @@ -100,7 +100,7 @@ class ThreadSafeLRUCache { * @return An optional containing the key-value pair if the cache is not * empty, otherwise std::nullopt. */ - std::optional pop_lru(); + auto popLru() -> std::optional; /** * @brief Resizes the cache to a new maximum size. @@ -117,7 +117,7 @@ class ThreadSafeLRUCache { * * @return The number of items currently in the cache. */ - size_t size() const; + auto size() const -> size_t; /** * @brief Gets the current load factor of the cache. @@ -126,7 +126,7 @@ class ThreadSafeLRUCache { * * @return The load factor of the cache. */ - float load_factor() const; + auto loadFactor() const -> float; /** * @brief Sets the callback function to be called when a new item is @@ -134,7 +134,7 @@ class ThreadSafeLRUCache { * * @param callback The callback function that takes a key and value. */ - void set_insert_callback( + void setInsertCallback( std::function callback); /** @@ -142,14 +142,14 @@ class ThreadSafeLRUCache { * * @param callback The callback function that takes a key. */ - void set_erase_callback(std::function callback); + void setEraseCallback(std::function callback); /** * @brief Sets the callback function to be called when the cache is cleared. * * @param callback The callback function. */ - void set_clear_callback(std::function callback); + void setClearCallback(std::function callback); /** * @brief Gets the hit rate of the cache. @@ -159,7 +159,7 @@ class ThreadSafeLRUCache { * * @return The hit rate of the cache. */ - float hit_rate() const; + auto hitRate() const -> float; /** * @brief Saves the cache contents to a file. @@ -167,7 +167,7 @@ class ThreadSafeLRUCache { * @param filename The name of the file to save to. * @throws std::runtime_error If a deadlock is avoided while locking. */ - void save_to_file(const std::string& filename) const; + void saveToFile(const std::string& filename) const; /** * @brief Loads cache contents from a file. @@ -175,7 +175,7 @@ class ThreadSafeLRUCache { * @param filename The name of the file to load from. * @throws std::runtime_error If a deadlock is avoided while locking. */ - void load_from_file(const std::string& filename); + void loadFromFile(const std::string& filename); private: mutable std::shared_mutex mutex_; ///< Mutex for protecting shared data. @@ -184,8 +184,8 @@ class ThreadSafeLRUCache { std::unordered_map cache_items_map_; ///< Map for fast key lookups. size_t max_size_; ///< Maximum number of items in the cache. - size_t hit_count_; ///< Number of cache hits. - size_t miss_count_; ///< Number of cache misses. + size_t hit_count_{}; ///< Number of cache hits. + size_t miss_count_{}; ///< Number of cache misses. std::function on_insert_; ///< Callback for item insertion. @@ -198,33 +198,33 @@ class ThreadSafeLRUCache { * @param item The cache item to check. * @return True if the item is expired, false otherwise. */ - bool is_expired(const CacheItem& item) const; + auto isExpired(const CacheItem& item) const -> bool; }; template ThreadSafeLRUCache::ThreadSafeLRUCache(size_t max_size) - : max_size_(max_size), hit_count_(0), miss_count_(0) {} + : max_size_(max_size) {} template -std::optional ThreadSafeLRUCache::get(const Key& key) { +auto ThreadSafeLRUCache::get(const Key& key) + -> std::optional { std::unique_lock lock(mutex_, std::try_to_lock); if (!lock) { return std::nullopt; // Avoid deadlock } auto it = cache_items_map_.find(key); - if (it == cache_items_map_.end() || is_expired(it->second)) { + if (it == cache_items_map_.end() || isExpired(it->second)) { ++miss_count_; if (it != cache_items_map_.end()) { erase(key); // Remove expired item } return std::nullopt; - } else { - ++hit_count_; - cache_items_list_.splice(cache_items_list_.begin(), cache_items_list_, - it->second.iterator); - return it->second.value; } + ++hit_count_; + cache_items_list_.splice(cache_items_list_.begin(), cache_items_list_, + it->second.iterator); + return it->second.value; } template @@ -280,7 +280,7 @@ void ThreadSafeLRUCache::clear() { } template -std::vector ThreadSafeLRUCache::keys() const { +auto ThreadSafeLRUCache::keys() const -> std::vector { std::shared_lock lock(mutex_); std::vector keys; for (const auto& pair : cache_items_list_) { @@ -290,8 +290,8 @@ std::vector ThreadSafeLRUCache::keys() const { } template -std::optional::KeyValuePair> -ThreadSafeLRUCache::pop_lru() { +auto ThreadSafeLRUCache::popLru() + -> std::optional::KeyValuePair> { std::unique_lock lock(mutex_); if (cache_items_list_.empty()) { return std::nullopt; @@ -323,38 +323,40 @@ size_t ThreadSafeLRUCache::size() const { } template -float ThreadSafeLRUCache::load_factor() const { +auto ThreadSafeLRUCache::loadFactor() const -> float { std::shared_lock lock(mutex_); return static_cast(cache_items_map_.size()) / max_size_; } template -void ThreadSafeLRUCache::set_insert_callback( +void ThreadSafeLRUCache::setInsertCallback( std::function callback) { on_insert_ = std::move(callback); } template -void ThreadSafeLRUCache::set_erase_callback( +void ThreadSafeLRUCache::setEraseCallback( std::function callback) { on_erase_ = std::move(callback); } template -void ThreadSafeLRUCache::set_clear_callback( +void ThreadSafeLRUCache::setClearCallback( std::function callback) { on_clear_ = std::move(callback); } template -float ThreadSafeLRUCache::hit_rate() const { +auto ThreadSafeLRUCache::hitRate() const -> float { std::shared_lock lock(mutex_); size_t total = hit_count_ + miss_count_; - return total == 0 ? 0.0f : static_cast(hit_count_) / total; + return total == 0 ? 0.0F + : static_cast(static_cast(hit_count_) / + static_cast(total)); } template -void ThreadSafeLRUCache::save_to_file( +void ThreadSafeLRUCache::saveToFile( const std::string& filename) const { std::unique_lock lock(mutex_, std::try_to_lock); if (!lock) { @@ -368,17 +370,16 @@ void ThreadSafeLRUCache::save_to_file( for (const auto& pair : cache_items_list_) { ofs.write(reinterpret_cast(&pair.first), sizeof(pair.first)); - size_t value_size = pair.second.size(); - ofs.write(reinterpret_cast(&value_size), - sizeof(value_size)); - ofs.write(pair.second.c_str(), value_size); + size_t valueSize = pair.second.size(); + ofs.write(reinterpret_cast(&valueSize), + sizeof(valueSize)); + ofs.write(pair.second.c_str(), valueSize); } } } template -void ThreadSafeLRUCache::load_from_file( - const std::string& filename) { +void ThreadSafeLRUCache::loadFromFile(const std::string& filename) { std::unique_lock lock(mutex_, std::try_to_lock); if (!lock) { throw std::runtime_error("Resource deadlock avoided"); @@ -392,17 +393,17 @@ void ThreadSafeLRUCache::load_from_file( for (size_t i = 0; i < size; ++i) { Key key; ifs.read(reinterpret_cast(&key), sizeof(key)); - size_t value_size; - ifs.read(reinterpret_cast(&value_size), sizeof(value_size)); - std::string value(value_size, '\0'); - ifs.read(&value[0], value_size); + size_t valueSize; + ifs.read(reinterpret_cast(&valueSize), sizeof(valueSize)); + std::string value(valueSize, '\0'); + ifs.read(value.data(), static_cast(valueSize)); put(key, value); } } } template -bool ThreadSafeLRUCache::is_expired(const CacheItem& item) const { +auto ThreadSafeLRUCache::isExpired(const CacheItem& item) const -> bool { return Clock::now() > item.expiryTime; } diff --git a/src/atom/system/command.cpp b/src/atom/system/command.cpp index c5929023..0461ad44 100644 --- a/src/atom/system/command.cpp +++ b/src/atom/system/command.cpp @@ -23,6 +23,7 @@ Description: Simple wrapper for executing commands. #include #include #include +#include "env.hpp" #ifdef _WIN32 #define SETENV(name, value) SetEnvironmentVariableA(name, value) @@ -43,8 +44,8 @@ Description: Simple wrapper for executing commands. #include "macro.hpp" #include "atom/error/exception.hpp" +#include "atom/function/global_ptr.hpp" #include "atom/system/process.hpp" - #include "atom/utils/to_string.hpp" #ifdef _WIN32 @@ -68,7 +69,11 @@ auto executeCommandInternal( auto pipeDeleter = [](FILE *pipe) { if (pipe != nullptr) { +#ifdef _MSC_VER + _pclose(pipe); +#else pclose(pipe); +#endif } }; @@ -111,8 +116,13 @@ auto executeCommandInternal( // 写入输入 if (!input.empty()) { - fwrite(input.c_str(), sizeof(char), input.size(), pipe.get()); - fflush(pipe.get()); + if (fwrite(input.c_str(), sizeof(char), input.size(), pipe.get()) != + input.size()) { + THROW_RUNTIME_ERROR("Error: failed to write input to pipe."); + } + if (fflush(pipe.get()) != 0) { + THROW_RUNTIME_ERROR("Error: failed to flush pipe."); + } } constexpr std::size_t BUFFER_SIZE = 4096; @@ -173,7 +183,11 @@ auto executeCommandStream( auto pipeDeleter = [](FILE *pipe) { if (pipe != nullptr) { +#ifdef _MSC_VER + _pclose(pipe); +#else pclose(pipe); +#endif } }; @@ -289,6 +303,7 @@ void executeCommands(const std::vector &commands) { std::vector threads; std::vector errors; + threads.reserve(commands.size()); for (const auto &command : commands) { threads.emplace_back([&command, &errors]() { try { @@ -327,8 +342,11 @@ auto executeCommandWithEnv(const std::string &command, std::lock_guard lock(envMutex); for (const auto &var : envVars) { - char *oldValue = std::getenv(var.first.c_str()); - if (oldValue != nullptr) { + std::lock_guard lock(envMutex); + std::shared_ptr env; + GET_OR_CREATE_PTR(env, utils::Env, "LITHIUM.ENV"); + auto oldValue = env->getEnv(var.first); + if (!oldValue.empty()) { oldEnvVars[var.first] = std::string(oldValue); } SETENV(var.first.c_str(), var.second.c_str()); diff --git a/src/atom/system/crash.cpp b/src/atom/system/crash.cpp index 041a9c4a..914a934b 100644 --- a/src/atom/system/crash.cpp +++ b/src/atom/system/crash.cpp @@ -14,15 +14,16 @@ Description: Crash Report #include "crash.hpp" -#include -#include -#include #include #include #include #include #include +#ifndef _MSC_VER +#include +#endif + #include "atom/error/stacktrace.hpp" #include "atom/log/loguru.hpp" #include "atom/sysinfo/cpu.hpp" @@ -35,37 +36,40 @@ Description: Crash Report #include "platform.hpp" namespace atom::system { + auto getSystemInfo() -> std::string { std::stringstream sss; auto osInfo = getOperatingSystemInfo(); - sss << "System Information:" << std::endl; - sss << "- Operating system: " << osInfo.osName << " " << osInfo.osVersion - << std::endl; - sss << "- Architecture: " << osInfo.architecture << std::endl; - sss << "- Kernel version: " << osInfo.kernelVersion << std::endl; - sss << "- Computer name: " << osInfo.computerName << std::endl; - sss << "- Compiler: " << osInfo.compiler << std::endl; - sss << "- GUI: " << (ATOM_HAS_GUI() ? "Yes" : "No") << std::endl; - - sss << "CPU:" << std::endl; - sss << "- Usage: " << getCurrentCpuUsage() << "%" << std::endl; - sss << "- Model: " << getCPUModel() << std::endl; - sss << "- Frequency: " << getProcessorFrequency() << " GHz" << std::endl; - sss << "- Temperature: " << getCurrentCpuTemperature() << " °C" - << std::endl; - sss << "- Core: " << getNumberOfPhysicalCPUs() << std::endl; - sss << "- Package: " << getNumberOfPhysicalPackages() << std::endl; - - sss << "Current Memory Status:" << std::endl; - sss << "- Usage: " << getMemoryUsage() << "%" << std::endl; - sss << "- Total: " << getTotalMemorySize() << " MB" << std::endl; - sss << "- Free: " << getAvailableMemorySize() << " MB" << std::endl; - - sss << "Disk:" << std::endl; - sss << "- Usage: " << std::endl; + sss << "System Information:\n"; + sss << "-------------------\n"; + sss << "Operating system: " << osInfo.osName << " " << osInfo.osVersion + << "\n"; + sss << "Architecture: " << osInfo.architecture << "\n"; + sss << "Kernel version: " << osInfo.kernelVersion << "\n"; + sss << "Computer name: " << osInfo.computerName << "\n"; + sss << "Compiler: " << osInfo.compiler << "\n"; + sss << "GUI: " << (ATOM_HAS_GUI() ? "Yes" : "No") << "\n\n"; + + sss << "CPU Information:\n"; + sss << "----------------\n"; + sss << "Usage: " << getCurrentCpuUsage() << "%\n"; + sss << "Model: " << getCPUModel() << "\n"; + sss << "Frequency: " << getProcessorFrequency() << " GHz\n"; + sss << "Temperature: " << getCurrentCpuTemperature() << " °C\n"; + sss << "Cores: " << getNumberOfPhysicalCPUs() << "\n"; + sss << "Packages: " << getNumberOfPhysicalPackages() << "\n\n"; + + sss << "Memory Status:\n"; + sss << "--------------\n"; + sss << "Usage: " << getMemoryUsage() << "%\n"; + sss << "Total: " << getTotalMemorySize() << " MB\n"; + sss << "Free: " << getAvailableMemorySize() << " MB\n\n"; + + sss << "Disk Usage:\n"; + sss << "-----------\n"; for (const auto &[drive, usage] : getDiskUsage()) { - sss << "- " << drive << ": " << usage << "%" << std::endl; + sss << drive << ": " << usage << "%\n"; } return sss.str(); @@ -79,34 +83,37 @@ void saveCrashLog(std::string_view error_msg) { } std::stringstream sss; - sss << "Program crashed at: " << utils::getChinaTimestampString() - << std::endl; - sss << "Error messsage: " << error_msg << std::endl; + sss << "Program crashed at: " << utils::getChinaTimestampString() << "\n"; + sss << "Error message: " << error_msg << "\n\n"; - sss << "==================== StackTrace ====================" << std::endl; + sss << "==================== Stack Trace ====================\n"; atom::error::StackTrace stackTrace; - sss << stackTrace.toString() << std::endl; - sss << "==================== System Information ====================" - << std::endl; - sss << systemInfo << std::endl; - sss << "================= Environment Variables Information " - "==================" - << std::endl; + sss << stackTrace.toString() << "\n\n"; + + sss << "==================== System Information ====================\n"; + sss << systemInfo << "\n"; + + sss << "================= Environment Variables ===================\n"; if (environmentInfo.empty()) { - sss << "Failed to get environment information." << std::endl; + sss << "Failed to get environment information.\n"; } else { - sss << environmentInfo << std::endl; + sss << environmentInfo << "\n"; } QuoteManager quotes; quotes.loadQuotesFromJson("./quotes.json"); - sss << "============ Famous saying: " << quotes.getRandomQuote() - << " ============" << std::endl; + sss << "============ Famous Saying: " << quotes.getRandomQuote() + << " ============\n"; std::stringstream ssss; std::time_t now = std::time(nullptr); - ssss << "crash_report/crash_" - << std::put_time(std::localtime(&now), "%Y%m%d_%H%M%S") << ".log"; + std::tm localTime; + if (localtime_s(&localTime, &now) != 0) { + // Handle error + THROW_RUNTIME_ERROR("Failed to get local time."); + } + ssss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") + << ".log"; std::filesystem::path dirPath("crash_report"); if (!std::filesystem::exists(dirPath)) { std::filesystem::create_directory(dirPath); @@ -117,4 +124,5 @@ void saveCrashLog(std::string_view error_msg) { ofs.close(); } } -} // namespace atom::system + +} // namespace atom::system \ No newline at end of file diff --git a/src/atom/system/device.cpp b/src/atom/system/device.cpp index 4cd5a297..090ff857 100644 --- a/src/atom/system/device.cpp +++ b/src/atom/system/device.cpp @@ -1,5 +1,6 @@ #include "device.hpp" +#include #include #include @@ -29,6 +30,16 @@ namespace atom::system { #ifdef _WIN32 +constexpr size_t BUFFER_SIZE = 512; +constexpr size_t ADDRESS_SIZE = 18; +constexpr int BLUETOOTH_SEARCH_TIMEOUT = 15; +constexpr int BYTE_5 = 5; +constexpr int BYTE_4 = 4; +constexpr int BYTE_3 = 3; +constexpr int BYTE_2 = 2; +constexpr int BYTE_1 = 1; +constexpr int BYTE_0 = 0; + auto enumerateUsbDevices() -> std::vector { std::vector devices; HDEVINFO deviceInfoSet = SetupDiGetClassDevs( @@ -43,11 +54,11 @@ auto enumerateUsbDevices() -> std::vector { SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData) != 0; i++) { DWORD dataType; DWORD size; - char buffer[512]; + std::array buffer; if (SetupDiGetDeviceRegistryProperty( deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC, &dataType, - (PBYTE)buffer, sizeof(buffer), &size)) { - devices.push_back({buffer, ""}); + reinterpret_cast(buffer.data()), buffer.size(), &size)) { + devices.push_back({buffer.data(), ""}); } } @@ -69,11 +80,11 @@ auto enumerateSerialPorts() -> std::vector { SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData) != 0; i++) { DWORD dataType; DWORD size; - char buffer[512]; + std::array buffer; if (SetupDiGetDeviceRegistryProperty( deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC, &dataType, - (PBYTE)buffer, sizeof(buffer), &size)) { - devices.push_back({buffer, ""}); + reinterpret_cast(buffer.data()), buffer.size(), &size)) { + devices.push_back({buffer.data(), ""}); } } @@ -84,7 +95,14 @@ auto enumerateSerialPorts() -> std::vector { auto enumerateBluetoothDevices() -> std::vector { std::vector devices; BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = { - sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), 1, 0, 1, 1, 1, 15, nullptr}; + sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), + 1, + 0, + 1, + 1, + 1, + BLUETOOTH_SEARCH_TIMEOUT, + nullptr}; BLUETOOTH_DEVICE_INFO deviceInfo; deviceInfo.dwSize = sizeof(BLUETOOTH_DEVICE_INFO); @@ -93,15 +111,20 @@ auto enumerateBluetoothDevices() -> std::vector { if (btFind != nullptr) { do { - std::wstring ws(deviceInfo.szName); - std::string name(ws.begin(), ws.end()); - char address[18]; - snprintf( - address, sizeof(address), "%02X:%02X:%02X:%02X:%02X:%02X", - deviceInfo.Address.rgBytes[5], deviceInfo.Address.rgBytes[4], - deviceInfo.Address.rgBytes[3], deviceInfo.Address.rgBytes[2], - deviceInfo.Address.rgBytes[1], deviceInfo.Address.rgBytes[0]); - devices.push_back({name, address}); + std::wstring wideName(deviceInfo.szName); + std::string name(wideName.begin(), wideName.end()); + std::array address; + std::string formattedAddress = + std::format("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", + deviceInfo.Address.rgBytes[BYTE_5], + deviceInfo.Address.rgBytes[BYTE_4], + deviceInfo.Address.rgBytes[BYTE_3], + deviceInfo.Address.rgBytes[BYTE_2], + deviceInfo.Address.rgBytes[BYTE_1], + deviceInfo.Address.rgBytes[BYTE_0]); + std::copy(formattedAddress.begin(), formattedAddress.end(), + address.begin()); + devices.push_back({name, address.data()}); } while (BluetoothFindNextDevice(btFind, &deviceInfo) != 0); BluetoothFindDeviceClose(btFind); } diff --git a/src/atom/system/env.cpp b/src/atom/system/env.cpp index fcb9ca39..cd3a2921 100644 --- a/src/atom/system/env.cpp +++ b/src/atom/system/env.cpp @@ -251,7 +251,7 @@ auto Env::Environ() -> std::unordered_map { void Env::setVariable(const std::string &name, const std::string &value, bool overwrite) { #if defined(_WIN32) || defined(_WIN64) - if (overwrite || !getenv(name.c_str())) { + if (overwrite || (getenv(name.c_str()) == nullptr)) { if (SetEnvironmentVariableA(name.c_str(), value.c_str()) == 0) { LOG_F(ERROR, "Failed to set environment variable: {}", name); } @@ -283,7 +283,7 @@ auto Env::getVariable(const std::string &name) -> std::string { // 删除环境变量 void Env::unsetVariable(const std::string &name) { #if defined(_WIN32) || defined(_WIN64) - if (SetEnvironmentVariableA(name.c_str(), NULL) == 0) { + if (SetEnvironmentVariableA(name.c_str(), nullptr) == 0) { LOG_F(ERROR, "Failed to unset environment variable: {}", name); } #else @@ -295,22 +295,24 @@ void Env::unsetVariable(const std::string &name) { // 列出所有环境变量 auto Env::listVariables() -> std::vector { + std::vector vars; + #if defined(_WIN32) || defined(_WIN64) LPCH envStrings = GetEnvironmentStringsA(); - if (envStrings) { - for (LPCH var = envStrings; *var; var += strlen(var) + 1) { + if (envStrings != nullptr) { + for (LPCH var = envStrings; *var != '\0'; var += strlen(var) + 1) { vars.emplace_back(var); } FreeEnvironmentStringsA(envStrings); } #else - std::vector vars; char **env = environ; while (*env != nullptr) { vars.emplace_back(*env); ++env; } #endif + return vars; } diff --git a/src/atom/utils/aligned.hpp b/src/atom/utils/aligned.hpp index 56e09a28..05286d2e 100644 --- a/src/atom/utils/aligned.hpp +++ b/src/atom/utils/aligned.hpp @@ -18,7 +18,20 @@ Description: Validate aligned storage #include namespace atom::utils { -//! Aligned storage validator +/** + * @brief A class template to validate aligned storage. + * + * This class template is used to validate that the storage size and alignment + * requirements are met for a given implementation. It uses static assertions + * to ensure that the storage size is greater than or equal to the + * implementation size, and that the storage alignment is a multiple of the + * implementation alignment. + * + * @tparam ImplSize The size of the implementation. + * @tparam ImplAlign The alignment of the implementation. + * @tparam StorageSize The size of the storage. + * @tparam StorageAlign The alignment of the storage. + */ template class ValidateAlignedStorage { @@ -27,6 +40,7 @@ class ValidateAlignedStorage { static_assert(StorageAlign % ImplAlign == 0, "StorageAlign must be a multiple of ImplAlign"); }; + } // namespace atom::utils -#endif // ATOM_UTILS_ALIGNED_HPP +#endif // ATOM_UTILS_ALIGNED_HPP \ No newline at end of file diff --git a/src/atom/utils/anyutils.hpp b/src/atom/utils/anyutils.hpp index e7cd5575..a7e92ab8 100644 --- a/src/atom/utils/anyutils.hpp +++ b/src/atom/utils/anyutils.hpp @@ -16,11 +16,12 @@ Description: A collection of useful functions with std::any Or Any #define ATOM_EXPERIMENT_ANYUTILS_HPP #include +#include #include +#include #include #include #include - #include "atom/function/concept.hpp" template @@ -34,11 +35,12 @@ concept CanBeStringifiedToJson = requires(T t) { }; template -[[nodiscard]] std::string toString(const T &value, bool prettyPrint = false); +[[nodiscard]] auto toString(const T &value, + bool prettyPrint = false) -> std::string; -template -[[nodiscard]] std::string toString(const Container &container, - bool prettyPrint = false) { +template +[[nodiscard]] auto toString(const Container &container, + bool prettyPrint = false) -> std::string { std::string result = "["; for (const auto &item : container) { if constexpr (IsBuiltIn) { @@ -56,7 +58,7 @@ template template [[nodiscard]] auto toString(const std::unordered_map &map, - bool prettyPrint = false) -> std::string { + bool prettyPrint = false) -> std::string { std::string result = "{"; for (const auto &pair : map) { result += toString(pair.first, prettyPrint) + ": " + @@ -70,24 +72,21 @@ template } template -[[nodiscard]] std::string toString(const std::pair &pair, - bool prettyPrint = false) { +[[nodiscard]] auto toString(const std::pair &pair, + bool prettyPrint = false) -> std::string { return "(" + toString(pair.first, prettyPrint) + ", " + toString(pair.second, prettyPrint) + ")"; } template -[[nodiscard]] std::string toString(const T &value, bool prettyPrint) { - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v) { +[[nodiscard]] auto toString(const T &value, bool prettyPrint) -> std::string { + if constexpr (String || Char) { return value; } else if constexpr (std::is_same_v) { return value ? "true" : "false"; - } else if constexpr (std::is_arithmetic_v) { + } else if constexpr (Number) { return std::to_string(value); - } else if constexpr (std::is_pointer_v) { + } else if constexpr (Pointer || SmartPointer) { if (value == nullptr) { return "nullptr"; } @@ -98,11 +97,12 @@ template } template -[[nodiscard]] std::string toJson(const T &value, bool prettyPrint = false); +[[nodiscard]] auto toJson(const T &value, + bool prettyPrint = false) -> std::string; -template -[[nodiscard]] std::string toJson(const Container &container, - bool prettyPrint = false) { +template +[[nodiscard]] auto toJson(const Container &container, + bool prettyPrint = false) -> std::string { std::string result = "["; for (const auto &item : container) { result += toJson(item, prettyPrint) + ", "; @@ -115,8 +115,8 @@ template } template -[[nodiscard]] std::string toJson(const std::unordered_map &map, - bool prettyPrint = false) { +[[nodiscard]] auto toJson(const std::unordered_map &map, + bool prettyPrint = false) -> std::string { std::string result = "{"; for (const auto &pair : map) { result += toJson(pair.first, prettyPrint) + ": " + @@ -130,22 +130,21 @@ template } template -[[nodiscard]] std::string toJson(const std::pair &pair, - bool prettyPrint = false) { +[[nodiscard]] auto toJson(const std::pair &pair, + bool prettyPrint = false) -> std::string { return "{" + toJson(pair.first, prettyPrint) + ", " + toJson(pair.second, prettyPrint) + "}"; } template -[[nodiscard]] std::string toJson(const T &value, bool prettyPrint) { - if constexpr (std::is_same_v) { +[[nodiscard]] auto toJson(const T &value, bool prettyPrint) -> std::string { + if constexpr (String) { return "\"" + value + "\""; - } else if constexpr (std::is_same_v || - std::is_same_v) { + } else if constexpr (Char) { return "\"" + std::string(value) + "\""; - } else if constexpr (std::is_arithmetic_v) { + } else if constexpr (Number) { return std::to_string(value); - } else if constexpr (std::is_pointer_v) { + } else if constexpr (Pointer || SmartPointer) { if (value == nullptr) { return "null"; } @@ -156,11 +155,12 @@ template } template -[[nodiscard]] std::string toXml(const T &value, const std::string &tagName); +[[nodiscard]] auto toXml(const T &value, + const std::string &tagName) -> std::string; -template -[[nodiscard]] std::string toXml(const Container &container, - const std::string &tagName) { +template +[[nodiscard]] auto toXml(const Container &container, + const std::string &tagName) -> std::string { std::string result; for (const auto &item : container) { result += toXml(item, tagName); @@ -169,8 +169,9 @@ template } template -[[nodiscard]] std::string toXml(const std::unordered_map &map, - [[maybe_unused]] const std::string &tagName) { +[[nodiscard]] auto toXml(const std::unordered_map &map, + [[maybe_unused]] const std::string &tagName) + -> std::string { std::string result; for (const auto &pair : map) { result += toXml(pair.second, pair.first); @@ -179,8 +180,8 @@ template } template -[[nodiscard]] std::string toXml(const std::pair &pair, - const std::string &tagName) { +[[nodiscard]] auto toXml(const std::pair &pair, + const std::string &tagName) -> std::string { std::string result = "<" + tagName + ">"; result += toXml(pair.first, "key"); result += toXml(pair.second, "value"); @@ -189,15 +190,14 @@ template } template -[[nodiscard]] std::string toXml(const T &value, const std::string &tagName) { - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) { +[[nodiscard]] auto toXml(const T &value, + const std::string &tagName) -> std::string { + if constexpr (String || Char) { return "<" + tagName + ">" + value + ""; - } else if constexpr (std::is_arithmetic_v) { + } else if constexpr (Number) { return "<" + tagName + ">" + std::to_string(value) + ""; - } else if constexpr (std::is_pointer_v) { + } else if constexpr (Pointer || SmartPointer) { if (value == nullptr) [[unlikely]] { return "<" + tagName + "null" + "/>"; } else [[likely]] { @@ -209,11 +209,12 @@ template } template -[[nodiscard]] std::string toYaml(const T &value, const std::string &key); +[[nodiscard]] auto toYaml(const T &value, + const std::string &key) -> std::string; -template -[[nodiscard]] std::string toYaml(const Container &container, - const std::string &key) { +template +[[nodiscard]] auto toYaml(const Container &container, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + ":\n"; for (const auto &item : container) { result += (key.empty() ? "- " : " - ") + toYaml(item, "") + "\n"; @@ -222,8 +223,8 @@ template } template -[[nodiscard]] std::string toYaml(const std::unordered_map &map, - const std::string &key) { +[[nodiscard]] auto toYaml(const std::unordered_map &map, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + ":\n"; for (const auto &pair : map) { result += (key.empty() ? "" : " ") + toYaml(pair.second, pair.first); @@ -232,8 +233,8 @@ template } template -[[nodiscard]] std::string toYaml(const std::pair &pair, - const std::string &key) { +[[nodiscard]] auto toYaml(const std::pair &pair, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + ":\n"; result += std::string((key.empty() ? "" : " ")) + "key: " + toYaml(pair.first, ""); @@ -243,16 +244,15 @@ template } template -[[nodiscard]] std::string toYaml(const T &value, const std::string &key) { - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) { +[[nodiscard]] auto toYaml(const T &value, + const std::string &key) -> std::string { + if constexpr (String || Char) { return key.empty() ? "\"" + std::string(value) + "\"" : key + ": \"" + std::string(value) + "\"\n"; - } else if constexpr (std::is_arithmetic_v) { + } else if constexpr (Number) { return key.empty() ? std::to_string(value) : key + ": " + std::to_string(value) + "\n"; - } else if constexpr (std::is_pointer_v) { + } else if constexpr (Pointer || SmartPointer) { if (value == nullptr) [[unlikely]] { return key.empty() ? "null" : key + ": null\n"; } else [[likely]] { @@ -264,25 +264,24 @@ template } template -[[nodiscard]] std::string toYaml(const std::tuple &tuple, - const std::string &key) { +[[nodiscard]] auto toYaml(const std::tuple &tuple, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + ":\n"; std::apply( - [&result, &key](const Ts &...args) { - ((result += - (key.empty() ? "- " : " - ") + toYaml(args, "") + "\n"), - ...); + [&result](const Ts &...args) { + ((result += "- " + toYaml(args, "") + "\n"), ...); }, tuple); return result; } template -[[nodiscard]] std::string toToml(const T &value, const std::string &key); +[[nodiscard]] auto toToml(const T &value, + const std::string &key) -> std::string; -template -[[nodiscard]] std::string toToml(const Container &container, - const std::string &key) { +template +[[nodiscard]] auto toToml(const Container &container, + const std::string &key) -> std::string { std::string result = key + " = [\n"; for (const auto &item : container) { result += " " + toToml(item, "") + ",\n"; @@ -295,8 +294,8 @@ template } template -[[nodiscard]] std::string toToml(const std::unordered_map &map, - const std::string &key) { +[[nodiscard]] auto toToml(const std::unordered_map &map, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + " = {\n"; for (const auto &pair : map) { result += " " + toToml(pair.second, pair.first); @@ -309,8 +308,8 @@ template } template -[[nodiscard]] std::string toToml(const std::pair &pair, - const std::string &key) { +[[nodiscard]] auto toToml(const std::pair &pair, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + " = {\n"; result += " key = " + toToml(pair.first, "") + ",\n"; result += " value = " + toToml(pair.second, "") + "\n"; @@ -319,16 +318,15 @@ template } template -[[nodiscard]] std::string toToml(const T &value, const std::string &key) { - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v) { +[[nodiscard]] auto toToml(const T &value, + const std::string &key) -> std::string { + if constexpr (String || Char) { return key.empty() ? "\"" + std::string(value) + "\"" : key + " = \"" + std::string(value) + "\"\n"; - } else if constexpr (std::is_arithmetic_v) { + } else if constexpr (Number) { return key.empty() ? std::to_string(value) : key + " = " + std::to_string(value) + "\n"; - } else if constexpr (std::is_pointer_v) { + } else if constexpr (Pointer || SmartPointer) { if (value == nullptr) [[unlikely]] { return key.empty() ? "null" : key + " = null\n"; } else [[likely]] { @@ -340,11 +338,11 @@ template } template -[[nodiscard]] std::string toToml(const std::tuple &tuple, - const std::string &key) { +[[nodiscard]] auto toToml(const std::tuple &tuple, + const std::string &key) -> std::string { std::string result = key.empty() ? "" : key + " = [\n"; std::apply( - [&result, &key](const Ts &...args) { + [&result](const Ts &...args) { ((result += " " + toToml(args, "") + ",\n"), ...); }, tuple); diff --git a/src/atom/utils/argsview.hpp b/src/atom/utils/argsview.hpp index a57b1aa1..736ac49c 100644 --- a/src/atom/utils/argsview.hpp +++ b/src/atom/utils/argsview.hpp @@ -2,19 +2,21 @@ #define ATOM_UTILS_ARGUMENT_PARSER_HPP #include +#include #include #include #include -#include #include #include #include +#include "exception.hpp" #include "macro.hpp" namespace atom::utils { + class ArgumentParser { public: - enum class ArgType { STRING, INTEGER, FLOAT, BOOLEAN, AUTO }; + enum class ArgType { STRING, INTEGER, FLOAT, BOOLEAN, FILEPATH, AUTO }; ArgumentParser() = default; @@ -77,13 +79,13 @@ class ArgumentParser { std::string description_; std::string epilog_; - auto detectType(const std::any& value) const -> ArgType; + static auto detectType(const std::any& value) -> ArgType; - auto parseValue(ArgType type, const std::string& value) const -> std::any; + static auto parseValue(ArgType type, const std::string& value) -> std::any; - auto argTypeToString(ArgType type) const -> std::string; + static auto argTypeToString(ArgType type) -> std::string; - auto anyToString(const std::any& value) const -> std::string; + static auto anyToString(const std::any& value) -> std::string; }; ATOM_INLINE ArgumentParser::ArgumentParser(const std::string& program_name) { @@ -153,12 +155,12 @@ ATOM_INLINE void ArgumentParser::parse(int argc, char* argv[]) { arguments_[arg].value = parseValue(arguments_[arg].type, argv[++i]); } else { - throw std::invalid_argument("Value for argument " + - arg + " not provided"); + THROW_INVALID_ARGUMENT("Value for argument " + arg + + " not provided"); } } } else { - throw std::invalid_argument("Unknown argument: " + arg); + THROW_INVALID_ARGUMENT("Unknown argument: " + arg); } } else { positional_arguments_.push_back(arg); @@ -168,14 +170,14 @@ ATOM_INLINE void ArgumentParser::parse(int argc, char* argv[]) { for (const auto& [name, argument] : arguments_) { if (argument.required && !argument.value.has_value() && !argument.defaultValue.has_value()) { - throw std::invalid_argument("Required argument " + name + - " not provided"); + THROW_INVALID_ARGUMENT("Required argument " + name + + " not provided"); } } } template -std::optional ArgumentParser::get(const std::string& name) const { +auto ArgumentParser::get(const std::string& name) const -> std::optional { if (arguments_.find(name) != arguments_.end()) { if (arguments_.at(name).value.has_value()) { std::cout << typeid(arguments_.at(name).value.value()).name() @@ -183,7 +185,7 @@ std::optional ArgumentParser::get(const std::string& name) const { if (arguments_.at(name).value.has_value()) { return std::any_cast(arguments_.at(name).value.value()); } - throw std::invalid_argument("Invalid value for argument " + name); + THROW_INVALID_ARGUMENT("Invalid value for argument " + name); } if (arguments_.at(name).defaultValue.has_value()) { return std::any_cast(arguments_.at(name).defaultValue); @@ -243,8 +245,7 @@ ATOM_INLINE void ArgumentParser::printHelp() const { std::cout << "\n" << epilog_ << std::endl; } -ATOM_INLINE auto ArgumentParser::detectType(const std::any& value) const - -> ArgType { +ATOM_INLINE auto ArgumentParser::detectType(const std::any& value) -> ArgType { if (value.type() == typeid(int)) { return ArgType::INTEGER; } @@ -257,11 +258,14 @@ ATOM_INLINE auto ArgumentParser::detectType(const std::any& value) const if (value.type() == typeid(std::string)) { return ArgType::STRING; } + if (value.type() == typeid(std::filesystem::path)) { + return ArgType::FILEPATH; + } return ArgType::STRING; // Default to string if undetectable } ATOM_INLINE auto ArgumentParser::parseValue( - ArgType type, const std::string& value) const -> std::any { + ArgType type, const std::string& value) -> std::any { switch (type) { case ArgType::STRING: return value; @@ -277,6 +281,8 @@ ATOM_INLINE auto ArgumentParser::parseValue( } case ArgType::BOOLEAN: return value == "true"; + case ArgType::FILEPATH: + return std::filesystem::path(value); case ArgType::AUTO: { if (value == "true" || value == "false") { return value == "true"; @@ -294,12 +300,11 @@ ATOM_INLINE auto ArgumentParser::parseValue( return value; } default: - throw std::invalid_argument("Unknown argument type"); + THROW_INVALID_ARGUMENT("Unknown argument type"); } } -ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) const - -> std::string { +ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) -> std::string { switch (type) { case ArgType::STRING: return "string"; @@ -309,6 +314,8 @@ ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) const return "float"; case ArgType::BOOLEAN: return "bool"; + case ArgType::FILEPATH: + return "filepath"; case ArgType::AUTO: return "auto"; default: @@ -316,7 +323,7 @@ ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) const } } -ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) const +ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) -> std::string { try { if (value.type() == typeid(std::string)) { @@ -342,6 +349,9 @@ ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) const } return result; } + if (value.type() == typeid(std::filesystem::path)) { + return std::any_cast(value).string(); + } } catch (const std::bad_any_cast&) { return "unknown"; } @@ -350,4 +360,4 @@ ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) const } // namespace atom::utils -#endif +#endif \ No newline at end of file diff --git a/src/atom/utils/bit.hpp b/src/atom/utils/bit.hpp index 27f4b0c9..b20e1756 100644 --- a/src/atom/utils/bit.hpp +++ b/src/atom/utils/bit.hpp @@ -2,6 +2,7 @@ #define ATOM_UTILS_BIT_HPP #include +#include #include #include #include @@ -43,12 +44,7 @@ constexpr auto createMask(uint32_t bits) ATOM_NOEXCEPT -> T { */ template constexpr auto countBytes(T value) ATOM_NOEXCEPT -> uint32_t { - if constexpr (sizeof(T) <= sizeof(unsigned int)) { - return static_cast(std::popcount(value)); - } else { - return static_cast( - std::popcount(static_cast(value))); - } + return static_cast(std::popcount(value)); } /** @@ -64,12 +60,8 @@ constexpr auto countBytes(T value) ATOM_NOEXCEPT -> uint32_t { */ template constexpr auto reverseBits(T value) ATOM_NOEXCEPT -> T { - T reversed = 0; - for (int i = 0; i < std::numeric_limits::digits; ++i) { - reversed |= ((value >> i) & T{1}) - << (std::numeric_limits::digits - i - 1); - } - return reversed; + return std::bit_cast( + std::bitset::digits>(value).to_ullong()); } /** @@ -87,9 +79,7 @@ constexpr auto reverseBits(T value) ATOM_NOEXCEPT -> T { */ template constexpr auto rotateLeft(T value, int shift) ATOM_NOEXCEPT -> T { - const int BITS = std::numeric_limits::digits; - shift %= BITS; - return (value << shift) | (value >> (BITS - shift)); + return std::rotl(value, shift); } /** @@ -107,11 +97,44 @@ constexpr auto rotateLeft(T value, int shift) ATOM_NOEXCEPT -> T { */ template constexpr auto rotateRight(T value, int shift) ATOM_NOEXCEPT -> T { - const int BITS = std::numeric_limits::digits; - shift %= BITS; - return (value >> shift) | (value << (BITS - shift)); + return std::rotr(value, shift); +} + +/** + * @brief Merges two bitmasks into one. + * + * This function merges two bitmasks of type `T` into one by performing a + * bitwise OR operation. + * + * @tparam T The unsigned integral type of the bitmasks. + * @param mask1 The first bitmask. + * @param mask2 The second bitmask. + * @return T The merged bitmask. + */ +template +constexpr auto mergeMasks(T mask1, T mask2) ATOM_NOEXCEPT -> T { + return mask1 | mask2; +} + +/** + * @brief Splits a bitmask into two parts. + * + * This function splits a bitmask of type `T` into two parts at the specified + * position. + * + * @tparam T The unsigned integral type of the bitmask. + * @param mask The bitmask to split. + * @param position The position to split the bitmask. + * @return std::pair A pair containing the two parts of the split bitmask. + */ +template +constexpr auto splitMask(T mask, + uint32_t position) ATOM_NOEXCEPT -> std::pair { + T lowerPart = mask & createMask(position); + T upperPart = mask & ~createMask(position); + return {lowerPart, upperPart}; } } // namespace atom::utils -#endif +#endif \ No newline at end of file diff --git a/src/atom/utils/span.hpp b/src/atom/utils/span.hpp new file mode 100644 index 00000000..fe8da8ea --- /dev/null +++ b/src/atom/utils/span.hpp @@ -0,0 +1,345 @@ +#ifndef ATOM_UTILS_SPAN_HPP +#define ATOM_UTILS_SPAN_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace atom::utils { +/** + * @brief Computes the sum of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return T The sum of the elements. + */ +template +auto sum(std::span data) -> T { + return std::accumulate(data.begin(), data.end(), T{0}); +} + +/** + * @brief Checks if a span contains a specific value. + * + * @tparam T The type of elements in the span. + * @param data The span to search. + * @param value The value to find. + * @return bool True if the value is found, false otherwise. + */ +template +auto contains(std::span data, T value) -> bool { + return std::find(data.begin(), data.end(), value) != data.end(); +} + +/** + * @brief Sorts the elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span to sort. + */ +template +void sortSpan(std::span data) { + std::sort(data.begin(), data.end()); +} + +/** + * @brief Filters elements in a span based on a predicate. + * + * @tparam T The type of elements in the span. + * @tparam Predicate The type of the predicate function. + * @param data The span to filter. + * @param pred The predicate function to apply. + * @return std::vector A vector containing the filtered elements. + */ +template +auto filterSpan(std::span data, Predicate pred) -> std::vector { + std::vector result; + std::copy_if(data.begin(), data.end(), std::back_inserter(result), pred); + return result; +} + +/** + * @brief Counts the number of elements in a span that satisfy a predicate. + * + * @tparam T The type of elements in the span. + * @tparam Predicate The type of the predicate function. + * @param data The span to search. + * @param pred The predicate function to apply. + * @return size_t The number of elements that satisfy the predicate. + */ +template +auto countIfSpan(std::span data, Predicate pred) -> size_t { + return std::count_if(data.begin(), data.end(), pred); +} + +/** + * @brief Finds the minimum element in a span. + * + * @tparam T The type of elements in the span. + * @param data The span to search. + * @return T The minimum element. + */ +template +auto minElementSpan(std::span data) -> T { + return *std::min_element(data.begin(), data.end()); +} + +/** + * @brief Finds the maximum element in a span. + * + * @tparam T The type of elements in the span. + * @param data The span to search. + * @return T The maximum element. + */ +template +auto maxElementSpan(std::span data) -> T { + return *std::max_element(data.begin(), data.end()); +} + +/** + * @brief Finds the index of the maximum element in a span. + * + * @tparam T The type of elements in the span. + * @param data The span to search. + * @return size_t The index of the maximum element. + */ +template +auto maxElementIndex(std::span data) -> size_t { + return std::distance(data.begin(), + std::max_element(data.begin(), data.end())); +} + +/** + * @brief Prints the elements of a span. + * + * @tparam T The type of elements in the span. + * @param data The span to print. + */ +template +void printSpan(std::span data) { + for (const auto& element : data) { + std::cout << element << " "; + } + std::cout << '\n'; +} + +/** + * @brief Transposes a matrix represented as a span. + * + * @tparam T The type of elements in the matrix. + * @tparam N The size of the span. + * @param matrix The span representing the matrix. + * @param rows The number of rows in the matrix. + * @param cols The number of columns in the matrix. + */ +template +void transposeMatrix(std::span matrix, size_t rows, size_t cols) { + std::vector transposedMatrix(cols * rows); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < cols; ++j) { + transposedMatrix[j * rows + i] = matrix[i * cols + j]; + } + } + std::copy(transposedMatrix.begin(), transposedMatrix.end(), matrix.begin()); +} + +/** + * @brief Normalizes the elements in a span to the range [0, 1]. + * + * @tparam T The type of elements in the span. + * @param data The span to normalize. + */ +template +void normalize(std::span data) { + T minVal = *std::min_element(data.begin(), data.end()); + T maxVal = *std::max_element(data.begin(), data.end()); + T range = maxVal - minVal; + if (range == 0) { + return; // Avoid division by zero + } + for (auto& element : data) { + element = (element - minVal) / range; + } +} + +/** + * @brief Computes the mean of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return double The mean of the elements. + */ +template +auto mean(std::span data) -> double { + if (data.empty()) { + return 0.0; + } + return static_cast(sum(data)) / data.size(); +} + +/** + * @brief Finds the median of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return double The median of the elements. + */ +template +auto median(std::span data) -> double { + if (data.empty()) { + return 0.0; + } + std::vector sortedData(data.begin(), data.end()); + std::sort(sortedData.begin(), sortedData.end()); + size_t mid = sortedData.size() / 2; + if (sortedData.size() % 2 == 0) { + return (sortedData[mid - 1] + sortedData[mid]) / 2.0; + } + return sortedData[mid]; +} + +/** + * @brief Finds the mode of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return T The mode of the elements. + */ +template +auto mode(std::span data) -> T { + std::unordered_map frequency; + for (const auto& element : data) { + ++frequency[element]; + } + return std::max_element( + frequency.begin(), frequency.end(), + [](const auto& a, const auto& b) { return a.second < b.second; }) + ->first; +} + +/** + * @brief Computes the standard deviation of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return double The standard deviation of the elements. + */ +template +auto standardDeviation(std::span data) -> double { + if (data.empty()) { + return 0.0; + } + double meanValue = mean(data); + double sumOfSquares = std::accumulate( + data.begin(), data.end(), 0.0, [meanValue](double acc, T value) { + return acc + (value - meanValue) * (value - meanValue); + }); + return std::sqrt(sumOfSquares / data.size()); +} + +/** + * @brief Finds the top N maximum elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @param n The number of top elements to find. + * @return std::vector A vector containing the top N elements. + */ +template +auto topNElements(std::span data, size_t n) -> std::vector { + std::vector result(data.begin(), data.end()); + std::partial_sort(result.begin(), result.begin() + n, result.end(), + std::greater()); + result.resize(n); + return result; +} + +/** + * @brief Computes the variance of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return double The variance of the elements. + */ +template +auto variance(std::span data) -> double { + if (data.empty()) { + return 0.0; + } + double meanValue = mean(data); + double sumOfSquares = std::accumulate( + data.begin(), data.end(), 0.0, [meanValue](double acc, T value) { + return acc + (value - meanValue) * (value - meanValue); + }); + return sumOfSquares / data.size(); +} + +/** + * @brief Finds the top N minimum elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @param n The number of top elements to find. + * @return std::vector A vector containing the top N elements. + */ +template +auto bottomNElements(std::span data, size_t n) -> std::vector { + std::vector result(data.begin(), data.end()); + std::partial_sort(result.begin(), result.begin() + n, result.end()); + result.resize(n); + return result; +} + +/** + * @brief Computes the cumulative sum of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return std::vector A vector containing the cumulative sums. + */ +template +auto cumulativeSum(std::span data) -> std::vector { + std::vector result(data.size()); + std::partial_sum(data.begin(), data.end(), result.begin()); + return result; +} + +/** + * @brief Computes the cumulative product of elements in a span. + * + * @tparam T The type of elements in the span. + * @param data The span containing the elements. + * @return std::vector A vector containing the cumulative products. + */ +template +auto cumulativeProduct(std::span data) -> std::vector { + std::vector result(data.size()); + std::partial_sum(data.begin(), data.end(), result.begin(), + std::multiplies()); + return result; +} + +/** + * @brief Finds the index of a specific value in a span. + * + * @tparam T The type of elements in the span. + * @param data The span to search. + * @param value The value to find. + * @return std::optional The index of the value if found, std::nullopt + * otherwise. + */ +template +auto findIndex(std::span data, T value) -> std::optional { + auto it = std::find(data.begin(), data.end(), value); + if (it != data.end()) { + return std::distance(data.begin(), it); + } + return std::nullopt; +} +} // namespace atom::utils + +#endif // ATOM_UTILS_SPAN_HPP diff --git a/src/atom/utils/switch.hpp b/src/atom/utils/switch.hpp index 65cc82e1..c13a69a1 100644 --- a/src/atom/utils/switch.hpp +++ b/src/atom/utils/switch.hpp @@ -17,69 +17,116 @@ Description: Smart Switch just like javascript #include #include +#include +#include #include #include -#include -#if ENABLE_FASTHASH -#include "emhash/hash_table8.hpp" -#else #include -#endif +#include +#include #include "atom/error/exception.hpp" #include "atom/type/noncopyable.hpp" #include "macro.hpp" namespace atom::utils { + /** * @brief A class for implementing a switch statement with string cases, - * enhanced with C++17/20 features. + * enhanced with C++20 features. + * + * This class allows you to register functions associated with string keys, + * similar to a switch statement in JavaScript. It supports multiple return + * types using std::variant and provides a default function if no match is + * found. * * @tparam Args The types of additional arguments to pass to the functions. */ template class StringSwitch : public NonCopyable { public: - using Func = std::function; + /** + * @brief Type alias for the function to be registered. + * + * The function can return a std::variant containing either std::monostate, + * int, or std::string. + */ + using Func = + std::function(Args...)>; + + /** + * @brief Type alias for the default function. + * + * The default function is optional and can be set to handle cases where no + * match is found. + */ using DefaultFunc = std::optional; + /** + * @brief Default constructor. + */ StringSwitch() = default; - // Register a case with the given string and function + /** + * @brief Register a case with the given string and function. + * + * @param str The string key for the case. + * @param func The function to be associated with the string key. + * @throws std::runtime_error if the case is already registered. + */ void registerCase(const std::string &str, Func func) { - if (cases_.find(str) != cases_.end()) { + if (cases_.contains(str)) { THROW_OBJ_ALREADY_EXIST("Case already registered"); } cases_[str] = std::move(func); // Use move semantics for efficiency } - // Unregister a case with the given string + /** + * @brief Unregister a case with the given string. + * + * @param str The string key for the case to be unregistered. + */ void unregisterCase(const std::string &str) { cases_.erase(str); } - // Clear all registered cases + /** + * @brief Clear all registered cases. + */ void clearCases() { cases_.clear(); } - // Match the given string against the registered cases - auto match(const std::string &str, Args... args) -> bool { - auto iter = cases_.find(str); - if (iter != cases_.end()) { - std::invoke(iter->second, args...); - return true; + /** + * @brief Match the given string against the registered cases. + * + * @param str The string key to match. + * @param args Additional arguments to pass to the function. + * @return std::optional> The + * result of the function call, or std::nullopt if no match is found. + */ + auto match(const std::string &str, Args... args) + -> std::optional> { + if (auto iter = cases_.find(str); iter != cases_.end()) { + return std::invoke(iter->second, args...); } if (defaultFunc_) { - std::invoke(*defaultFunc_, - args...); // Use optional's value() for clarity - return true; + return std::invoke(*defaultFunc_, args...); } - return false; + return std::nullopt; } - // Set the default function to be called if no match is found + /** + * @brief Set the default function to be called if no match is found. + * + * @param func The default function. + */ void setDefault(DefaultFunc func) { defaultFunc_ = std::move(func); } - // Get a vector of all registered cases + /** + * @brief Get a vector of all registered cases. + * + * @return std::vector A vector containing all registered + * string keys. + */ ATOM_NODISCARD auto getCases() const -> std::vector { std::vector caseList; for (const auto &[key, value] : @@ -89,29 +136,70 @@ class StringSwitch : public NonCopyable { return caseList; } - // C++17 deduction guide for easier initialization + /** + * @brief C++17 deduction guide for easier initialization. + * + * @tparam T The type of the function to be registered. + * @param str The string key for the case. + * @param func The function to be associated with the string key. + */ template >> void registerCase(const std::string &str, T &&func) { registerCase(str, Func(std::forward(func))); } - // C++20 designated initializers for easier case registration + /** + * @brief C++20 designated initializers for easier case registration. + * + * @param initList An initializer list of pairs containing string keys and + * functions. + */ StringSwitch(std::initializer_list> initList) { for (auto [str, func] : initList) { registerCase(str, std::move(func)); } } + /** + * @brief Match the given string against the registered cases with a span of + * arguments. + * + * @param str The string key to match. + * @param args A span of additional arguments to pass to the function. + * @return std::optional> The + * result of the function call, or std::nullopt if no match is found. + */ + auto matchWithSpan(const std::string &str, std::span args) + -> std::optional> { + if (auto iter = cases_.find(str); iter != cases_.end()) { + return std::apply(iter->second, args); + } + + if (defaultFunc_) { + return std::apply(*defaultFunc_, args); + } + + return std::nullopt; + } + + /** + * @brief Get a vector of all registered cases using ranges. + * + * @return std::vector A vector containing all registered + * string keys. + */ + ATOM_NODISCARD auto getCasesWithRanges() const -> std::vector { + return cases_ | std::views::keys | std::ranges::to(); + } + private: -#if ENABLE_FASTHASH - emhash8::HashMap cases_; -#else - std::unordered_map cases_; -#endif - DefaultFunc defaultFunc_; + std::unordered_map + cases_; ///< A map of string keys to functions. + DefaultFunc defaultFunc_; ///< The default function to be called if no + ///< match is found. }; } // namespace atom::utils -#endif +#endif \ No newline at end of file diff --git a/src/atom/utils/time.cpp b/src/atom/utils/time.cpp index 5c2c5b8a..5681e149 100644 --- a/src/atom/utils/time.cpp +++ b/src/atom/utils/time.cpp @@ -14,91 +14,130 @@ Description: Some useful functions about time #include "time.hpp" +#include #include #include #include namespace atom::utils { -std::string getTimestampString() { +constexpr int K_MILLISECONDS_IN_SECOND = + 1000; // Named constant for magic number +constexpr int K_CHINA_TIMEZONE_OFFSET = 8; + +auto getTimestampString() -> std::string { auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); - auto ms = std::chrono::duration_cast( - now.time_since_epoch()) % - 1000; + auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch()) % + K_MILLISECONDS_IN_SECOND; + + std::tm timeInfo{}; + if (localtime_s(&timeInfo, &time) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert time to local time"); + } - std::stringstream ss; - ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S") << '.' - << std::setfill('0') << std::setw(3) << ms.count(); + std::stringstream timestampStream; + timestampStream << std::put_time(&timeInfo, "%Y-%m-%d %H:%M:%S") << '.' + << std::setfill('0') << std::setw(3) + << milliseconds.count(); - return ss.str(); + return timestampStream.str(); } -std::string convertToChinaTime(const std::string &utcTimeStr) { +auto convertToChinaTime(const std::string &utcTimeStr) -> std::string { // 解析UTC时间字符串 - std::tm tm = {}; - std::istringstream iss(utcTimeStr); - iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + std::tm timeStruct = {}; + std::istringstream inputStream(utcTimeStr); + inputStream >> std::get_time(&timeStruct, "%Y-%m-%d %H:%M:%S"); // 转换为时间点 - auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + auto timePoint = + std::chrono::system_clock::from_time_t(std::mktime(&timeStruct)); - // 转换为东八区时间 - std::chrono::hours offset(8); - auto local_tp = - std::chrono::time_point_cast(tp) + offset; + std::chrono::hours offset(K_CHINA_TIMEZONE_OFFSET); + auto localTimePoint = + std::chrono::time_point_cast(timePoint) + offset; // 格式化为字符串 - auto local_time = std::chrono::system_clock::to_time_t(local_tp); - std::stringstream ss; - ss << std::put_time(std::localtime(&local_time), "%Y-%m-%d %H:%M:%S"); + auto localTime = std::chrono::system_clock::to_time_t(localTimePoint); + std::tm localTimeStruct{}; + if (localtime_s(&localTimeStruct, &localTime) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert time to local time"); + } - return ss.str(); + std::stringstream outputStream; + outputStream << std::put_time(&localTimeStruct, "%Y-%m-%d %H:%M:%S"); + + return outputStream.str(); } -std::string getChinaTimestampString() { +auto getChinaTimestampString() -> std::string { // 获取当前时间点 auto now = std::chrono::system_clock::now(); // 转换为东八区时间点 - std::chrono::hours offset(8); - auto local_tp = + std::chrono::hours offset(K_CHINA_TIMEZONE_OFFSET); + auto localTimePoint = std::chrono::time_point_cast(now) + offset; // 格式化为字符串 - auto local_time = std::chrono::system_clock::to_time_t(local_tp); - std::stringstream ss; - ss << std::put_time(std::localtime(&local_time), "%Y-%m-%d %H:%M:%S"); + auto localTime = std::chrono::system_clock::to_time_t(localTimePoint); + std::tm localTimeStruct{}; + if (localtime_s(&localTimeStruct, &localTime) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert time to local time"); + } + + std::stringstream timestampStream; + timestampStream << std::put_time(&localTimeStruct, "%Y-%m-%d %H:%M:%S"); - return ss.str(); + return timestampStream.str(); } -std::string timeStampToString(time_t timestamp) { - char buffer[80]; - std::strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", std::localtime(×tamp)); - return std::string(buffer); +auto timeStampToString(time_t timestamp) -> std::string { + constexpr size_t K_BUFFER_SIZE = 80; // Named constant for magic number + std::array buffer{}; + std::tm timeStruct{}; + if (localtime_s(&timeStruct, ×tamp) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert timestamp to local time"); + } + + if (std::strftime(buffer.data(), buffer.size(), "%Y-%m-%d %H:%M:%S", + &timeStruct) == 0) { + THROW_TIME_CONVERT_ERROR("strftime failed"); + } + + return std::string(buffer.data()); } // Specially for Astrometry.net -std::string toString(const std::tm &tm, const std::string &format) { +auto toString(const std::tm &tm, const std::string &format) -> std::string { std::ostringstream oss; oss << std::put_time(&tm, format.c_str()); return oss.str(); } -std::string getUtcTime() { - const auto now = std::chrono::system_clock::now(); - const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now); +auto getUtcTime() -> std::string { + const auto NOW = std::chrono::system_clock::now(); + const std::time_t NOW_TIME_T = std::chrono::system_clock::to_time_t(NOW); std::tm tm; #ifdef _WIN32 - gmtime_s(&tm, &now_time_t); + if (gmtime_s(&tm, &NOW_TIME_T) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert time to UTC"); + } #else gmtime_r(&now_time_t, &tm); #endif return toString(tm, "%FT%TZ"); } -std::tm timestampToTime(long long timestamp) { - std::time_t time = static_cast(timestamp / 1000); - return *std::localtime(&time); +auto timestampToTime(long long timestamp) -> std::tm { + auto time = static_cast(timestamp / K_MILLISECONDS_IN_SECOND); + + std::tm timeStruct; + if (localtime_s(&timeStruct, &time) != 0) { + THROW_TIME_CONVERT_ERROR("Failed to convert timestamp to local time"); + } // Use localtime_s for thread safety + + return timeStruct; } } // namespace atom::utils diff --git a/src/atom/utils/time.hpp b/src/atom/utils/time.hpp index 2ea99bdb..8473b342 100644 --- a/src/atom/utils/time.hpp +++ b/src/atom/utils/time.hpp @@ -18,7 +18,21 @@ Description: Some useful functions about time #include #include +#include "atom/error/exception.hpp" + namespace atom::utils { +class TimeConvertException : public atom::error::Exception { + using atom::error::Exception::Exception; +}; + +#define THROW_TIME_CONVERT_ERROR(...) \ + throw atom::utils::TimeConvertException(ATOM_FILE_NAME, ATOM_FILE_LINE, \ + ATOM_FUNC_NAME, __VA_ARGS__) + +#define THROW_NESTED_TIME_CONVERT_ERROR(...) \ + atom::utils::RuntimeError::TimeConvertException( \ + ATOM_FILE_NAME, ATOM_FILE_LINE, ATOM_FUNC_NAME, __VA_ARGS__) + /** * @brief Retrieves the current timestamp as a formatted string. * @@ -27,7 +41,7 @@ namespace atom::utils { * * @return std::string The current timestamp formatted as "%Y-%m-%d %H:%M:%S". */ -[[nodiscard]] std::string getTimestampString(); +[[nodiscard]] auto getTimestampString() -> std::string; /** * @brief Converts a UTC time string to China Standard Time (CST, UTC+8). @@ -42,7 +56,8 @@ namespace atom::utils { * @return std::string The corresponding time in China Standard Time, formatted * as "%Y-%m-%d %H:%M:%S". */ -[[nodiscard]] std::string convertToChinaTime(const std::string &utcTimeStr); +[[nodiscard]] auto convertToChinaTime(const std::string &utcTimeStr) + -> std::string; /** * @brief Retrieves the current China Standard Time (CST) as a formatted @@ -55,7 +70,7 @@ namespace atom::utils { * @return std::string The current China Standard Time formatted as "%Y-%m-%d * %H:%M:%S". */ -[[nodiscard]] std::string getChinaTimestampString(); +[[nodiscard]] auto getChinaTimestampString() -> std::string; /** * @brief Converts a timestamp to a formatted string. @@ -69,7 +84,7 @@ namespace atom::utils { * * @return std::string The string representation of the timestamp. */ -[[nodiscard]] std::string timeStampToString(time_t timestamp); +[[nodiscard]] auto timeStampToString(time_t timestamp) -> std::string; /** * @brief Converts a `tm` structure to a formatted string. @@ -83,8 +98,8 @@ namespace atom::utils { * @return std::string The formatted time string based on the `tm` structure and * format. */ -[[nodiscard]] std::string toString(const std::tm &tm, - const std::string &format); +[[nodiscard]] auto toString(const std::tm &tm, + const std::string &format) -> std::string; /** * @brief Retrieves the current UTC time as a formatted string. @@ -94,7 +109,7 @@ namespace atom::utils { * * @return std::string The current UTC time formatted as "%Y-%m-%d %H:%M:%S". */ -[[nodiscard]] std::string getUtcTime(); +[[nodiscard]] auto getUtcTime() -> std::string; /** * @brief Converts a timestamp to a `tm` structure. @@ -109,7 +124,7 @@ namespace atom::utils { * @return std::tm The corresponding `std::tm` structure representing the * timestamp. */ -[[nodiscard]] std::tm timestampToTime(long long timestamp); +[[nodiscard]] auto timestampToTime(long long timestamp) -> std::tm; } // namespace atom::utils #endif diff --git a/src/atom/utils/to_byte.hpp b/src/atom/utils/to_byte.hpp index f642b8cd..61338dac 100644 --- a/src/atom/utils/to_byte.hpp +++ b/src/atom/utils/to_byte.hpp @@ -14,6 +14,8 @@ #include #include +#include "atom/function/concept.hpp" + namespace atom::utils { /** @@ -24,9 +26,8 @@ namespace atom::utils { * function available. */ template -concept Serializable = - std::is_arithmetic_v || std::is_enum_v || - std::same_as || requires(T a) { serialize(a); }; +concept Serializable = Number || Enum || String || Char || + requires(const T& t) { serialize(t); }; /** * @brief Serializes a serializable type into a vector of bytes. @@ -42,7 +43,7 @@ concept Serializable = * data. */ template -std::vector serialize(const T& data) { +auto serialize(const T& data) -> std::vector { std::vector bytes(sizeof(T)); std::memcpy(bytes.data(), &data, sizeof(T)); return bytes; @@ -58,7 +59,7 @@ std::vector serialize(const T& data) { * @return std::vector A vector of bytes representing the serialized * string. */ -inline std::vector serialize(const std::string& str) { +inline auto serialize(const std::string& str) -> std::vector { std::vector bytes; size_t size = str.size(); bytes.resize(sizeof(size) + size); @@ -80,15 +81,15 @@ inline std::vector serialize(const std::string& str) { * vector. */ template -std::vector serialize(const std::vector& vec) { +auto serialize(const std::vector& vec) -> std::vector { std::vector bytes; size_t size = vec.size(); - auto size_bytes = serialize(size); - bytes.insert(bytes.end(), size_bytes.begin(), size_bytes.end()); + auto sizeBytes = serialize(size); + bytes.insert(bytes.end(), sizeBytes.begin(), sizeBytes.end()); for (const auto& item : vec) { - auto item_bytes = serialize(item); - bytes.insert(bytes.end(), item_bytes.begin(), item_bytes.end()); + auto itemBytes = serialize(item); + bytes.insert(bytes.end(), itemBytes.begin(), itemBytes.end()); } return bytes; } @@ -106,15 +107,15 @@ std::vector serialize(const std::vector& vec) { * list. */ template -std::vector serialize(const std::list& list) { +auto serialize(const std::list& list) -> std::vector { std::vector bytes; size_t size = list.size(); - auto size_bytes = serialize(size); - bytes.insert(bytes.end(), size_bytes.begin(), size_bytes.end()); + auto sizeBytes = serialize(size); + bytes.insert(bytes.end(), sizeBytes.begin(), sizeBytes.end()); for (const auto& item : list) { - auto item_bytes = serialize(item); - bytes.insert(bytes.end(), item_bytes.begin(), item_bytes.end()); + auto itemBytes = serialize(item); + bytes.insert(bytes.end(), itemBytes.begin(), itemBytes.end()); } return bytes; } @@ -134,17 +135,17 @@ std::vector serialize(const std::list& list) { * map. */ template -std::vector serialize(const std::map& map) { +auto serialize(const std::map& map) -> std::vector { std::vector bytes; size_t size = map.size(); - auto size_bytes = serialize(size); - bytes.insert(bytes.end(), size_bytes.begin(), size_bytes.end()); + auto sizeBytes = serialize(size); + bytes.insert(bytes.end(), sizeBytes.begin(), sizeBytes.end()); for (const auto& [key, value] : map) { - auto key_bytes = serialize(key); - auto value_bytes = serialize(value); - bytes.insert(bytes.end(), key_bytes.begin(), key_bytes.end()); - bytes.insert(bytes.end(), value_bytes.begin(), value_bytes.end()); + auto keyBytes = serialize(key); + auto valueBytes = serialize(value); + bytes.insert(bytes.end(), keyBytes.begin(), keyBytes.end()); + bytes.insert(bytes.end(), valueBytes.begin(), valueBytes.end()); } return bytes; } @@ -163,15 +164,15 @@ std::vector serialize(const std::map& map) { * optional. */ template -std::vector serialize(const std::optional& opt) { +auto serialize(const std::optional& opt) -> std::vector { std::vector bytes; - bool has_value = opt.has_value(); - auto has_value_bytes = serialize(has_value); - bytes.insert(bytes.end(), has_value_bytes.begin(), has_value_bytes.end()); + bool hasValue = opt.has_value(); + auto hasValueBytes = serialize(hasValue); + bytes.insert(bytes.end(), hasValueBytes.begin(), hasValueBytes.end()); - if (has_value) { - auto value_bytes = serialize(opt.value()); - bytes.insert(bytes.end(), value_bytes.begin(), value_bytes.end()); + if (hasValue) { + auto valueBytes = serialize(opt.value()); + bytes.insert(bytes.end(), valueBytes.begin(), valueBytes.end()); } return bytes; } @@ -189,16 +190,16 @@ std::vector serialize(const std::optional& opt) { * variant. */ template -std::vector serialize(const std::variant& var) { +auto serialize(const std::variant& var) -> std::vector { std::vector bytes; size_t index = var.index(); - auto index_bytes = serialize(index); - bytes.insert(bytes.end(), index_bytes.begin(), index_bytes.end()); + auto indexBytes = serialize(index); + bytes.insert(bytes.end(), indexBytes.begin(), indexBytes.end()); std::visit( [&bytes](const auto& value) { - auto value_bytes = serialize(value); - bytes.insert(bytes.end(), value_bytes.begin(), value_bytes.end()); + auto valueBytes = serialize(value); + bytes.insert(bytes.end(), valueBytes.begin(), valueBytes.end()); }, var); return bytes; @@ -219,7 +220,7 @@ std::vector serialize(const std::variant& var) { * type. */ template -T deserialize(const std::span& bytes, size_t& offset) { +auto deserialize(const std::span& bytes, size_t& offset) -> T { if (bytes.size() < offset + sizeof(T)) { throw std::runtime_error( "Invalid data: too short to contain the expected type."); @@ -241,9 +242,9 @@ T deserialize(const std::span& bytes, size_t& offset) { * @return std::string The deserialized string. * @throws std::runtime_error if the size of the string or the data is invalid. */ -std::string deserializeString(const std::span& bytes, - size_t& offset) { - size_t size = deserialize(bytes, offset); +inline auto deserializeString(const std::span& bytes, + size_t& offset) -> std::string { + auto size = deserialize(bytes, offset); if (bytes.size() < offset + size) { throw std::runtime_error("Invalid data: size mismatch."); } @@ -265,9 +266,9 @@ std::string deserializeString(const std::span& bytes, * @return std::vector The deserialized vector. */ template -std::vector deserializeVector(const std::span& bytes, - size_t& offset) { - size_t size = deserialize(bytes, offset); +auto deserializeVector(const std::span& bytes, + size_t& offset) -> std::vector { + auto size = deserialize(bytes, offset); std::vector vec; vec.reserve(size); @@ -291,9 +292,9 @@ std::vector deserializeVector(const std::span& bytes, * @return std::list The deserialized list. */ template -std::list deserializeList(const std::span& bytes, - size_t& offset) { - size_t size = deserialize(bytes, offset); +auto deserializeList(const std::span& bytes, + size_t& offset) -> std::list { + auto size = deserialize(bytes, offset); std::list list; for (size_t i = 0; i < size; ++i) { @@ -319,9 +320,9 @@ std::list deserializeList(const std::span& bytes, * @return std::map The deserialized map. */ template -std::map deserializeMap(const std::span& bytes, - size_t& offset) { - size_t size = deserialize(bytes, offset); +auto deserializeMap(const std::span& bytes, + size_t& offset) -> std::map { + auto size = deserialize(bytes, offset); std::map map; for (size_t i = 0; i < size; ++i) { @@ -348,10 +349,10 @@ std::map deserializeMap(const std::span& bytes, * @return std::optional The deserialized optional. */ template -std::optional deserializeOptional(const std::span& bytes, - size_t& offset) { - bool has_value = deserialize(bytes, offset); - if (has_value) { +auto deserializeOptional(const std::span& bytes, + size_t& offset) -> std::optional { + bool hasValue = deserialize(bytes, offset); + if (hasValue) { return deserialize(bytes, offset); } return std::nullopt; @@ -373,8 +374,8 @@ std::optional deserializeOptional(const std::span& bytes, * @return Variant The constructed variant. */ template -Variant constructVariant(const std::span& bytes, size_t& offset, - size_t index, std::index_sequence) { +auto constructVariant(const std::span& bytes, size_t& offset, + size_t index, std::index_sequence) -> Variant { Variant var; ( [&](auto I) { @@ -402,9 +403,9 @@ Variant constructVariant(const std::span& bytes, size_t& offset, * @throws std::runtime_error if the index of the variant is out of range. */ template -std::variant deserializeVariant(const std::span& bytes, - size_t& offset) { - size_t index = deserialize(bytes, offset); +auto deserializeVariant(const std::span& bytes, + size_t& offset) -> std::variant { + auto index = deserialize(bytes, offset); if (index >= sizeof...(Ts)) { throw std::runtime_error("Invalid data: variant index out of range."); } @@ -442,7 +443,7 @@ inline void saveToFile(const std::vector& data, * @return std::vector A vector of bytes representing the loaded data. * @throws std::runtime_error if the file cannot be opened for reading. */ -inline std::vector loadFromFile(const std::string& filename) { +inline auto loadFromFile(const std::string& filename) -> std::vector { std::ifstream file(filename, std::ios::binary); if (!file) { throw std::runtime_error("Could not open file for reading: " + diff --git a/src/atom/utils/to_string.hpp b/src/atom/utils/to_string.hpp index a87a8eb5..5ad6ba81 100644 --- a/src/atom/utils/to_string.hpp +++ b/src/atom/utils/to_string.hpp @@ -97,7 +97,7 @@ auto toString(const Enum& value) -> std::string { * @return A string representation of the pointer address or value. */ template -std::string toString(T ptr) { +auto toString(T ptr) -> std::string { if (ptr) { return "Pointer(" + toString(*ptr) + ")"; } @@ -111,7 +111,7 @@ std::string toString(T ptr) { * @return A string representation of the smart pointer. */ template -std::string toString(const SmartPtr& ptr) { +auto toString(const SmartPtr& ptr) -> std::string { if (ptr) { return "SmartPointer(" + toString(*ptr) + ")"; } diff --git a/src/atom/utils/utf.cpp b/src/atom/utils/utf.cpp index 206406fc..9251301d 100644 --- a/src/atom/utils/utf.cpp +++ b/src/atom/utils/utf.cpp @@ -13,15 +13,16 @@ namespace atom::utils { // platforms auto utF16toUtF8(std::u16string_view str) -> std::string { #if defined(_WIN32) || defined(_WIN64) - if (str.empty()) - return std::string(); - int size_needed = WideCharToMultiByte( + if (str.empty()) { + return {}; + } + int sizeNeeded = WideCharToMultiByte( CP_UTF8, 0, reinterpret_cast(str.data()), str.size(), nullptr, 0, nullptr, nullptr); - std::string result(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, - reinterpret_cast(str.data()), - str.size(), &result[0], size_needed, nullptr, nullptr); + std::string result(sizeNeeded, 0); + WideCharToMultiByte( + CP_UTF8, 0, reinterpret_cast(str.data()), str.size(), + result.data(), sizeNeeded, nullptr, nullptr); return result; #else std::string result; @@ -46,13 +47,14 @@ auto utF16toUtF8(std::u16string_view str) -> std::string { // platforms auto utF8toUtF16(std::string_view str) -> std::u16string { #if defined(_WIN32) || defined(_WIN64) - if (str.empty()) - return std::u16string(); - int size_needed = + if (str.empty()) { + return {}; + } + int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0); - std::u16string result(size_needed, 0); + std::u16string result(sizeNeeded, 0); MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), - reinterpret_cast(&result[0]), size_needed); + reinterpret_cast(result.data()), sizeNeeded); return result; #else std::u16string result; @@ -120,40 +122,46 @@ auto utF8toUtF32(std::string_view str) -> std::u32string { std::u32string result; size_t i = 0; while (i < str.size()) { - unsigned char c = static_cast(str[i++]); + auto c = static_cast(str[i++]); if (c <= 0x7F) { result.push_back(static_cast(c)); } else if (c <= 0xDF) { - if (i >= str.size()) + if (i >= str.size()) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: unexpected end of input"); - unsigned char c2 = static_cast(str[i++]); - if ((c2 & 0xC0) != 0x80) + } + auto c2 = static_cast(str[i++]); + if ((c2 & 0xC0) != 0x80) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: invalid continuation byte"); + } result.push_back(((c & 0x1F) << 6) | (c2 & 0x3F)); } else if (c <= 0xEF) { - if (i + 1 >= str.size()) + if (i + 1 >= str.size()) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: unexpected end of input"); - unsigned char c2 = static_cast(str[i++]); - unsigned char c3 = static_cast(str[i++]); - if ((c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80) + } + auto c2 = static_cast(str[i++]); + auto c3 = static_cast(str[i++]); + if ((c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: invalid continuation byte"); + } result.push_back(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); } else if (c <= 0xF7) { - if (i + 2 >= str.size()) + if (i + 2 >= str.size()) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: unexpected end of input"); - unsigned char c2 = static_cast(str[i++]); - unsigned char c3 = static_cast(str[i++]); - unsigned char c4 = static_cast(str[i++]); + } + auto c2 = static_cast(str[i++]); + auto c3 = static_cast(str[i++]); + auto c4 = static_cast(str[i++]); if ((c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80 || - (c4 & 0xC0) != 0x80) + (c4 & 0xC0) != 0x80) { THROW_INVALID_ARGUMENT( "Invalid UTF-8 string: invalid continuation byte"); + } result.push_back(((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F)); } else { @@ -175,15 +183,15 @@ auto surrogateToCodepoint(char16_t high, char16_t low) -> char32_t { // UTF-16 to UTF-32 conversion auto utF16toUtF32(std::u16string_view str) -> std::u32string { std::u32string result; - const auto *it = str.begin(); - while (it != str.end()) { - char16_t codeUnit = *it++; + auto iterator = str.begin(); // Renamed 'it' to 'iterator' and fixed type + while (iterator != str.end()) { + char16_t codeUnit = *iterator++; if (isHighSurrogate(codeUnit)) { - if (it == str.end() || !isLowSurrogate(*it)) { + if (iterator == str.end() || !isLowSurrogate(*iterator)) { THROW_INVALID_ARGUMENT( "Invalid UTF-16 string: incomplete surrogate pair"); } - char32_t codePoint = surrogateToCodepoint(codeUnit, *it++); + char32_t codePoint = surrogateToCodepoint(codeUnit, *iterator++); result.push_back(codePoint); } else if (isLowSurrogate(codeUnit)) { THROW_INVALID_ARGUMENT( diff --git a/src/atom/utils/uuid.cpp b/src/atom/utils/uuid.cpp index 570b6b70..4780b39b 100644 --- a/src/atom/utils/uuid.cpp +++ b/src/atom/utils/uuid.cpp @@ -21,6 +21,7 @@ Description: UUID Generator #include #include #include +#include #if defined(_WIN32) // clang-format off @@ -244,8 +245,8 @@ auto getCPUSerial() -> std::string { __cpuid(cpuInfo, 1); std::ostringstream oss; oss << std::hex << std::setfill('0'); - for (int i = 0; i < 4; i++) { - oss << std::setw(8) << cpuInfo[i]; + for (int i : cpuInfo) { + oss << std::setw(8) << i; } cpuSerial = oss.str(); diff --git a/src/atom/utils/xml.cpp b/src/atom/utils/xml.cpp index 117963e1..8e788ecc 100644 --- a/src/atom/utils/xml.cpp +++ b/src/atom/utils/xml.cpp @@ -14,114 +14,141 @@ Description: A XML reader class using tinyxml2. #include "xml.hpp" -#include +#include "atom/log/loguru.hpp" namespace atom::utils { XMLReader::XMLReader(const std::string &filePath) { + LOG_F(INFO, "Loading XML file: {}", filePath); if (doc_.LoadFile(filePath.c_str()) != tinyxml2::XML_SUCCESS) { + LOG_F(ERROR, "Failed to load XML file: {}", filePath); throw std::runtime_error("Failed to load XML file"); } + LOG_F(INFO, "Successfully loaded XML file: {}", filePath); } -std::vector XMLReader::getChildElementNames( - const std::string &parentElementName) const { +auto XMLReader::getChildElementNames(const std::string &parentElementName) const + -> std::vector { + LOG_F(INFO, "Getting child element names for parent: {}", + parentElementName); std::vector childElementNames; const tinyxml2::XMLElement *parentElement = doc_.FirstChildElement(parentElementName.c_str()); - if (parentElement) { + if (parentElement != nullptr) { for (const tinyxml2::XMLElement *element = parentElement->FirstChildElement(); element != nullptr; element = element->NextSiblingElement()) { - childElementNames.push_back(element->Name()); + childElementNames.emplace_back(element->Name()); } } + LOG_F(INFO, "Found {} child elements for parent: {}", + childElementNames.size(), parentElementName); return childElementNames; } -std::string XMLReader::getElementText(const std::string &elementName) const { +auto XMLReader::getElementText(const std::string &elementName) const + -> std::string { + LOG_F(INFO, "Getting text for element: {}", elementName); const tinyxml2::XMLElement *element = doc_.FirstChildElement(elementName.c_str()); - if (element) { + if (element != nullptr) { return element->GetText(); } return ""; } -std::string XMLReader::getAttributeValue( - const std::string &elementName, const std::string &attributeName) const { +auto XMLReader::getAttributeValue(const std::string &elementName, + const std::string &attributeName) const + -> std::string { + LOG_F(INFO, "Getting attribute value for element: {}, attribute: {}", + elementName, attributeName); const tinyxml2::XMLElement *element = doc_.FirstChildElement(elementName.c_str()); - if (element) { + if (element != nullptr) { return element->Attribute(attributeName.c_str()); } return ""; } -std::vector XMLReader::getRootElementNames() const { +auto XMLReader::getRootElementNames() const -> std::vector { + LOG_F(INFO, "Getting root element names"); std::vector rootElementNames; const tinyxml2::XMLElement *rootElement = doc_.RootElement(); - if (rootElement) { - rootElementNames.push_back(rootElement->Name()); + if (rootElement != nullptr) { + rootElementNames.emplace_back(rootElement->Name()); } + LOG_F(INFO, "Found {} root elements", rootElementNames.size()); return rootElementNames; } -bool XMLReader::hasChildElement(const std::string &parentElementName, - const std::string &childElementName) const { +auto XMLReader::hasChildElement(const std::string &parentElementName, + const std::string &childElementName) const + -> bool { + LOG_F(INFO, "Checking if parent element: {} has child element: {}", + parentElementName, childElementName); const tinyxml2::XMLElement *parentElement = doc_.FirstChildElement(parentElementName.c_str()); - if (parentElement) { + if (parentElement != nullptr) { return parentElement->FirstChildElement(childElementName.c_str()) != nullptr; } return false; } -std::string XMLReader::getChildElementText( - const std::string &parentElementName, - const std::string &childElementName) const { +auto XMLReader::getChildElementText(const std::string &parentElementName, + const std::string &childElementName) const + -> std::string { + LOG_F(INFO, "Getting text for child element: {} of parent element: {}", + childElementName, parentElementName); const tinyxml2::XMLElement *parentElement = doc_.FirstChildElement(parentElementName.c_str()); - if (parentElement) { + if (parentElement != nullptr) { const tinyxml2::XMLElement *childElement = parentElement->FirstChildElement(childElementName.c_str()); - if (childElement) { + if (childElement != nullptr) { return childElement->GetText(); } } return ""; } -std::string XMLReader::getChildElementAttributeValue( +auto XMLReader::getChildElementAttributeValue( const std::string &parentElementName, const std::string &childElementName, - const std::string &attributeName) const { + const std::string &attributeName) const -> std::string { + LOG_F(INFO, + "Getting attribute value for child element: {} of parent element: " + "{}, attribute: {}", + childElementName, parentElementName, attributeName); const tinyxml2::XMLElement *parentElement = doc_.FirstChildElement(parentElementName.c_str()); - if (parentElement) { + if (parentElement != nullptr) { const tinyxml2::XMLElement *childElement = parentElement->FirstChildElement(childElementName.c_str()); - if (childElement) { + if (childElement != nullptr) { return childElement->Attribute(attributeName.c_str()); } } return ""; } -std::string XMLReader::getValueByPath(const std::string &path) const { +auto XMLReader::getValueByPath(const std::string &path) const -> std::string { + LOG_F(INFO, "Getting value by path: {}", path); std::string value; tinyxml2::XMLElement *element = getElementByPath(path); - if (element) { + if (element != nullptr) { value = element->GetText(); } return value; } // 根据路径获取属性值 -std::string XMLReader::getAttributeValueByPath( - const std::string &path, const std::string &attributeName) const { +auto XMLReader::getAttributeValueByPath(const std::string &path, + const std::string &attributeName) const + -> std::string { + LOG_F(INFO, "Getting attribute value by path: {}, attribute: {}", path, + attributeName); std::string value; tinyxml2::XMLElement *element = getElementByPath(path); - if (element) { + if (element != nullptr) { value = element->Attribute(attributeName.c_str()); } return value; @@ -130,52 +157,63 @@ std::string XMLReader::getAttributeValueByPath( // 根据路径判断是否存在子元素 bool XMLReader::hasChildElementByPath( const std::string &path, const std::string &childElementName) const { + LOG_F(INFO, "Checking if path: {} has child element: {}", path, + childElementName); tinyxml2::XMLElement *element = getElementByPath(path); - if (element) { + if (element != nullptr) { return element->FirstChildElement(childElementName.c_str()) != nullptr; } return false; } // 根据路径获取子元素的文本 -std::string XMLReader::getChildElementTextByPath( - const std::string &path, const std::string &childElementName) const { +auto XMLReader::getChildElementTextByPath( + const std::string &path, + const std::string &childElementName) const -> std::string { + LOG_F(INFO, "Getting text for child element: {} by path: {}", + childElementName, path); std::string value; tinyxml2::XMLElement *element = getElementByPath(path); - if (element) { + if (element != nullptr) { tinyxml2::XMLElement *childElement = element->FirstChildElement(childElementName.c_str()); - if (childElement) { + if (childElement != nullptr) { value = childElement->GetText(); } } return value; } -std::string XMLReader::getChildElementAttributeValueByPath( +auto XMLReader::getChildElementAttributeValueByPath( const std::string &path, const std::string &childElementName, - const std::string &attributeName) const { + const std::string &attributeName) const -> std::string { + LOG_F(INFO, + "Getting attribute value for child element: {} by path: {}, " + "attribute: {}", + childElementName, path, attributeName); std::string value; tinyxml2::XMLElement *element = getElementByPath(path); - if (element) { + if (element != nullptr) { tinyxml2::XMLElement *childElement = element->FirstChildElement(childElementName.c_str()); - if (childElement) { + if (childElement != nullptr) { value = childElement->Attribute(attributeName.c_str()); } } return value; } -bool XMLReader::saveToFile(const std::string &filePath) const { +auto XMLReader::saveToFile(const std::string &filePath) const -> bool { + LOG_F(INFO, "Saving XML to file: {}", filePath); return doc_.SaveFile(filePath.c_str()) == tinyxml2::XML_SUCCESS; } tinyxml2::XMLElement *XMLReader::getElementByPath( const std::string &path) const { + LOG_F(INFO, "Getting element by path: {}", path); tinyxml2::XMLElement *element = doc_.RootElement(); size_t pos = 0; - while (element && pos != std::string::npos) { + while ((element != nullptr) && pos != std::string::npos) { size_t newPos = path.find('.', pos); std::string elementName = path.substr(pos, newPos - pos); element = element->FirstChildElement(elementName.c_str()); @@ -183,4 +221,4 @@ tinyxml2::XMLElement *XMLReader::getElementByPath( } return element; } -} // namespace atom::utils +} // namespace atom::utils \ No newline at end of file diff --git a/src/atom/utils/xml.hpp b/src/atom/utils/xml.hpp index 3b832068..bcb92318 100644 --- a/src/atom/utils/xml.hpp +++ b/src/atom/utils/xml.hpp @@ -39,8 +39,8 @@ class XMLReader { * @param parentElementName The name of the parent element. * @return A vector containing the names of all child elements. */ - std::vector getChildElementNames( - const std::string &parentElementName) const; + auto getChildElementNames(const std::string &parentElementName) const + -> std::vector; /** * @brief Returns the text value of the specified element. @@ -48,7 +48,7 @@ class XMLReader { * @param elementName The name of the element. * @return The text value of the element. */ - std::string getElementText(const std::string &elementName) const; + auto getElementText(const std::string &elementName) const -> std::string; /** * @brief Returns the value of the specified attribute of the specified @@ -58,15 +58,16 @@ class XMLReader { * @param attributeName The name of the attribute. * @return The value of the attribute. */ - std::string getAttributeValue(const std::string &elementName, - const std::string &attributeName) const; + auto getAttributeValue(const std::string &elementName, + const std::string &attributeName) const + -> std::string; /** * @brief Returns the names of all root elements in the XML file. * * @return A vector containing the names of all root elements. */ - std::vector getRootElementNames() const; + auto getRootElementNames() const -> std::vector; /** * @brief Checks if the specified parent element has a child element with @@ -76,8 +77,8 @@ class XMLReader { * @param childElementName The name of the child element. * @return true if the child element exists, false otherwise. */ - bool hasChildElement(const std::string &parentElementName, - const std::string &childElementName) const; + auto hasChildElement(const std::string &parentElementName, + const std::string &childElementName) const -> bool; /** * @brief Returns the text value of the specified child element of the @@ -87,8 +88,9 @@ class XMLReader { * @param childElementName The name of the child element. * @return The text value of the child element. */ - std::string getChildElementText(const std::string &parentElementName, - const std::string &childElementName) const; + auto getChildElementText(const std::string &parentElementName, + const std::string &childElementName) const + -> std::string; /** * @brief Returns the value of the specified attribute of the specified @@ -99,10 +101,10 @@ class XMLReader { * @param attributeName The name of the attribute. * @return The value of the attribute. */ - std::string getChildElementAttributeValue( - const std::string &parentElementName, - const std::string &childElementName, - const std::string &attributeName) const; + auto getChildElementAttributeValue(const std::string &parentElementName, + const std::string &childElementName, + const std::string &attributeName) const + -> std::string; /** * @brief Returns the text value of the element specified by a given path. @@ -111,7 +113,7 @@ class XMLReader { * @return The text value of the element. * @throw std::runtime_error if the element does not exist. */ - std::string getValueByPath(const std::string &path) const; + auto getValueByPath(const std::string &path) const -> std::string; /** * @brief Returns the value of the specified attribute of the element @@ -122,8 +124,9 @@ class XMLReader { * @return The value of the attribute. * @throw std::runtime_error if the element does not exist. */ - std::string getAttributeValueByPath(const std::string &path, - const std::string &attributeName) const; + auto getAttributeValueByPath(const std::string &path, + const std::string &attributeName) const + -> std::string; /** * @brief Checks if the element specified by a given path has a child @@ -134,8 +137,9 @@ class XMLReader { * @return true if the child element exists, false otherwise. * @throw std::runtime_error if the parent element does not exist. */ - bool hasChildElementByPath(const std::string &path, - const std::string &childElementName) const; + auto hasChildElementByPath(const std::string &path, + const std::string &childElementName) const + -> bool; /** * @brief Returns the text value of the child element with the specified @@ -146,8 +150,9 @@ class XMLReader { * @return The text value of the child element. * @throw std::runtime_error if the parent or child element does not exist. */ - std::string getChildElementTextByPath( - const std::string &path, const std::string &childElementName) const; + auto getChildElementTextByPath(const std::string &path, + const std::string &childElementName) const + -> std::string; /** * @brief Returns the value of the specified attribute of the child element @@ -159,9 +164,9 @@ class XMLReader { * @return The value of the attribute. * @throw std::runtime_error if the parent or child element does not exist. */ - std::string getChildElementAttributeValueByPath( + auto getChildElementAttributeValueByPath( const std::string &path, const std::string &childElementName, - const std::string &attributeName) const; + const std::string &attributeName) const -> std::string; /** * @brief Saves the XML document to the specified file. @@ -169,7 +174,7 @@ class XMLReader { * @param filePath The path to save the XML document to. * @return true if the document was saved successfully, false otherwise. */ - bool saveToFile(const std::string &filePath) const; + auto saveToFile(const std::string &filePath) const -> bool; private: mutable tinyxml2::XMLDocument doc_; @@ -181,7 +186,8 @@ class XMLReader { * @return A pointer to the element. * @throw std::runtime_error if the element does not exist. */ - tinyxml2::XMLElement *getElementByPath(const std::string &path) const; + auto getElementByPath(const std::string &path) const + -> tinyxml2::XMLElement *; }; } // namespace atom::utils diff --git a/src/target/engine.cpp b/src/target/engine.cpp index 813150a7..09d4dbb7 100644 --- a/src/target/engine.cpp +++ b/src/target/engine.cpp @@ -2,12 +2,10 @@ #define STAR_SEARCH_SEARCH_HPP #include -#include #include #include #include #include -#include #include #include #include @@ -31,18 +29,18 @@ class LRUCache { auto get(const Key& key) -> std::optional { std::lock_guard lock(cacheMutex_); - if (auto it = cacheMap_.find(key); it != cacheMap_.end()) { - cacheList_.splice(cacheList_.begin(), cacheList_, it->second); - return it->second->second; + if (auto iter = cacheMap_.find(key); iter != cacheMap_.end()) { + cacheList_.splice(cacheList_.begin(), cacheList_, iter->second); + return iter->second->second; } return std::nullopt; } void put(const Key& key, const Value& value) { std::lock_guard lock(cacheMutex_); - if (auto it = cacheMap_.find(key); it != cacheMap_.end()) { - cacheList_.splice(cacheList_.begin(), cacheList_, it->second); - it->second->second = value; + if (auto iter = cacheMap_.find(key); iter != cacheMap_.end()) { + cacheList_.splice(cacheList_.begin(), cacheList_, iter->second); + iter->second->second = value; return; } @@ -59,7 +57,7 @@ class LRUCache { // Trie树用于自动补全 class Trie { private: - struct TrieNode { + struct alignas(128) TrieNode { std::unordered_map children; bool isEndOfWord = false; }; @@ -70,26 +68,32 @@ class Trie { ~Trie() { clear(root_); } + Trie(const Trie&) = delete; + Trie& operator=(const Trie&) = delete; + + Trie(Trie&&) noexcept = default; + Trie& operator=(Trie&&) noexcept = default; + void insert(const std::string& word) { TrieNode* node = root_; - for (char c : word) { - if (!node->children.contains(c)) { - node->children[c] = new TrieNode(); + for (char ch : word) { + if (!node->children.contains(ch)) { + node->children[ch] = new TrieNode(); } - node = node->children[c]; + node = node->children[ch]; } node->isEndOfWord = true; } - auto autoComplete(const std::string& prefix) const + [[nodiscard]] auto autoComplete(const std::string& prefix) const -> std::vector { std::vector suggestions; TrieNode* node = root_; - for (char c : prefix) { - if (!node->children.contains(c)) { + for (char ch : prefix) { + if (!node->children.contains(ch)) { return suggestions; // 前缀不存在 } - node = node->children[c]; + node = node->children[ch]; } dfs(node, prefix, suggestions); return suggestions; @@ -101,8 +105,8 @@ class Trie { if (node->isEndOfWord) { suggestions.push_back(prefix); } - for (const auto& [c, childNode] : node->children) { - dfs(childNode, prefix + c, suggestions); + for (const auto& [ch, childNode] : node->children) { + dfs(childNode, prefix + ch, suggestions); } } @@ -114,14 +118,28 @@ class Trie { } }; -struct StarObject { - std::string name; - std::vector aliases; - int clickCount; // 用于调整权重 +struct alignas(64) StarObject { +private: + std::string name_; + std::vector aliases_; + int clickCount_; +public: StarObject(std::string name, std::initializer_list aliases, int clickCount = 0) - : name(std::move(name)), aliases(aliases), clickCount(clickCount) {} + : name_(std::move(name)), aliases_(aliases), clickCount_(clickCount) {} + + [[nodiscard]] auto getName() const -> const std::string& { return name_; } + [[nodiscard]] auto getAliases() const -> const std::vector& { + return aliases_; + } + [[nodiscard]] auto getClickCount() const -> int { return clickCount_; } + + void setName(const std::string& name) { name_ = name; } + void setAliases(const std::vector& aliases) { + aliases_ = aliases; + } + void setClickCount(int clickCount) { clickCount_ = clickCount; } }; class SearchEngine { @@ -130,23 +148,21 @@ class SearchEngine { Trie trie_; mutable LRUCache> queryCache_; mutable std::shared_mutex indexMutex_; + static constexpr int CACHE_CAPACITY = 10; public: - SearchEngine() : queryCache_(10) {} + SearchEngine() : queryCache_(CACHE_CAPACITY) {} - // 添加星体对象 void addStarObject(const StarObject& starObject) { std::unique_lock lock(indexMutex_); - starObjectIndex_.emplace(starObject.name, starObject); + starObjectIndex_.emplace(starObject.getName(), starObject); - // 将名称和别名添加到Trie树中,用于自动补全和快速搜索 - trie_.insert(starObject.name); - for (const auto& alias : starObject.aliases) { + trie_.insert(starObject.getName()); + for (const auto& alias : starObject.getAliases()) { trie_.insert(alias); } } - // 按名称或别名搜索 auto searchStarObject(const std::string& query) const -> std::vector { std::shared_lock lock(indexMutex_); @@ -159,7 +175,7 @@ class SearchEngine { const auto& [name, starObject] = pair; if (name == query || std::ranges::any_of( - starObject.aliases, + starObject.getAliases(), [&query](const auto& alias) { return alias == query; })) { results.push_back(starObject); } @@ -167,12 +183,10 @@ class SearchEngine { std::ranges::for_each(starObjectIndex_, searchFn); - // 缓存结果 queryCache_.put(query, results); return results; } - // 模糊搜索 auto fuzzySearchStarObject(const std::string& query, int tolerance) const -> std::vector { std::shared_lock lock(indexMutex_); @@ -183,7 +197,7 @@ class SearchEngine { if (levenshteinDistance(query, name) <= tolerance) { results.push_back(starObject); } else { - for (const auto& alias : starObject.aliases) { + for (const auto& alias : starObject.getAliases()) { if (levenshteinDistance(query, alias) <= tolerance) { results.push_back(starObject); break; @@ -197,18 +211,16 @@ class SearchEngine { return results; } - // 自动补全 auto autoCompleteStarObject(const std::string& prefix) const -> std::vector { auto suggestions = trie_.autoComplete(prefix); - // 进一步过滤建议,只返回与实际名称或别名相关的内容 std::vector filteredSuggestions; auto filterFn = [&](const auto& suggestion) { for (const auto& [name, starObject] : starObjectIndex_) { if (name == suggestion || - std::ranges::any_of(starObject.aliases, + std::ranges::any_of(starObject.getAliases(), [&suggestion](const auto& alias) { return alias == suggestion; })) { @@ -223,38 +235,40 @@ class SearchEngine { return filteredSuggestions; } - // 按点击量排序结果 - auto getRankedResults(std::vector& results) const + static auto getRankedResults(std::vector& results) -> std::vector { std::ranges::sort(results, std::ranges::greater{}, - &StarObject::clickCount); + &StarObject::getClickCount); return results; } private: - static auto levenshteinDistance(const std::string& s1, - const std::string& s2) -> int { - const auto size1 = s1.size(); - const auto size2 = s2.size(); - std::vector> dp(size1 + 1, - std::vector(size2 + 1)); - - for (size_t i = 0; i <= size1; i++) - dp[i][0] = static_cast(i); - for (size_t j = 0; j <= size2; j++) - dp[0][j] = static_cast(j); + static auto levenshteinDistance(const std::string& str1, + const std::string& str2) -> int { + const auto size1 = str1.size(); + const auto size2 = str2.size(); + std::vector> distanceMatrix( + size1 + 1, std::vector(size2 + 1)); + + for (size_t i = 0; i <= size1; i++) { + distanceMatrix[i][0] = static_cast(i); + } + for (size_t j = 0; j <= size2; j++) { + distanceMatrix[0][j] = static_cast(j); + } for (size_t i = 1; i <= size1; i++) { for (size_t j = 1; j <= size2; j++) { - const int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; - dp[i][j] = std::min({dp[i - 1][j] + 1, dp[i][j - 1] + 1, - dp[i - 1][j - 1] + cost}); + const int cost = (str1[i - 1] == str2[j - 1]) ? 0 : 1; + distanceMatrix[i][j] = std::min( + {distanceMatrix[i - 1][j] + 1, distanceMatrix[i][j - 1] + 1, + distanceMatrix[i - 1][j - 1] + cost}); } } - return dp[size1][size2]; + return distanceMatrix[size1][size2]; } }; } // namespace lithium::target -#endif +#endif \ No newline at end of file diff --git a/src/target/engine.hpp b/src/target/engine.hpp index 9c36d88d..1dea70db 100644 --- a/src/target/engine.hpp +++ b/src/target/engine.hpp @@ -1,13 +1,10 @@ #ifndef STAR_SEARCH_SEARCH_HPP #define STAR_SEARCH_SEARCH_HPP -#include -#include #include #include #include #include -#include #include #include #include @@ -60,9 +57,9 @@ class LRUCache { */ auto get(const Key& key) -> std::optional { std::lock_guard lock(cacheMutex_); - if (auto it = cacheMap_.find(key); it != cacheMap_.end()) { - cacheList_.splice(cacheList_.begin(), cacheList_, it->second); - return it->second->second; + if (auto iter = cacheMap_.find(key); iter != cacheMap_.end()) { + cacheList_.splice(cacheList_.begin(), cacheList_, iter->second); + return iter->second->second; } return std::nullopt; } @@ -79,9 +76,9 @@ class LRUCache { */ void put(const Key& key, const Value& value) { std::lock_guard lock(cacheMutex_); - if (auto it = cacheMap_.find(key); it != cacheMap_.end()) { - cacheList_.splice(cacheList_.begin(), cacheList_, it->second); - it->second->second = value; + if (auto iter = cacheMap_.find(key); iter != cacheMap_.end()) { + cacheList_.splice(cacheList_.begin(), cacheList_, iter->second); + iter->second->second = value; return; } @@ -102,10 +99,10 @@ class LRUCache { * useful for tasks like auto-completion. */ class Trie { - struct TrieNode { + struct alignas(128) TrieNode { std::unordered_map children; ///< Children nodes. bool isEndOfWord = false; ///< Flag indicating the end of a word. - } ATOM_ALIGNAS(64); + }; public: /** @@ -118,6 +115,14 @@ class Trie { */ ~Trie(); + // Deleted copy constructor and copy assignment operator + Trie(const Trie&) = delete; + Trie& operator=(const Trie&) = delete; + + // Defaulted move constructor and move assignment operator + Trie(Trie&&) noexcept = default; + Trie& operator=(Trie&&) noexcept = default; + /** * @brief Inserts a word into the Trie. * @@ -163,13 +168,15 @@ class Trie { * including their name, possible aliases, and a click count which can be used * to adjust search result rankings. */ -struct StarObject { - std::string name; ///< The name of the star object. +struct alignas(64) StarObject { +private: + std::string name_; ///< The name of the star object. std::vector - aliases; ///< A list of aliases for the star object. - int clickCount; ///< The number of times this object has been clicked, used - ///< for ranking. + aliases_; ///< A list of aliases for the star object. + int clickCount_; ///< The number of times this object has been clicked, + ///< used for ranking. +public: /** * @brief Constructs a StarObject with a name, aliases, and an optional * click count. @@ -179,8 +186,23 @@ struct StarObject { * @param clickCount The initial click count (default is 0). */ StarObject(std::string name, std::initializer_list aliases, - int clickCount = 0); -} ATOM_ALIGNAS(64); + int clickCount = 0) + : name_(std::move(name)), aliases_(aliases), clickCount_(clickCount) {} + + // Accessor methods + [[nodiscard]] auto getName() const -> const std::string& { return name_; } + [[nodiscard]] auto getAliases() const -> const std::vector& { + return aliases_; + } + [[nodiscard]] auto getClickCount() const -> int { return clickCount_; } + + // Mutator methods + void setName(const std::string& name) { name_ = name; } + void setAliases(const std::vector& aliases) { + aliases_ = aliases; + } + void setClickCount(int clickCount) { clickCount_ = clickCount; } +}; /** * @brief A search engine for star objects. @@ -267,14 +289,14 @@ class SearchEngine { * strings, defined as the minimum number of single-character edits required * to change one word into the other. * - * @param s1 The first string. - * @param s2 The second string. + * @param str1 The first string. + * @param str2 The second string. * @return int The Levenshtein distance between the two strings. */ - static auto levenshteinDistance(const std::string& s1, - const std::string& s2) -> int; + static auto levenshteinDistance(const std::string& str1, + const std::string& str2) -> int; }; } // namespace lithium::target -#endif +#endif \ No newline at end of file diff --git a/src/target/favorites.hpp b/src/target/favorites.hpp index 02017b14..2779a5d4 100644 --- a/src/target/favorites.hpp +++ b/src/target/favorites.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -17,18 +16,20 @@ namespace lithium::target { +using json = nlohmann::json; + template -concept Serializable = requires(T a) { - { std::cout << a }; // 输出支持 - { a == a }; // 可比较 +concept Serializable = requires(T item) { + { std::cout << item }; // 输出支持 + { item == item }; // 可比较 }; template class FavoritesManager { private: - std::vector favorites; - std::vector backup; - std::optional> undoBackup; + std::vector favorites_; + std::vector backup_; + std::optional> undoBackup_; public: void addFavorite(const T& item); @@ -38,14 +39,14 @@ class FavoritesManager { void saveToFile(const std::string& filename) const; void loadFromFile(const std::string& filename); void sortFavorites(); - bool findFavorite(const T& item) const; + [[nodiscard]] auto findFavorite(const T& item) const -> bool; void removeDuplicates(); - size_t countFavorites() const; + [[nodiscard]] auto countFavorites() const -> size_t; void backupFavorites(); void restoreFavorites(); void clearFavorites(); void undoLastOperation(); - std::optional mostFrequentFavorite() const; + [[nodiscard]] auto mostFrequentFavorite() const -> std::optional; // 新增功能 void batchAddFavorites(const std::vector& items); @@ -58,16 +59,16 @@ class FavoritesManager { template void FavoritesManager::addFavorite(const T& item) { createUndoBackup(); - favorites.push_back(item); + favorites_.push_back(item); LOG_F(INFO, "Added to favorites: {}", item); } template void FavoritesManager::removeFavorite(const T& item) { createUndoBackup(); - auto it = std::ranges::find(favorites, item); - if (it != favorites.end()) { - favorites.erase(it); + auto iter = std::ranges::find(favorites_, item); + if (iter != favorites_.end()) { + favorites_.erase(iter); LOG_F(INFO, "Removed from favorites: {}", item); } else { LOG_F(ERROR, "Item not found in favorites: {}", item); @@ -76,20 +77,20 @@ void FavoritesManager::removeFavorite(const T& item) { template void FavoritesManager::displayFavorites() const { - if (favorites.empty()) { + if (favorites_.empty()) { std::cout << "Favorites list is empty" << std::endl; return; } LOG_F(INFO, "Favorites list:"); - for (const auto& item : favorites) { + for (const auto& item : favorites_) { LOG_F(INFO, "- {}", item); } } template void FavoritesManager::displayFavoriteByIndex(size_t index) const { - if (index < favorites.size()) { - LOG_F(INFO, "Favorite at index {}: {}", index, favorites[index]); + if (index < favorites_.size()) { + LOG_F(INFO, "Favorite at index {}: {}", index, favorites_[index]); } else { THROW_OUT_OF_RANGE("Index out of range"); } @@ -97,10 +98,10 @@ void FavoritesManager::displayFavoriteByIndex(size_t index) const { template void FavoritesManager::saveToFile(const std::string& filename) const { - nlohmann::json j = favorites; + json jsonFavorites = favorites_; std::ofstream file(filename); if (file.is_open()) { - file << j.dump(4); // Pretty print with 4 spaces indent + file << jsonFavorites.dump(4); // Pretty print with 4 spaces indent LOG_F(INFO, "Favorites list saved to file: {}", filename); } else { THROW_FAIL_TO_OPEN_FILE("Unable to open file: {}", filename); @@ -111,10 +112,10 @@ template void FavoritesManager::loadFromFile(const std::string& filename) { std::ifstream file(filename); if (file.is_open()) { - nlohmann::json j; - file >> j; + json jsonFavorites; + file >> jsonFavorites; createUndoBackup(); - favorites = j.get>(); + favorites_ = jsonFavorites.get>(); LOG_F(INFO, "Favorites list loaded from file: {}", filename); } else { THROW_FAIL_TO_OPEN_FILE("Unable to open file: {}", filename); @@ -124,39 +125,39 @@ void FavoritesManager::loadFromFile(const std::string& filename) { template void FavoritesManager::sortFavorites() { createUndoBackup(); - std::ranges::sort(favorites); + std::ranges::sort(favorites_); LOG_F(INFO, "Favorites list sorted"); } template -bool FavoritesManager::findFavorite(const T& item) const { - return std::ranges::find(favorites, item) != favorites.end(); +auto FavoritesManager::findFavorite(const T& item) const -> bool { + return std::ranges::find(favorites_, item) != favorites_.end(); } template void FavoritesManager::removeDuplicates() { createUndoBackup(); - std::set uniqueFavorites(favorites.begin(), favorites.end()); - favorites.assign(uniqueFavorites.begin(), uniqueFavorites.end()); + std::set uniqueFavorites(favorites_.begin(), favorites_.end()); + favorites_.assign(uniqueFavorites.begin(), uniqueFavorites.end()); LOG_F(INFO, "Duplicates removed from favorites list"); } template -size_t FavoritesManager::countFavorites() const { - return favorites.size(); +auto FavoritesManager::countFavorites() const -> size_t { + return favorites_.size(); } template void FavoritesManager::backupFavorites() { - backup = favorites; + backup_ = favorites_; LOG_F(INFO, "Favorites list backed up"); } template void FavoritesManager::restoreFavorites() { - if (!backup.empty()) { + if (!backup_.empty()) { createUndoBackup(); - favorites = backup; + favorites_ = backup_; LOG_F(INFO, "Favorites list restored from backup"); } else { THROW_FAIL_TO_OPEN_FILE("No backup available"); @@ -166,15 +167,15 @@ void FavoritesManager::restoreFavorites() { template void FavoritesManager::clearFavorites() { createUndoBackup(); - favorites.clear(); + favorites_.clear(); LOG_F(INFO, "Favorites list cleared"); } template void FavoritesManager::undoLastOperation() { - if (undoBackup.has_value()) { - favorites = undoBackup.value(); - undoBackup.reset(); + if (undoBackup_.has_value()) { + favorites_ = undoBackup_.value(); + undoBackup_.reset(); LOG_F(INFO, "Last operation undone"); } else { THROW_FAIL_TO_OPEN_FILE("No operation to undo"); @@ -182,13 +183,13 @@ void FavoritesManager::undoLastOperation() { } template -std::optional FavoritesManager::mostFrequentFavorite() const { - if (favorites.empty()) { +auto FavoritesManager::mostFrequentFavorite() const -> std::optional { + if (favorites_.empty()) { return std::nullopt; } - std::map frequencyMap; - for (const auto& item : favorites) { + std::unordered_map frequencyMap; + for (const auto& item : favorites_) { frequencyMap[item]++; } @@ -202,13 +203,13 @@ std::optional FavoritesManager::mostFrequentFavorite() const { template void FavoritesManager::createUndoBackup() { - undoBackup = favorites; + undoBackup_ = favorites_; } template void FavoritesManager::batchAddFavorites(const std::vector& items) { createUndoBackup(); - favorites.insert(favorites.end(), items.begin(), items.end()); + favorites_.insert(favorites_.end(), items.begin(), items.end()); LOG_F(INFO, "Batch added favorites."); } @@ -217,7 +218,7 @@ void FavoritesManager::analyzeFavorites() const { LOG_F(INFO, "Analyzing favorites..."); std::unordered_map frequencyMap; - for (const auto& item : favorites) { + for (const auto& item : favorites_) { frequencyMap[item]++; } @@ -227,4 +228,4 @@ void FavoritesManager::analyzeFavorites() const { } } // namespace lithium::target -#endif // LITHIUM_TARGET_FAVORITES_HPP +#endif // LITHIUM_TARGET_FAVORITES_HPP \ No newline at end of file diff --git a/src/task/custom/autofocus/curve.cpp b/src/task/custom/autofocus/curve.cpp index 150268b6..c0f2fc7a 100644 --- a/src/task/custom/autofocus/curve.cpp +++ b/src/task/custom/autofocus/curve.cpp @@ -13,15 +13,15 @@ class FocusCurveFitter::Impl { public: std::vector data_; - int polynomial_degree_ = 2; - ModelType current_model_ = ModelType::POLYNOMIAL; + int polynomialDegree = 2; + ModelType currentModel = ModelType::POLYNOMIAL; void addDataPoint(double position, double sharpness) { data_.push_back({position, sharpness}); } - std::vector fitCurve() { - switch (current_model_) { + auto fitCurve() -> std::vector { + switch (currentModel) { case ModelType::POLYNOMIAL: return fitPolynomialCurve(); case ModelType::GAUSSIAN: @@ -32,63 +32,68 @@ class FocusCurveFitter::Impl { return {}; } - std::vector fitPolynomialCurve() { - int n = data_.size(); - int degree = polynomial_degree_; + auto fitPolynomialCurve() -> std::vector { + auto dataSize = static_cast(data_.size()); + int degree = polynomialDegree; - std::vector> X(n, std::vector(degree + 1)); - std::vector y(n); + std::vector> matrixX( + dataSize, std::vector(degree + 1)); + std::vector vectorY(dataSize); - for (int i = 0; i < n; ++i) { + for (int i = 0; i < dataSize; ++i) { for (int j = 0; j <= degree; ++j) { - X[i][j] = std::pow(data_[i].position, j); + matrixX[i][j] = std::pow(data_[i].position, j); } - y[i] = data_[i].sharpness; + vectorY[i] = data_[i].sharpness; } - auto xt = transpose(X); - auto xtX = matrixMultiply(xt, X); - auto xty = matrixVectorMultiply(xt, y); - return solveLinearSystem(xtX, xty); + auto matrixXt = transpose(matrixX); + auto matrixXtX = matrixMultiply(matrixXt, matrixX); + auto vectorXty = matrixVectorMultiply(matrixXt, vectorY); + return solveLinearSystem(matrixXtX, vectorXty); } - std::vector fitGaussianCurve() { - auto [min_it, max_it] = - std::minmax_element(data_.begin(), data_.end(), - [](const DataPoint& a, const DataPoint& b) { - return a.sharpness < b.sharpness; - }); + auto fitGaussianCurve() -> std::vector { + auto [min_it, max_it] = std::minmax_element( + data_.begin(), data_.end(), + [](const DataPoint& point_a, const DataPoint& point_b) { + return point_a.sharpness < point_b.sharpness; + }); std::vector initialGuess = { max_it->sharpness - min_it->sharpness, max_it->position, 1.0, min_it->sharpness}; return levenbergMarquardt( - initialGuess, [](double x, const std::vector& params) { - double A = params[0], mu = params[1], sigma = params[2], - C = params[3]; - return A * std::exp(-std::pow(x - mu, 2) / - (2 * std::pow(sigma, 2))) + - C; + initialGuess, + [](double position, const std::vector& params) { + double amplitude = params[0], mean = params[1], + std_dev = params[2], offset = params[3]; + return amplitude * std::exp(-std::pow(position - mean, 2) / + (2 * std::pow(std_dev, 2))) + + offset; }); } - std::vector fitLorentzianCurve() { - auto [min_it, max_it] = - std::minmax_element(data_.begin(), data_.end(), - [](const DataPoint& a, const DataPoint& b) { - return a.sharpness < b.sharpness; - }); + auto fitLorentzianCurve() -> std::vector { + auto [min_it, max_it] = std::minmax_element( + data_.begin(), data_.end(), + [](const DataPoint& point_a, const DataPoint& point_b) { + return point_a.sharpness < point_b.sharpness; + }); std::vector initialGuess = { max_it->sharpness - min_it->sharpness, max_it->position, 1.0, min_it->sharpness}; return levenbergMarquardt( - initialGuess, [](double x, const std::vector& params) { - double A = params[0], x0 = params[1], gamma = params[2], - C = params[3]; - return A / (1 + std::pow((x - x0) / gamma, 2)) + C; + initialGuess, + [](double position, const std::vector& params) { + double amplitude = params[0], center = params[1], + width = params[2], offset = params[3]; + return amplitude / + (1 + std::pow((position - center) / width, 2)) + + offset; }); } @@ -99,7 +104,7 @@ class FocusCurveFitter::Impl { ModelType bestModel = ModelType::POLYNOMIAL; for (const auto& model : models) { - current_model_ = model; + currentModel = model; auto coeffs = fitCurve(); double aic = calculateAIC(coeffs); if (aic < bestAic) { @@ -108,22 +113,23 @@ class FocusCurveFitter::Impl { } } - current_model_ = bestModel; - LOG_F(INFO, "Selected model: {}", getModelName(current_model_)); + currentModel = bestModel; + LOG_F(INFO, "Selected model: {}", getModelName(currentModel)); } - std::vector> calculateConfidenceIntervals( - double confidence_level = 0.95) { + auto calculateConfidenceIntervals(double confidence_level = 0.95) + -> std::vector> { auto coeffs = fitCurve(); - int n = data_.size(); - int p = coeffs.size(); - double tValue = calculateTValue(n - p, confidence_level); + auto dataSize = static_cast(data_.size()); + auto coeffsSize = static_cast(coeffs.size()); + double tValue = + calculateTValue(dataSize - coeffsSize, confidence_level); std::vector> intervals; - for (int i = 0; i < p; ++i) { - double se = calculateStandardError(coeffs, i); - intervals.emplace_back(coeffs[i] - tValue * se, - coeffs[i] + tValue * se); + for (int i = 0; i < coeffsSize; ++i) { + double stdError = calculateStandardError(coeffs, i); + intervals.emplace_back(coeffs[i] - tValue * stdError, + coeffs[i] + tValue * stdError); } return intervals; } @@ -146,8 +152,10 @@ class FocusCurveFitter::Impl { auto coeffs = fitCurve(); double minPos = data_.front().position; double maxPos = data_.back().position; - for (double pos = minPos; pos <= maxPos; - pos += (maxPos - minPos) / 100) { + int steps = 100; + double stepSize = (maxPos - minPos) / steps; + for (int i = 0; i <= steps; ++i) { + double pos = minPos + i * stepSize; gnuplotScript << pos << " " << evaluateCurve(coeffs, pos) << "\n"; } gnuplotScript << "e\n"; @@ -164,15 +172,16 @@ class FocusCurveFitter::Impl { void preprocessData() { std::sort(data_.begin(), data_.end(), - [](const DataPoint& a, const DataPoint& b) { - return a.position < b.position; + [](const DataPoint& point_a, const DataPoint& point_b) { + return point_a.position < point_b.position; }); - data_.erase(std::unique(data_.begin(), data_.end(), - [](const DataPoint& a, const DataPoint& b) { - return a.position == b.position; - }), - data_.end()); + data_.erase( + std::unique(data_.begin(), data_.end(), + [](const DataPoint& point_a, const DataPoint& point_b) { + return point_a.position == point_b.position; + }), + data_.end()); double minPos = data_.front().position; double maxPos = data_.back().position; @@ -203,6 +212,7 @@ class FocusCurveFitter::Impl { void parallelFitting() { int numThreads = std::thread::hardware_concurrency(); std::vector>> futures; + futures.reserve(numThreads); for (int i = 0; i < numThreads; ++i) { futures.push_back(std::async(std::launch::async, @@ -216,11 +226,11 @@ class FocusCurveFitter::Impl { } // Choose the best fit based on MSE - auto bestFit = - *std::min_element(results.begin(), results.end(), - [this](const auto& a, const auto& b) { - return calculateMSE(a) < calculateMSE(b); - }); + auto bestFit = *std::min_element( + results.begin(), results.end(), + [this](const auto& coeffs_a, const auto& coeffs_b) { + return calculateMSE(coeffs_a) < calculateMSE(coeffs_b); + }); LOG_F(INFO, "Best parallel fit MSE: {}", calculateMSE(bestFit)); } @@ -228,38 +238,39 @@ class FocusCurveFitter::Impl { private: // Helper functions - static auto matrixVectorMultiply(const std::vector>& A, - const std::vector& v) - -> std::vector { - int m = A.size(); - int n = A[0].size(); - std::vector result(m, 0.0); + static auto matrixVectorMultiply( + const std::vector>& matrix_A, + const std::vector& vector_v) -> std::vector { + auto matrixARows = static_cast(matrix_A.size()); + auto matrixACols = static_cast(matrix_A[0].size()); + std::vector result(matrixARows, 0.0); - for (int i = 0; i < m; ++i) { - for (int j = 0; j < n; ++j) { - result[i] += A[i][j] * v[j]; + for (int i = 0; i < matrixARows; ++i) { + for (int j = 0; j < matrixACols; ++j) { + result[i] += matrix_A[i][j] * vector_v[j]; } } return result; } - static auto matrixMultiply(const std::vector>& A, - const std::vector>& B) + static auto matrixMultiply(const std::vector>& matrix_A, + const std::vector>& matrix_B) -> std::vector> { - int m = A.size(); - int n = A[0].size(); - int p = B[0].size(); + auto matrixARows = static_cast(matrix_A.size()); + auto matrixACols = static_cast(matrix_A[0].size()); + auto matrixBCols = static_cast(matrix_B[0].size()); - std::vector> C(m, std::vector(p, 0.0)); + std::vector> matrixC( + matrixARows, std::vector(matrixBCols, 0.0)); - for (int i = 0; i < m; ++i) { - for (int j = 0; j < p; ++j) { - for (int k = 0; k < n; ++k) { - C[i][j] += A[i][k] * B[k][j]; + for (int i = 0; i < matrixARows; ++i) { + for (int j = 0; j < matrixBCols; ++j) { + for (int k = 0; k < matrixACols; ++k) { + matrixC[i][j] += matrix_A[i][k] * matrix_B[k][j]; } } } - return C; + return matrixC; } template @@ -270,37 +281,39 @@ class FocusCurveFitter::Impl { double lambda = 0.001; std::vector params = initial_guess; - int n = data_.size(); - int p = initial_guess.size(); + auto dataSize = static_cast(data_.size()); + auto paramsSize = static_cast(initial_guess.size()); for (int iter = 0; iter < MAX_ITERATIONS; ++iter) { - std::vector> J( - n, std::vector(p)); // Jacobian matrix - std::vector residuals(n); - - for (int i = 0; i < n; ++i) { - double x = data_[i].position; - double y = data_[i].sharpness; - double modelValue = model(x, params); - residuals[i] = y - modelValue; - - for (int j = 0; j < p; ++j) { + std::vector> jacobianMatrix( + dataSize, + std::vector(paramsSize)); // Jacobian matrix + std::vector residuals(dataSize); + + for (int i = 0; i < dataSize; ++i) { + double position = data_[i].position; + double sharpness = data_[i].sharpness; + double modelValue = model(position, params); + residuals[i] = sharpness - modelValue; + + for (int j = 0; j < paramsSize; ++j) { std::vector paramsDelta = params; paramsDelta[j] += TOLERANCE; - double modelDelta = model(x, paramsDelta); - J[i][j] = (modelDelta - modelValue) / TOLERANCE; + double modelDelta = model(position, paramsDelta); + jacobianMatrix[i][j] = + (modelDelta - modelValue) / TOLERANCE; } } - auto jt = transpose(J); - auto jtJ = matrixMultiply(jt, J); - for (int i = 0; i < p; ++i) { + auto jacobianTranspose = transpose(jacobianMatrix); + auto jtJ = matrixMultiply(jacobianTranspose, jacobianMatrix); + for (int i = 0; i < paramsSize; ++i) { jtJ[i][i] += lambda; } - auto jtr = matrixVectorMultiply(jt, residuals); - auto deltaParams = solveLinearSystem(jtJ, jtr); + auto jtR = matrixVectorMultiply(jacobianTranspose, residuals); + auto deltaParams = solveLinearSystem(jtJ, jtR); - for (int i = 0; i < p; ++i) { + for (int i = 0; i < paramsSize; ++i) { params[i] += deltaParams[i]; } @@ -314,10 +327,10 @@ class FocusCurveFitter::Impl { } auto calculateAIC(const std::vector& coeffs) -> double { - int n = data_.size(); - int p = coeffs.size(); + auto dataSize = static_cast(data_.size()); + auto coeffsSize = static_cast(coeffs.size()); double mse = calculateMSE(coeffs); - double aic = n * std::log(mse) + 2 * p; + double aic = dataSize * std::log(mse) + 2 * coeffsSize; return aic; } @@ -335,17 +348,18 @@ class FocusCurveFitter::Impl { return std::sqrt(mse); } - static auto transpose(const std::vector>& A) + static auto transpose(const std::vector>& matrix_A) -> std::vector> { - int m = A.size(); - int n = A[0].size(); - std::vector> At(n, std::vector(m)); - for (int i = 0; i < m; ++i) { - for (int j = 0; j < n; ++j) { - At[j][i] = A[i][j]; + auto matrixARows = static_cast(matrix_A.size()); + auto matrixACols = static_cast(matrix_A[0].size()); + std::vector> matrixAt( + matrixACols, std::vector(matrixARows)); + for (int i = 0; i < matrixARows; ++i) { + for (int j = 0; j < matrixACols; ++j) { + matrixAt[j][i] = matrix_A[i][j]; } } - return At; + return matrixAt; } auto calculateMSE(const std::vector& coeffs) -> double { @@ -354,7 +368,7 @@ class FocusCurveFitter::Impl { double predicted = evaluateCurve(coeffs, point.position); mse += std::pow(predicted - point.sharpness, 2); } - return mse / data_.size(); + return mse / static_cast(data_.size()); } static auto solveLinearSystem(std::vector> A, @@ -392,7 +406,7 @@ class FocusCurveFitter::Impl { } auto evaluateCurve(const std::vector& coeffs, double x) -> double { - switch (current_model_) { + switch (currentModel) { case ModelType::POLYNOMIAL: return evaluatePolynomial(coeffs, x); case ModelType::GAUSSIAN: diff --git a/src/task/custom/autofocus/curve.hpp b/src/task/custom/autofocus/curve.hpp index 6ddc6ee0..7c50c708 100644 --- a/src/task/custom/autofocus/curve.hpp +++ b/src/task/custom/autofocus/curve.hpp @@ -4,13 +4,14 @@ #include #include #include +#include "macro.hpp" enum class ModelType { POLYNOMIAL, GAUSSIAN, LORENTZIAN }; struct DataPoint { double position; double sharpness; -}; +} ATOM_ALIGNAS(16); class FocusCurveFitter { public: @@ -18,10 +19,10 @@ class FocusCurveFitter { ~FocusCurveFitter(); void addDataPoint(double position, double sharpness); - std::vector fitCurve(); + auto fitCurve() -> std::vector; void autoSelectModel(); - std::vector> calculateConfidenceIntervals( - double confidence_level = 0.95); + auto calculateConfidenceIntervals(double confidence_level = 0.95) + -> std::vector>; void visualize(const std::string& filename = "focus_curve.png"); void preprocessData(); void realTimeFitAndPredict(double new_position); diff --git a/src/task/custom/camera/take_many_exposure.cpp b/src/task/custom/camera/take_many_exposure.cpp index 69850780..56c17bf9 100644 --- a/src/task/custom/camera/take_many_exposure.cpp +++ b/src/task/custom/camera/take_many_exposure.cpp @@ -65,8 +65,8 @@ TaskScheduler::Task TakeManyExposure::validateExposure() { if (impl_->gain < min_gain_ || impl_->gain > max_gain_) { LOG_F(ERROR, "Invalid gain: {}", impl_->gain); impl_->gain = default_gain_; - //THROW_INVALID_ARGUMENT("Exposure failed due to invalid gain: ", - // impl_->gain); + // THROW_INVALID_ARGUMENT("Exposure failed due to invalid gain: ", + // impl_->gain); co_return std::format("Exposure failed due to invalid gain: {}", impl_->gain); } @@ -79,82 +79,84 @@ TaskScheduler::Task TakeManyExposure::validateExposure() { TaskScheduler::Task TakeManyExposure::takeExposure() { try { LOG_F(INFO, "Taking exposure for camera {} with {} seconds.", - camera_name_, exposure_time_); + impl_->camera_name_, impl_->exposure_time_); std::this_thread::sleep_for( - std::chrono::duration(exposure_time_)); + std::chrono::duration(impl_->exposure_time_)); - std::string result = "Exposure result for camera " + camera_name_ + - " with " + std::to_string(exposure_time_) + - " seconds."; + std::string result = + "Exposure result for camera " + impl_->camera_name_ + " with " + + std::to_string(impl_->exposure_time_) + " seconds."; co_yield "Exposure completed: " + result; co_return result; } catch (const std::exception& e) { - THROW_UNLAWFUL_OPERATION("Exposure failed for camera " + camera_name_ + - ": " + e.what()); + THROW_UNLAWFUL_OPERATION("Exposure failed for camera " + + impl_->camera_name_ + ": " + e.what()); } } TaskScheduler::Task TakeManyExposure::handleExposureError() { - int exposure_time = exposure_time_; - - GET_CONFIG_VALUE(config_manager_, "/camera/retry_attempts", int, + GET_CONFIG_VALUE(impl_->config_manager_, "/camera/retry_attempts", int, retryAttempts); - GET_CONFIG_VALUE(config_manager_, "/camera/retry_delay", int, retryDelay); - GET_CONFIG_VALUE(config_manager_, "/camera/quality_threshold", double, - qualityThreshold); + GET_CONFIG_VALUE(impl_->config_manager_, "/camera/retry_delay", int, + retryDelay); + GET_CONFIG_VALUE(impl_->config_manager_, "/camera/quality_threshold", + double, qualityThreshold); for (int i = 0; i < retryAttempts; ++i) { try { - co_yield "Attempting exposure for camera " + camera_name_ + + co_yield "Attempting exposure for camera " + impl_->camera_name_ + " after " + std::to_string(i + 1) + " retry(ies)."; - auto exposure_task = takeExposure(); - co_await exposure_task; + auto exposureTask = takeExposure(); + co_await exposureTask; - if (auto result = task_scheduler_->getResult(exposure_task)) { + if (auto result = impl_->task_scheduler_->getResult(exposureTask)) { double quality = evaluateExposureQuality(*result); - LOG_F(INFO, "Exposure quality for camera {}: {}", camera_name_, - quality); + LOG_F(INFO, "Exposure quality for camera {}: {}", + impl_->camera_name_, quality); if (quality >= qualityThreshold) { co_return *result; } else { - exposure_time = adjustExposureTime(exposure_time, quality); + impl_->exposure_time_ = + adjustExposureTime(impl_->exposure_time_, quality); LOG_F(INFO, "Adjusted exposure time for camera {}: {}", - camera_name_, exposure_time); + impl_->camera_name_, impl_->exposure_time_); } } else { LOG_F(ERROR, "Exposure task completed but no result produced for {}", - camera_name_); + impl_->camera_name_); THROW_UNLAWFUL_OPERATION( "Exposure task completed but no result produced for " "camera " + - camera_name_); + impl_->camera_name_); } } catch (const atom::error::UnlawfulOperation& e) { LOG_F(ERROR, "Exposure attempt {} failed for camera {}: {}", i + 1, - camera_name_, e.what()); + impl_->camera_name_, e.what()); } } co_return std::format("Exposure failed for camera {} after {} retries.", - camera_name_, retryAttempts); + impl_->camera_name_, retryAttempts); } TaskScheduler::Task TakeManyExposure::run() { - auto validate_task = + auto validateTask = std::make_shared(validateExposure()); - task_scheduler_->schedule("validate_exposure_" + camera_name_, - validate_task); + impl_->task_scheduler_->schedule("validate_exposure_" + impl_->camera_name_, + validateTask); - auto exposure_task = + auto exposureTask = std::make_shared(handleExposureError()); - exposure_task->dependencies.push_back("validate_exposure_" + camera_name_); - task_scheduler_->schedule("exposure_task_" + camera_name_, exposure_task); + exposureTask->dependencies.push_back("validate_exposure_" + + impl_->camera_name_); + impl_->task_scheduler_->schedule("exposure_task_" + impl_->camera_name_, + exposureTask); - co_await *exposure_task; + co_await *exposureTask; - co_return "Exposure sequence completed for camera " + camera_name_; + co_return "Exposure sequence completed for camera " + impl_->camera_name_; } double TakeManyExposure::evaluateExposureQuality( diff --git a/src/task/custom/cotask.cpp b/src/task/custom/cotask.cpp index 10d0325d..59b4b41b 100644 --- a/src/task/custom/cotask.cpp +++ b/src/task/custom/cotask.cpp @@ -1,4 +1,6 @@ #include "cotask.hpp" + +#include #include #include "atom/log/loguru.hpp" diff --git a/src/task/simple/sequencer.cpp b/src/task/simple/sequencer.cpp index 6ec9a8a8..baec62ed 100644 --- a/src/task/simple/sequencer.cpp +++ b/src/task/simple/sequencer.cpp @@ -1,7 +1,8 @@ #include "sequencer.hpp" #include #include -#include + +#include "atom/type/json.hpp" namespace lithium::sequencer { diff --git a/src/tools/croods.cpp b/src/tools/croods.cpp index 6f8ffab5..684db3f4 100644 --- a/src/tools/croods.cpp +++ b/src/tools/croods.cpp @@ -4,6 +4,10 @@ #include #include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + namespace lithium::tools { auto rangeTo(double value, double max, double min) -> double { diff --git a/src/tools/croods.hpp b/src/tools/croods.hpp index 85caa28c..98f08351 100644 --- a/src/tools/croods.hpp +++ b/src/tools/croods.hpp @@ -2,7 +2,9 @@ #define LITHIUM_SEARCH_CROODS_HPP #include +#include #include +#include #include #include #include @@ -77,213 +79,240 @@ constexpr double SOLARMASS = 1.98847e30; constexpr double SOLARRADIUS = 6.957e8; constexpr double PARSEC = 3.0857e16; -template -constexpr T LUMEN(T wavelength) { - return 1.464128843e-3 / (wavelength * wavelength); +constexpr auto lumen(double wavelength) -> double { + constexpr double MAGIC_NUMBER = 1.464128843e-3; + return MAGIC_NUMBER / (wavelength * wavelength); } -template -constexpr T REDSHIFT(T observed, T rest) { +constexpr auto redshift(double observed, double rest) -> double { return (observed - rest) / rest; } -template -constexpr T DOPPLER(T redshift, T speed) { +constexpr auto doppler(double redshift, double speed) -> double { return redshift * speed; } -template -T rangeHA(T r) { - while (r < -12.0) - r += 24.0; - while (r >= 12.0) - r -= 24.0; - return r; +template +auto rangeHA(T range) -> T { + constexpr double MAGIC_NUMBER24 = 24.0; + constexpr double MAGIC_NUMBER12 = 12.0; + + if (range < -MAGIC_NUMBER12) { + range += MAGIC_NUMBER24; + } + while (range >= MAGIC_NUMBER12) { + range -= MAGIC_NUMBER24; + } + return range; } -template -T range24(T r) { - while (r < 0.0) - r += 24.0; - while (r > 24.0) - r -= 24.0; - return r; +template +auto range24(T range) -> T { + constexpr double MAGIC_NUMBER24 = 24.0; + + if (range < 0.0) { + range += MAGIC_NUMBER24; + } + while (range > MAGIC_NUMBER24) { + range -= MAGIC_NUMBER24; + } + return range; } -template -T range360(T r) { - while (r < 0.0) - r += 360.0; - while (r > 360.0) - r -= 360.0; - return r; +template +auto range360(T range) -> T { + constexpr double MAGIC_NUMBER360 = 360.0; + + if (range < 0.0) { + range += MAGIC_NUMBER360; + } + while (range > MAGIC_NUMBER360) { + range -= MAGIC_NUMBER360; + } + return range; } -template -T rangeDec(T decdegrees) { - if ((decdegrees >= 270.0) && (decdegrees <= 360.0)) - return (decdegrees - 360.0); - if ((decdegrees >= 180.0) && (decdegrees < 270.0)) - return (180.0 - decdegrees); - if ((decdegrees >= 90.0) && (decdegrees < 180.0)) - return (180.0 - decdegrees); - return decdegrees; +template +auto rangeDec(T decDegrees) -> T { + constexpr double MAGIC_NUMBER360 = 360.0; + constexpr double MAGIC_NUMBER180 = 180.0; + constexpr double MAGIC_NUMBER90 = 90.0; + constexpr double MAGIC_NUMBER270 = 270.0; + + if (decDegrees >= MAGIC_NUMBER270 && decDegrees <= MAGIC_NUMBER360) { + return decDegrees - MAGIC_NUMBER360; + } + if (decDegrees >= MAGIC_NUMBER180 && decDegrees < MAGIC_NUMBER270) { + return MAGIC_NUMBER180 - decDegrees; + } + if (decDegrees >= MAGIC_NUMBER90 && decDegrees < MAGIC_NUMBER180) { + return MAGIC_NUMBER180 - decDegrees; + } + return decDegrees; } -template -T get_local_hour_angle(T sideral_time, T ra) { - T HA = sideral_time - ra; - return rangeHA(HA); +template +auto getLocalHourAngle(T siderealTime, T rightAscension) -> T { + T hourAngle = siderealTime - rightAscension; + return range24(hourAngle); } -template -std::pair get_alt_az_coordinates(T Ha, T Dec, T Lat) { +template +auto getAltAzCoordinates(T hourAngle, T declination, + T latitude) -> std::pair { using namespace std::numbers; - Ha *= pi_v / 180.0; - Dec *= pi_v / 180.0; - Lat *= pi_v / 180.0; - T alt = std::asin(std::sin(Dec) * std::sin(Lat) + - std::cos(Dec) * std::cos(Lat) * std::cos(Ha)); - T az = std::acos((std::sin(Dec) - std::sin(alt) * std::sin(Lat)) / - (std::cos(alt) * std::cos(Lat))); - alt *= 180.0 / pi_v; - az *= 180.0 / pi_v; - if (std::sin(Ha) >= 0.0) - az = 360 - az; - return {alt, az}; + hourAngle *= pi_v / 180.0; + declination *= pi_v / 180.0; + latitude *= pi_v / 180.0; + + T altitude = std::asin(std::sin(declination) * std::sin(latitude) + + std::cos(declination) * std::cos(latitude) * + std::cos(hourAngle)); + T azimuth = std::acos( + (std::sin(declination) - std::sin(altitude) * std::sin(latitude)) / + (std::cos(altitude) * std::cos(latitude))); + + altitude *= 180.0 / pi_v; + azimuth *= 180.0 / pi_v; + + if (hourAngle > 0) { + azimuth = 360 - azimuth; + } + + return {altitude, azimuth}; } -template -T estimate_geocentric_elevation(T Lat, T El) { +template +auto estimateGeocentricElevation(T latitude, T elevation) -> T { using namespace std::numbers; - Lat *= pi_v / 180.0; - Lat = std::sin(Lat); - El += Lat * (EARTHRADIUSPOLAR - EARTHRADIUSEQUATORIAL); - return El; + latitude *= pi_v / 180.0; + return elevation - (elevation * std::cos(latitude)); } -template -T estimate_field_rotation_rate(T Alt, T Az, T Lat) { +template +auto estimateFieldRotationRate(T altitude, T azimuth, T latitude) -> T { using namespace std::numbers; - Alt *= pi_v / 180.0; - Az *= pi_v / 180.0; - Lat *= pi_v / 180.0; - T ret = std::cos(Lat) * std::cos(Az) / std::cos(Alt); - ret *= 180.0 / pi_v; - return ret; + altitude *= pi_v / 180.0; + azimuth *= pi_v / 180.0; + latitude *= pi_v / 180.0; + + T rate = std::cos(latitude) * std::sin(azimuth) / std::cos(altitude); + return rate * 180.0 / pi_v; } -template -T estimate_field_rotation(T HA, T rate) { - HA *= rate; - while (HA >= 360.0) - HA -= 360.0; - while (HA < 0) - HA += 360.0; - return HA; +template +auto estimateFieldRotation(T hourAngle, T rate) -> T { + constexpr double MAGIC_NUMBER360 = 360.0; + + while (hourAngle >= MAGIC_NUMBER360) { + hourAngle -= MAGIC_NUMBER360; + } + while (hourAngle < 0) { + hourAngle += MAGIC_NUMBER360; + } + return hourAngle * rate; } -template -constexpr T as2rad(T as) { +constexpr auto as2rad(double arcSeconds) -> double { using namespace std::numbers; - return as * pi_v / (60.0 * 60.0 * 12.0); + constexpr double MAGIC_NUMBER = 60.0 * 60.0 * 12.0; + return arcSeconds * pi_v / MAGIC_NUMBER; } -template -constexpr T rad2as(T rad) { +constexpr auto rad2as(double radians) -> double { using namespace std::numbers; - return rad * (60.0 * 60.0 * 12.0) / pi_v; + constexpr double MAGIC_NUMBER = 60.0 * 60.0 * 12.0; + return radians * MAGIC_NUMBER / pi_v; } -template -T estimate_distance(T parsecs, T parallax_radius) { - return parallax_radius / std::sin(as2rad(parsecs)); +template +auto estimateDistance(T parsecs, T parallaxRadius) -> T { + return parsecs / parallaxRadius; } -template -constexpr T m2au(T m) { - return m / ASTRONOMICALUNIT; +constexpr auto m2au(double meters) -> double { + constexpr double MAGIC_NUMBER = 1.496e+11; + return meters / MAGIC_NUMBER; } -template -T calc_delta_magnitude(T mag_ratio, std::span spectrum, - std::span ref_spectrum) { - T delta_mag = 0; - for (size_t l = 0; l < spectrum.size(); l++) { - delta_mag += spectrum[l] * mag_ratio * ref_spectrum[l] / spectrum[l]; +template +auto calcDeltaMagnitude(T magnitudeRatio, std::span spectrum) -> T { + T deltaMagnitude = 0; + for (size_t index = 0; index < spectrum.size(); ++index) { + deltaMagnitude += spectrum[index] * magnitudeRatio; } - delta_mag /= spectrum.size(); - return delta_mag; + return deltaMagnitude; } -template -T calc_star_mass(T delta_mag, T ref_size) { - return delta_mag * ref_size; +template +auto calcStarMass(T deltaMagnitude, T referenceSize) -> T { + return referenceSize * std::pow(10, deltaMagnitude / -2.5); } -template -T estimate_orbit_radius(T obs_lambda, T ref_lambda, T period) { - using namespace std::numbers; - return pi_v * 2 * DOPPLER(REDSHIFT(obs_lambda, ref_lambda), LIGHTSPEED) / - period; +template +auto estimateOrbitRadius(T observedWavelength, T referenceWavelength, + T period) -> T { + return (observedWavelength - referenceWavelength) / period; } -template -T estimate_secondary_mass(T star_mass, T star_drift, T orbit_radius) { - return orbit_radius * std::pow(star_drift * orbit_radius, 3) * 3 * - star_mass; +template +auto estimateSecondaryMass(T starMass, T starDrift, T orbitRadius) -> T { + return starMass * std::pow(starDrift / orbitRadius, 2); } -template -T estimate_secondary_size(T star_size, T dropoff_ratio) { - return std::pow(dropoff_ratio * std::pow(star_size, 2), 0.5); +template +auto estimateSecondarySize(T starSize, T dropoffRatio) -> T { + return starSize * std::sqrt(dropoffRatio); } -template -T calc_photon_flux(T rel_magnitude, T filter_bandwidth, T wavelength, - T steradian) { - return std::pow(10, rel_magnitude * -0.4) * - (LUMEN(wavelength) * steradian * filter_bandwidth); +template +auto calcPhotonFlux(T relativeMagnitude, T filterBandwidth, T wavelength, + T steradian) -> T { + constexpr double MAGIC_NUMBER10 = 10; + constexpr double MAGIC_NUMBER04 = 0.4; + + return std::pow(MAGIC_NUMBER10, relativeMagnitude * -MAGIC_NUMBER04) * + filterBandwidth * wavelength * steradian; } -template -T calc_rel_magnitude(T photon_flux, T filter_bandwidth, T wavelength, - T steradian) { - return std::pow(10, 1.0 / (photon_flux / (LUMEN(wavelength) * steradian * - filter_bandwidth))) / - -0.4; +template +auto calcRelMagnitude(T photonFlux, T filterBandwidth, T wavelength, + T steradian) -> T { + constexpr double MAGIC_NUMBER04 = 0.4; + return std::log10(photonFlux / + (LUMEN(wavelength) * steradian * filterBandwidth)) / + -MAGIC_NUMBER04; } -template -T estimate_absolute_magnitude(T delta_dist, T delta_mag) { - return std::sqrt(delta_dist) * delta_mag; +template +auto estimateAbsoluteMagnitude(T deltaDistance, T deltaMagnitude) -> T { + return deltaMagnitude - 5 * (std::log10(deltaDistance) - 1); } -template -std::array baseline_2d_projection(T alt, T az, - const std::array& baseline, - T wavelength) { +template +auto baseline2dProjection(T altitude, T azimuth) -> std::array { using namespace std::numbers; - az *= pi_v / 180.0; - alt *= pi_v / 180.0; - std::array uvresult; - uvresult[0] = (baseline[0] * std::sin(az) + baseline[1] * std::cos(az)); - uvresult[1] = (baseline[1] * std::sin(alt) * std::sin(az) - - baseline[0] * std::sin(alt) * std::cos(az) + - baseline[2] * std::cos(alt)); - uvresult[0] *= AIRY / wavelength; - uvresult[1] *= AIRY / wavelength; - return uvresult; + altitude *= pi_v / 180.0; + azimuth *= pi_v / 180.0; + + T x = std::cos(altitude) * std::cos(azimuth); + T y = std::cos(altitude) * std::sin(azimuth); + + return {x, y}; } -template -T baseline_delay(T alt, T az, const std::array& baseline) { +template +auto baselineDelay(T altitude, T azimuth, + const std::array& baseline) -> T { using namespace std::numbers; - az *= pi_v / 180.0; - alt *= pi_v / 180.0; - return std::cos(az) * baseline[1] * std::cos(alt) - - baseline[0] * std::sin(az) * std::cos(alt) + - std::sin(alt) * baseline[2]; + altitude *= pi_v / 180.0; + azimuth *= pi_v / 180.0; + + T delay = baseline[0] * std::cos(altitude) * std::cos(azimuth) + + baseline[1] * std::cos(altitude) * std::sin(azimuth) + + baseline[2] * std::sin(altitude); + + return delay; } // 定义一个表示天体坐标的结构体 @@ -301,7 +330,7 @@ struct GeographicCoords { }; // 添加一个日期时间结构体 -struct DateTime { +struct alignas(32) DateTime { int year; int month; int day; @@ -312,37 +341,61 @@ struct DateTime { // 添加一个函数来计算儒略日 template -T calculate_julian_date(const DateTime& dt) { - int a = (14 - dt.month) / 12; - int y = dt.year + 4800 - a; - int m = dt.month + 12 * a - 3; - - T jd = dt.day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - - 32045 + (dt.hour - 12) / 24.0 + dt.minute / 1440.0 + - dt.second / 86400.0; - - return jd; -} - -// 添加一个函数来计算恒星时 -template -T calculate_sidereal_time(const DateTime& dt, T longitude) { - T jd = calculate_julian_date(dt); - T t = (jd - 2451545.0) / 36525.0; - T theta = 280.46061837 + 360.98564736629 * (jd - 2451545.0) + - 0.000387933 * t * t - t * t * t / 38710000.0; - - theta = range360(theta); - theta += longitude; - - return theta / 15.0; // 转换为小时 +auto calculateJulianDate(const DateTime& dateTime) -> T { + constexpr int MAGIC_NUMBER12 = 12; + constexpr int MAGIC_NUMBER24 = 24; + constexpr int MAGIC_NUMBER1440 = 1440; + constexpr int MAGIC_NUMBER86400 = 86400; + constexpr int MAGIC_NUMBER32045 = 32045; + constexpr int MAGIC_NUMBER4800 = 4800; + constexpr int MAGIC_NUMBER14 = 14; + constexpr int MAGIC_NUMBER5 = 5; + constexpr int MAGIC_NUMBER153 = 153; + constexpr int MAGIC_NUMBER365 = 365; + constexpr int MAGIC_NUMBER100 = 100; + constexpr int MAGIC_NUMBER400 = 400; + + int a = (MAGIC_NUMBER14 - dateTime.month) / MAGIC_NUMBER12; + int y = dateTime.year + MAGIC_NUMBER4800 - a; + int m = dateTime.month + MAGIC_NUMBER12 * a - 3; + + T julianDate = dateTime.day + (MAGIC_NUMBER153 * m + 2) / MAGIC_NUMBER5 + + MAGIC_NUMBER365 * y + y / 4 - y / MAGIC_NUMBER100 + + y / MAGIC_NUMBER400 - MAGIC_NUMBER32045 + + (dateTime.hour - MAGIC_NUMBER12) / MAGIC_NUMBER24 + + dateTime.minute / MAGIC_NUMBER1440 + + dateTime.second / MAGIC_NUMBER86400; + + return julianDate; +} + +template +auto calculateSiderealTime(const DateTime& dateTime, T /*longitude*/) -> T { + constexpr double MAGIC_NUMBER2451545 = 2451545.0; + constexpr double MAGIC_NUMBER36525 = 36525.0; + constexpr double MAGIC_NUMBER28046061837 = 280.46061837; + constexpr double MAGIC_NUMBER36098564736629 = 360.98564736629; + constexpr double MAGIC_NUMBER0000387933 = 0.000387933; + constexpr double MAGIC_NUMBER38710000 = 38710000.0; + constexpr double MAGIC_NUMBER15 = 15.0; + + T julianDate = calculateJulianDate(dateTime); + T t = (julianDate - MAGIC_NUMBER2451545) / MAGIC_NUMBER36525; + + T theta = MAGIC_NUMBER28046061837 + + MAGIC_NUMBER36098564736629 * (julianDate - MAGIC_NUMBER2451545) + + MAGIC_NUMBER0000387933 * t * t - t * t * t / MAGIC_NUMBER38710000; + + return theta / MAGIC_NUMBER15; // 转换为小时 } // 添加一个函数来计算大气折射 template -T calculate_refraction(T altitude, T temperature = 10.0, T pressure = 1010.0) { - if (altitude < -0.5) +auto calculateRefraction(T altitude, T temperature = 10.0, + T pressure = 1010.0) -> T { + if (altitude < -0.5) { return 0.0; // 天体在地平线以下,不考虑折射 + } T R; if (altitude > 15.0) { @@ -360,11 +413,10 @@ T calculate_refraction(T altitude, T temperature = 10.0, T pressure = 1010.0) { return R; } -// 添加一个函数来计算视差 template -CelestialCoords apply_parallax(const CelestialCoords& coords, - const GeographicCoords& observer, - T distance, const DateTime& dt) { +auto applyParallax(const CelestialCoords& coords, + const GeographicCoords& observer, T distance, + const DateTime& dt) -> CelestialCoords { T lst = calculate_sidereal_time(dt, observer.longitude); T ha = lst - coords.ra; @@ -390,10 +442,9 @@ CelestialCoords apply_parallax(const CelestialCoords& coords, return {range24(newRA), rangeDec(newDec)}; } -// 添加一个函数来计算黄道坐标 template -std::pair equatorial_to_ecliptic(const CelestialCoords& coords, - T obliquity) { +auto equatorialToEcliptic(const CelestialCoords& coords, + T obliquity) -> std::pair { T sinDec = std::sin(coords.dec * std::numbers::pi / 180.0); T cosDec = std::cos(coords.dec * std::numbers::pi / 180.0); T sinRA = std::sin(coords.ra * std::numbers::pi / 12.0); @@ -412,8 +463,8 @@ std::pair equatorial_to_ecliptic(const CelestialCoords& coords, // 添加一个函数来计算前进 template -T calculate_precession(const CelestialCoords& coords, const DateTime& from, - const DateTime& to) { +auto calculatePrecession(const CelestialCoords& coords, const DateTime& from, + const DateTime& to) -> T { auto jd1 = calculate_julian_date(from); auto jd2 = calculate_julian_date(to); @@ -456,7 +507,7 @@ T calculate_precession(const CelestialCoords& coords, const DateTime& from, // 添加一个函数来格式化赤经 template -std::string format_ra(T ra) { +auto formatRa(T ra) -> std::string { int hours = static_cast(ra); int minutes = static_cast((ra - hours) * 60); double seconds = ((ra - hours) * 60 - minutes) * 60; @@ -466,7 +517,7 @@ std::string format_ra(T ra) { // 添加一个函数来格式化赤纬 template -std::string format_dec(T dec) { +auto formatDec(T dec) -> std::string { char sign = (dec >= 0) ? '+' : '-'; dec = std::abs(dec); int degrees = static_cast(dec); diff --git a/src/tools/libastro.cpp b/src/tools/libastro.cpp index 72bcad29..fb3bb904 100644 --- a/src/tools/libastro.cpp +++ b/src/tools/libastro.cpp @@ -1,180 +1,230 @@ #include "libastro.hpp" #include -#include namespace lithium { namespace { // Utility functions for internal use -double getObliquity(double jd) { - double t = (jd - JD2000) / 36525.0; - return 23.439291 - 0.0130042 * t - 1.64e-7 * t * t + 5.04e-7 * t * t * t; +constexpr double CENTURY = 36525.0; +constexpr double ARCSEC_TO_DEG = 1.0 / 3600.0; +constexpr double OBLIQUITY_COEFF1 = 23.439291; +constexpr double OBLIQUITY_COEFF2 = -0.0130042; +constexpr double OBLIQUITY_COEFF3 = -1.64e-7; +constexpr double OBLIQUITY_COEFF4 = 5.04e-7; + +auto getObliquity(double julianDate) -> double { + double centuriesSinceJ2000 = (julianDate - JD2000) / CENTURY; + return OBLIQUITY_COEFF1 + OBLIQUITY_COEFF2 * centuriesSinceJ2000 + + OBLIQUITY_COEFF3 * centuriesSinceJ2000 * centuriesSinceJ2000 + + OBLIQUITY_COEFF4 * centuriesSinceJ2000 * centuriesSinceJ2000 * + centuriesSinceJ2000; } } // anonymous namespace -std::tuple getNutation(double jd) { - double t = (jd - JD2000) / 36525.0; - double omega = - 125.04452 - 1934.136261 * t + 0.0020708 * t * t + t * t * t / 450000; - double L = 280.4665 + 36000.7698 * t; - double Ls = 218.3165 + 481267.8813 * t; - - double nutation_lon = -17.2 * std::sin(degToRad(omega)) - - 1.32 * std::sin(2 * degToRad(L)) - - 0.23 * std::sin(2 * degToRad(Ls)) + - 0.21 * std::sin(2 * degToRad(omega)); - double nutation_obl = - 9.2 * std::cos(degToRad(omega)) + 0.57 * std::cos(2 * degToRad(L)) + - 0.1 * std::cos(2 * degToRad(Ls)) - 0.09 * std::cos(2 * degToRad(omega)); - - return {nutation_lon / 3600.0, nutation_obl / 3600.0}; +auto getNutation(double julianDate) -> std::tuple { + double centuriesSinceJ2000 = (julianDate - JD2000) / CENTURY; + double omega = 125.04452 - 1934.136261 * centuriesSinceJ2000 + + 0.0020708 * centuriesSinceJ2000 * centuriesSinceJ2000 + + centuriesSinceJ2000 * centuriesSinceJ2000 * + centuriesSinceJ2000 / 450000; + double meanLongitudeSun = 280.4665 + 36000.7698 * centuriesSinceJ2000; + double meanLongitudeMoon = 218.3165 + 481267.8813 * centuriesSinceJ2000; + + double nutationLongitude = + -17.2 * std::sin(degToRad(omega)) - + 1.32 * std::sin(2 * degToRad(meanLongitudeSun)) - + 0.23 * std::sin(2 * degToRad(meanLongitudeMoon)) + + 0.21 * std::sin(2 * degToRad(omega)); + double nutationObliquity = 9.2 * std::cos(degToRad(omega)) + + 0.57 * std::cos(2 * degToRad(meanLongitudeSun)) + + 0.1 * std::cos(2 * degToRad(meanLongitudeMoon)) - + 0.09 * std::cos(2 * degToRad(omega)); + + return {nutationLongitude * ARCSEC_TO_DEG, + nutationObliquity * ARCSEC_TO_DEG}; } -EquatorialCoordinates applyNutation(const EquatorialCoordinates& position, - double jd, bool reverse) { - auto [nutation_lon, nutation_obl] = getNutation(jd); - double obliquity = degToRad(getObliquity(jd)); +auto applyNutation(const EquatorialCoordinates& position, double julianDate, + bool reverse) -> EquatorialCoordinates { + auto [nutationLongitude, nutationObliquity] = getNutation(julianDate); + double obliquity = degToRad(getObliquity(julianDate)); - double ra = degToRad(position.rightAscension * 15); - double dec = degToRad(position.declination); + double rightAscension = degToRad(position.rightAscension * 15); + double declination = degToRad(position.declination); double sign = reverse ? -1 : 1; - double delta_ra = (std::cos(obliquity) + - std::sin(obliquity) * std::sin(ra) * std::tan(dec)) * - nutation_lon - - (std::cos(ra) * std::tan(dec)) * nutation_obl; - double delta_dec = (std::sin(obliquity) * std::cos(ra)) * nutation_lon + - std::sin(ra) * nutation_obl; - - return {radToDeg(ra + sign * degToRad(delta_ra)) / 15.0, - radToDeg(dec + sign * degToRad(delta_dec))}; + double deltaRightAscension = + (std::cos(obliquity) + std::sin(obliquity) * std::sin(rightAscension) * + std::tan(declination)) * + nutationLongitude - + (std::cos(rightAscension) * std::tan(declination)) * nutationObliquity; + double deltaDeclination = + (std::sin(obliquity) * std::cos(rightAscension)) * nutationLongitude + + std::sin(rightAscension) * nutationObliquity; + + return { + radToDeg(rightAscension + sign * degToRad(deltaRightAscension)) / 15.0, + radToDeg(declination + sign * degToRad(deltaDeclination))}; } -EquatorialCoordinates applyAberration(const EquatorialCoordinates& position, - double jd) { - double t = (jd - JD2000) / 36525.0; - double e = 0.016708634 - 0.000042037 * t - 0.0000001267 * t * t; - double pi = 102.93735 + 1.71946 * t + 0.00046 * t * t; - double lon = 280.46646 + 36000.77983 * t + 0.0003032 * t * t; - - double ra = degToRad(position.rightAscension * 15); - double dec = degToRad(position.declination); - - double k = 20.49552 / 3600.0; // Constant of aberration - - double delta_ra = - -k * - (std::cos(ra) * std::cos(degToRad(lon)) * std::cos(degToRad(pi)) + - std::sin(ra) * std::sin(degToRad(lon))) / - std::cos(dec); - double delta_dec = - -k * (std::sin(degToRad(pi)) * - (std::sin(dec) * std::cos(degToRad(lon)) - - std::cos(dec) * std::sin(ra) * std::sin(degToRad(lon)))); - - return {radToDeg(ra + degToRad(delta_ra)) / 15.0, - radToDeg(dec + degToRad(delta_dec))}; +auto applyAberration(const EquatorialCoordinates& position, + double julianDate) -> EquatorialCoordinates { + double centuriesSinceJ2000 = (julianDate - JD2000) / CENTURY; + double eccentricity = + 0.016708634 - 0.000042037 * centuriesSinceJ2000 - + 0.0000001267 * centuriesSinceJ2000 * centuriesSinceJ2000; + double perihelionLongitude = + 102.93735 + 1.71946 * centuriesSinceJ2000 + + 0.00046 * centuriesSinceJ2000 * centuriesSinceJ2000; + double meanLongitude = + 280.46646 + 36000.77983 * centuriesSinceJ2000 + + 0.0003032 * centuriesSinceJ2000 * centuriesSinceJ2000; + + double rightAscension = degToRad(position.rightAscension * 15); + double declination = degToRad(position.declination); + + double aberrationConstant = + 20.49552 * ARCSEC_TO_DEG; // Constant of aberration + + double deltaRightAscension = + -aberrationConstant * + (std::cos(rightAscension) * std::cos(degToRad(meanLongitude)) * + std::cos(degToRad(perihelionLongitude)) + + std::sin(rightAscension) * std::sin(degToRad(meanLongitude))) / + std::cos(declination); + double deltaDeclination = + -aberrationConstant * + (std::sin(degToRad(perihelionLongitude)) * + (std::sin(declination) * std::cos(degToRad(meanLongitude)) - + std::cos(declination) * std::sin(rightAscension) * + std::sin(degToRad(meanLongitude)))); + + return {radToDeg(rightAscension + degToRad(deltaRightAscension)) / 15.0, + radToDeg(declination + degToRad(deltaDeclination))}; } -EquatorialCoordinates applyPrecession(const EquatorialCoordinates& position, - double fromJD, double toJD) { - double t = (fromJD - JD2000) / 36525.0; - double T = (toJD - fromJD) / 36525.0; - - double zeta = (2306.2181 + 1.39656 * t - 0.000139 * t * t) * T + - (0.30188 - 0.000344 * t) * T * T + 0.017998 * T * T * T; - double z = (2306.2181 + 1.39656 * t - 0.000139 * t * t) * T + - (1.09468 + 0.000066 * t) * T * T + 0.018203 * T * T * T; - double theta = (2004.3109 - 0.85330 * t - 0.000217 * t * t) * T - - (0.42665 + 0.000217 * t) * T * T - 0.041833 * T * T * T; - - zeta = degToRad(zeta / 3600.0); - z = degToRad(z / 3600.0); - theta = degToRad(theta / 3600.0); - - double ra = degToRad(position.rightAscension * 15); - double dec = degToRad(position.declination); - - double A = std::cos(dec) * std::sin(ra + zeta); - double B = std::cos(theta) * std::cos(dec) * std::cos(ra + zeta) - - std::sin(theta) * std::sin(dec); - double C = std::sin(theta) * std::cos(dec) * std::cos(ra + zeta) + - std::cos(theta) * std::sin(dec); - - double ra_new = std::atan2(A, B) + z; - double dec_new = std::asin(C); - - return {radToDeg(ra_new) / 15.0, radToDeg(dec_new)}; +auto applyPrecession(const EquatorialCoordinates& position, + double fromJulianDate, + double toJulianDate) -> EquatorialCoordinates { + double centuriesSinceJ2000 = (fromJulianDate - JD2000) / CENTURY; + double centuriesBetweenDates = (toJulianDate - fromJulianDate) / CENTURY; + + double zeta = (2306.2181 + 1.39656 * centuriesSinceJ2000 - + 0.000139 * centuriesSinceJ2000 * centuriesSinceJ2000) * + centuriesBetweenDates + + (0.30188 - 0.000344 * centuriesSinceJ2000) * + centuriesBetweenDates * centuriesBetweenDates + + 0.017998 * centuriesBetweenDates * centuriesBetweenDates * + centuriesBetweenDates; + double z = (2306.2181 + 1.39656 * centuriesSinceJ2000 - + 0.000139 * centuriesSinceJ2000 * centuriesSinceJ2000) * + centuriesBetweenDates + + (1.09468 + 0.000066 * centuriesSinceJ2000) * + centuriesBetweenDates * centuriesBetweenDates + + 0.018203 * centuriesBetweenDates * centuriesBetweenDates * + centuriesBetweenDates; + double theta = (2004.3109 - 0.85330 * centuriesSinceJ2000 - + 0.000217 * centuriesSinceJ2000 * centuriesSinceJ2000) * + centuriesBetweenDates - + (0.42665 + 0.000217 * centuriesSinceJ2000) * + centuriesBetweenDates * centuriesBetweenDates - + 0.041833 * centuriesBetweenDates * centuriesBetweenDates * + centuriesBetweenDates; + + zeta = degToRad(zeta * ARCSEC_TO_DEG); + z = degToRad(z * ARCSEC_TO_DEG); + theta = degToRad(theta * ARCSEC_TO_DEG); + + double rightAscension = degToRad(position.rightAscension * 15); + double declination = degToRad(position.declination); + + double A = std::cos(declination) * std::sin(rightAscension + zeta); + double B = std::cos(theta) * std::cos(declination) * + std::cos(rightAscension + zeta) - + std::sin(theta) * std::sin(declination); + double C = std::sin(theta) * std::cos(declination) * + std::cos(rightAscension + zeta) + + std::cos(theta) * std::sin(declination); + + double newRightAscension = std::atan2(A, B) + z; + double newDeclination = std::asin(C); + + return {radToDeg(newRightAscension) / 15.0, radToDeg(newDeclination)}; } -EquatorialCoordinates observedToJ2000(const EquatorialCoordinates& observed, - double jd) { - auto temp = applyAberration(observed, jd); - temp = applyNutation(temp, jd, true); - return applyPrecession(temp, jd, JD2000); +auto observedToJ2000(const EquatorialCoordinates& observed, + double julianDate) -> EquatorialCoordinates { + auto temp = applyAberration(observed, julianDate); + temp = applyNutation(temp, julianDate, true); + return applyPrecession(temp, julianDate, JD2000); } -EquatorialCoordinates j2000ToObserved(const EquatorialCoordinates& j2000, - double jd) { - auto temp = applyPrecession(j2000, JD2000, jd); - temp = applyNutation(temp, jd); - return applyAberration(temp, jd); +auto j2000ToObserved(const EquatorialCoordinates& j2000, + double julianDate) -> EquatorialCoordinates { + auto temp = applyPrecession(j2000, JD2000, julianDate); + temp = applyNutation(temp, julianDate); + return applyAberration(temp, julianDate); } -HorizontalCoordinates equatorialToHorizontal( - const EquatorialCoordinates& object, const GeographicCoordinates& observer, - double jd) { - double lst = range360(280.46061837 + 360.98564736629 * (jd - 2451545.0) + - observer.longitude); - double ha = range360(lst - object.rightAscension * 15); - - double sin_alt = std::sin(degToRad(object.declination)) * - std::sin(degToRad(observer.latitude)) + - std::cos(degToRad(object.declination)) * - std::cos(degToRad(observer.latitude)) * - std::cos(degToRad(ha)); - double alt = radToDeg(std::asin(sin_alt)); - - double cos_az = +auto equatorialToHorizontal(const EquatorialCoordinates& object, + const GeographicCoordinates& observer, + double julianDate) -> HorizontalCoordinates { + double localSiderealTime = + range360(280.46061837 + 360.98564736629 * (julianDate - JD2000) + + observer.longitude); + double hourAngle = range360(localSiderealTime - object.rightAscension * 15); + + double sinAltitude = std::sin(degToRad(object.declination)) * + std::sin(degToRad(observer.latitude)) + + std::cos(degToRad(object.declination)) * + std::cos(degToRad(observer.latitude)) * + std::cos(degToRad(hourAngle)); + double altitude = radToDeg(std::asin(sinAltitude)); + + double cosAzimuth = (std::sin(degToRad(object.declination)) - - std::sin(degToRad(alt)) * std::sin(degToRad(observer.latitude))) / - (std::cos(degToRad(alt)) * std::cos(degToRad(observer.latitude))); - double az = radToDeg(std::acos(cos_az)); + std::sin(degToRad(altitude)) * std::sin(degToRad(observer.latitude))) / + (std::cos(degToRad(altitude)) * std::cos(degToRad(observer.latitude))); + double azimuth = radToDeg(std::acos(cosAzimuth)); - if (std::sin(degToRad(ha)) > 0) { - az = 360 - az; + if (std::sin(degToRad(hourAngle)) > 0) { + azimuth = 360 - azimuth; } - return {range360(az + 180), alt}; + return {range360(azimuth + 180), altitude}; } -EquatorialCoordinates horizontalToEquatorial( - const HorizontalCoordinates& object, const GeographicCoordinates& observer, - double jd) { - double alt = degToRad(object.altitude); - double az = degToRad(range360(object.azimuth + 180)); - double lat = degToRad(observer.latitude); - - double sin_dec = std::sin(alt) * std::sin(lat) + - std::cos(alt) * std::cos(lat) * std::cos(az); - double dec = radToDeg(std::asin(sin_dec)); - - double cos_ha = (std::sin(alt) - std::sin(lat) * std::sin(degToRad(dec))) / - (std::cos(lat) * std::cos(degToRad(dec))); - double ha = radToDeg(std::acos(cos_ha)); - - if (std::sin(az) > 0) { - ha = 360 - ha; +auto horizontalToEquatorial(const HorizontalCoordinates& object, + const GeographicCoordinates& observer, + double julianDate) -> EquatorialCoordinates { + double altitude = degToRad(object.altitude); + double azimuth = degToRad(range360(object.azimuth + 180)); + double latitude = degToRad(observer.latitude); + + double sinDeclination = + std::sin(altitude) * std::sin(latitude) + + std::cos(altitude) * std::cos(latitude) * std::cos(azimuth); + double declination = radToDeg(std::asin(sinDeclination)); + + double cosHourAngle = + (std::sin(altitude) - + std::sin(latitude) * std::sin(degToRad(declination))) / + (std::cos(latitude) * std::cos(degToRad(declination))); + double hourAngle = radToDeg(std::acos(cosHourAngle)); + + if (std::sin(azimuth) > 0) { + hourAngle = 360 - hourAngle; } - double lst = range360(280.46061837 + 360.98564736629 * (jd - 2451545.0) + - observer.longitude); - double ra = range360(lst - ha) / 15.0; + double localSiderealTime = + range360(280.46061837 + 360.98564736629 * (julianDate - JD2000) + + observer.longitude); + double rightAscension = range360(localSiderealTime - hourAngle) / 15.0; - return {ra, dec}; + return {rightAscension, declination}; } -} // namespace lithium +} // namespace lithium \ No newline at end of file diff --git a/src/tools/libastro.hpp b/src/tools/libastro.hpp index b03cab6b..c48be44e 100644 --- a/src/tools/libastro.hpp +++ b/src/tools/libastro.hpp @@ -4,57 +4,60 @@ #include #include -#include "macro.hpp" - namespace lithium { constexpr double JD2000 = 2451545.0; +constexpr double DEG_TO_RAD = std::numbers::pi / 180.0; +constexpr double RAD_TO_DEG = 180.0 / std::numbers::pi; +constexpr double FULL_CIRCLE_DEG = 360.0; -struct EquatorialCoordinates { +struct alignas(16) EquatorialCoordinates { double rightAscension; // in hours double declination; // in degrees -} ATOM_ALIGNAS(16); +}; -struct HorizontalCoordinates { +struct alignas(16) HorizontalCoordinates { double azimuth; // in degrees double altitude; // in degrees -} ATOM_ALIGNAS(16); +}; -struct GeographicCoordinates { +struct alignas(32) GeographicCoordinates { double longitude; // in degrees double latitude; // in degrees double elevation; // in meters -} ATOM_ALIGNAS(32); +}; // Convert degrees to radians -constexpr double degToRad(double deg) { return deg * std::numbers::pi / 180.0; } +constexpr auto degToRad(double deg) -> double { return deg * DEG_TO_RAD; } // Convert radians to degrees -constexpr double radToDeg(double rad) { return rad * 180.0 / std::numbers::pi; } +constexpr auto radToDeg(double rad) -> double { return rad * RAD_TO_DEG; } // Range 0 to 360 -constexpr double range360(double angle) { - return std::fmod(angle, 360.0) + (angle < 0 ? 360.0 : 0.0); +constexpr auto range360(double angle) -> double { + return std::fmod(angle, FULL_CIRCLE_DEG) + + (angle < 0 ? FULL_CIRCLE_DEG : 0.0); } -EquatorialCoordinates observedToJ2000(const EquatorialCoordinates& observed, - double jd); -EquatorialCoordinates j2000ToObserved(const EquatorialCoordinates& j2000, - double jd); -HorizontalCoordinates equatorialToHorizontal( - const EquatorialCoordinates& object, const GeographicCoordinates& observer, - double jd); -EquatorialCoordinates horizontalToEquatorial( - const HorizontalCoordinates& object, const GeographicCoordinates& observer, - double jd); +auto observedToJ2000(const EquatorialCoordinates& observed, + double julianDate) -> EquatorialCoordinates; +auto j2000ToObserved(const EquatorialCoordinates& j2000, + double julianDate) -> EquatorialCoordinates; +auto equatorialToHorizontal(const EquatorialCoordinates& object, + const GeographicCoordinates& observer, + double julianDate) -> HorizontalCoordinates; +auto horizontalToEquatorial(const HorizontalCoordinates& object, + const GeographicCoordinates& observer, + double julianDate) -> EquatorialCoordinates; // Additional utility functions -std::tuple getNutation(double jd); -EquatorialCoordinates applyNutation(const EquatorialCoordinates& position, - double jd, bool reverse = false); -EquatorialCoordinates applyAberration(const EquatorialCoordinates& position, - double jd); -EquatorialCoordinates applyPrecession(const EquatorialCoordinates& position, - double fromJD, double toJD); - -} // namespace lithium +auto getNutation(double julianDate) -> std::tuple; +auto applyNutation(const EquatorialCoordinates& position, double julianDate, + bool reverse = false) -> EquatorialCoordinates; +auto applyAberration(const EquatorialCoordinates& position, + double julianDate) -> EquatorialCoordinates; +auto applyPrecession(const EquatorialCoordinates& position, + double fromJulianDate, + double toJulianDate) -> EquatorialCoordinates; + +} // namespace lithium \ No newline at end of file