From 5560795f77391b195fb6854de26e2ebbf724f77e Mon Sep 17 00:00:00 2001 From: HeZongLun <13425468+hezonglun@user.noreply.gitee.com> Date: Fri, 7 Nov 2025 20:51:10 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=20SQLite=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B1=BB=EF=BC=8C=E6=94=B9=E8=BF=9B=E4=BA=86?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CommandLineParser.cpp | 389 ++++++++++++++---- .../Sources/CommandLineParser.h | 102 +++-- .../Tests/ModuleUnitTestFile.cpp | 47 +++ NahidaProject.Database/CMakeLists.txt | 6 +- NahidaProject.Database/Sources/ODBC.cpp | 48 +-- NahidaProject.Database/Sources/ODBC.h | 230 ++++++----- NahidaProject.Database/Sources/SQLite.cpp | 265 ++++++++++++ NahidaProject.Database/Sources/SQLite.h | 104 +++++ .../Tests/ModuleUnitTestFile.cpp | 99 ++++- .../Sources/DynamicLibraryLoader.cpp | 118 ++++++ .../Sources/DynamicLibraryLoader.h | 53 +++ NahidaProject.Mathematics/Sources/Complex.cpp | 116 ++++++ NahidaProject.Mathematics/Sources/Complex.h | 40 ++ NahidaProject.Mathematics/Sources/Matrix.cpp | 236 +++++++++++ NahidaProject.Mathematics/Sources/Matrix.h | 88 ++++ .../Tests/ModuleUnitTestFile.cpp | 63 +++ 16 files changed, 1747 insertions(+), 257 deletions(-) create mode 100644 NahidaProject.Database/Sources/SQLite.cpp create mode 100644 NahidaProject.Database/Sources/SQLite.h create mode 100644 NahidaProject.Generic/Sources/DynamicLibraryLoader.cpp create mode 100644 NahidaProject.Generic/Sources/DynamicLibraryLoader.h create mode 100644 NahidaProject.Mathematics/Sources/Complex.cpp create mode 100644 NahidaProject.Mathematics/Sources/Complex.h create mode 100644 NahidaProject.Mathematics/Sources/Matrix.cpp create mode 100644 NahidaProject.Mathematics/Sources/Matrix.h diff --git a/NahidaProject.Console/Sources/CommandLineParser.cpp b/NahidaProject.Console/Sources/CommandLineParser.cpp index fae63b0..8e5bef1 100644 --- a/NahidaProject.Console/Sources/CommandLineParser.cpp +++ b/NahidaProject.Console/Sources/CommandLineParser.cpp @@ -1,107 +1,346 @@ -/* -Copyright (c) 2025 HeZongLun -NahidaProject is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan -PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -*/ - #include "CommandLineParser.h" -void NahidaProject::CommandLineParser::Add(std::vector const& opts, NahidaProject::CommandLineParser::Value const& value, std::string const& desc) { - args_.emplace_back(Argument{ opts, value, desc }); +// 添加全局参数 +NahidaProject::CommandLineParser& NahidaProject::CommandLineParser::AddArgument(const std::string& name, const std::string& shortName, const std::string& description, ArgumentType type, bool required) { + Argument arg; + arg.name = name; + arg.shortName = shortName; + arg.description = description; + arg.type = type; + arg.required = required; + arg.hasDefault = false; + arg.parsed = false; + + // 检查名称冲突 + if (!name.empty() && usedLongNames.find(name) != usedLongNames.end()) { + throw std::runtime_error("Duplicate argument name: " + name); + } + if (!shortName.empty() && usedShortNames.find(shortName) != usedShortNames.end()) { + throw std::runtime_error("Duplicate short argument name: " + shortName); + } + + if (!name.empty()) { + usedLongNames.insert(name); + } + if (!shortName.empty()) { + usedShortNames.insert(shortName); + } + + globalArguments[name] = arg; + return *this; +} + +// 添加带默认值的全局参数 +NahidaProject::CommandLineParser& NahidaProject::CommandLineParser::AddArgumentWithDefault(const std::string& name, + const std::string& shortName, const std::string& description, ArgumentType type, const std::string& defaultValue) { + AddArgument(name, shortName, description, type, false); + globalArguments[name].hasDefault = true; + globalArguments[name].defaultValue = defaultValue; + return *this; +} + +// 添加子命令 +NahidaProject::CommandLineParser& NahidaProject::CommandLineParser::AddSubCommand(const std::string& name, const std::string& description) { + SubCommand subCommand; + subCommand.name = name; + subCommand.description = description; + subCommand.parsed = false; + subCommands[name] = subCommand; + return *this; +} + +// 为子命令添加参数 +NahidaProject::CommandLineParser& NahidaProject::CommandLineParser::AddSubCommandArgument(const std::string& subcommandName, const std::string& name, const std::string& shortName, const std::string& description, ArgumentType type, bool required) { + if (subCommands.find(subcommandName) == subCommands.end()) { + throw std::runtime_error("Subcommand not found: " + subcommandName); + } + + Argument arg; + arg.name = name; + arg.shortName = shortName; + arg.description = description; + arg.type = type; + arg.required = required; + arg.hasDefault = false; + arg.parsed = false; + + subCommands[subcommandName].arguments[name] = arg; + return *this; +} + +// 为子命令设置回调函数 +NahidaProject::CommandLineParser& NahidaProject::CommandLineParser::SetSubCommandCallback(const std::string& subcommandName, std::function callback) { + if (subCommands.find(subcommandName) == subCommands.end()) { + throw std::runtime_error("Subcommand not found: " + subcommandName); + } + subCommands[subcommandName].callback = callback; + return *this; +} + +// 解析命令行参数 +void NahidaProject::CommandLineParser::Parse(int argc, char* argv[]) { + std::vector args; + for (int i = 1; i < argc; ++i) { + args.push_back(argv[i]); + } + Parse(args); } -void NahidaProject::CommandLineParser::PrintHelp(std::ostream& os) const { - const auto maxOptionLength = MaxOptionsLength(); +void NahidaProject::CommandLineParser::Parse(const std::vector& args) { + size_t i = 0; + std::string subCommandName; - for (auto const& arg : args_) { - std::string opts; + // 解析全局参数 + while (i < args.size()) { + const std::string& arg = args[i]; - for (auto const& opt : arg.options_) { - opts += opt + ", "; + // 检查是否是子命令 + if (subCommands.find(arg) != subCommands.end()) { + subCommandName = arg; + break; } - std::stringstream sstr; - sstr << std::left << std::setw(maxOptionLength) << opts.substr(0, opts.size() - 2); + // 解析全局参数 + if (!ParseArgument(args, i, globalArguments)) { + i++; + } + } + + // 解析子命令参数 + if (!subCommandName.empty() && i < args.size()) { + i++; // 跳过子命令名称 + SubCommand& subCommand = subCommands[subCommandName]; + subCommand.parsed = true; + + while (i < args.size()) { + if (!ParseArgument(args, i, subCommand.arguments)) { + positionalArguments.push_back(args[i]); + i++; + } + } + + // 验证必需参数 + ValidateRequiredArguments(subCommand.arguments); + + // 执行回调 + if (subCommand.callback) { + subCommand.callback(); + } + } + else if (!subCommandName.empty()) { + // 只有子命令名称,没有参数 + SubCommand& subCommand = subCommands[subCommandName]; + subCommand.parsed = true; + ValidateRequiredArguments(subCommand.arguments); + if (subCommand.callback) { + subCommand.callback(); + } + } - size_t spacePos = 0; - size_t lineWidth = 0; + // 验证全局必需参数 + ValidateRequiredArguments(globalArguments); +} - while (spacePos != std::string::npos) { - size_t nextspacePos = arg.desc_.find_first_of(' ', spacePos + 1); - sstr << arg.desc_.substr(spacePos, nextspacePos - spacePos); - lineWidth += nextspacePos - spacePos; - spacePos = nextspacePos; +// 检查是否解析了某个子命令 +bool NahidaProject::CommandLineParser::IsSubcommandParsed(const std::string& name) const { + auto it = subCommands.find(name); + return it != subCommands.end() && it->second.parsed; +} - if (lineWidth > 60) { - os << sstr.str() << std::endl; - sstr = std::stringstream(); - sstr << std::left << std::setw(maxOptionLength - 1) << " "; - lineWidth = 0; +// 获取参数值 +std::string NahidaProject::CommandLineParser::GetValue(const std::string& name) const { + // 先检查子命令参数 + for (const auto& pair : subCommands) { + if (pair.second.parsed) { + auto it = pair.second.arguments.find(name); + if (it != pair.second.arguments.end() && it->second.parsed) { + return it->second.value; + } + if (it != pair.second.arguments.end() && it->second.hasDefault) { + return it->second.defaultValue; } } } + + // 再检查全局参数 + auto it = globalArguments.find(name); + if (it != globalArguments.end() && it->second.parsed) { + return it->second.value; + } + if (it != globalArguments.end() && it->second.hasDefault) { + return it->second.defaultValue; + } + + return ""; } -void NahidaProject::CommandLineParser::Parse(int argc, char* argv[])const { - int i = 1; +// 获取整数值 +int NahidaProject::CommandLineParser::GetIntegerValue(const std::string& name) const { + std::string value = GetValue(name); + if (value.empty()) return 0; + return std::stoi(value); +} - while (i < argc) { - std::string flag(argv[i]); - std::string value; - bool valueIsSeparate = false; - size_t equalPos = flag.find('='); +// 获取浮点数值 +float NahidaProject::CommandLineParser::GetFloatValue(const std::string& name) const { + std::string value = GetValue(name); + if (value.empty()) return 0.0f; + return std::stof(value); +} - if (equalPos != std::string::npos) { - value = flag.substr(equalPos + 1); - flag = flag.substr(0, equalPos); - } - else if (i + 1 < argc) { - value = argv[i + 1]; - valueIsSeparate = true; +// 获取布尔值 +bool NahidaProject::CommandLineParser::GetBoolValue(const std::string& name) const { + std::string value = GetValue(name); + return !value.empty() && (value == "true" || value == "1"); +} + +// 获取位置参数 +const std::vector& NahidaProject::CommandLineParser::GetPositionalArguments() const { + return positionalArguments; +} + +// 显示帮助信息 +void NahidaProject::CommandLineParser::ShowHelp() const { + std::cout << "Usage: " << programName << " [GLOBAL_OPTIONS] [SUBCOMMAND_OPTIONS]\n\n"; + + if (!description.empty()) { + std::cout << description << "\n\n"; + } + + // 显示全局参数 + if (!globalArguments.empty()) { + std::cout << "Options:\n"; + for (const auto& pair : globalArguments) { + const Argument& arg = pair.second; + std::cout << " "; + if (!arg.shortName.empty()) { + std::cout << "-" << arg.shortName << ", "; + } + std::cout << "--" << arg.name; + if (arg.required) { + std::cout << " (required)"; + } + if (arg.hasDefault) { + std::cout << " [default: " << arg.defaultValue << "]"; + } + std::cout << "\n " << arg.description << "\n\n"; } + } - bool foundArgument = false; + // 显示子命令 + if (!subCommands.empty()) { + std::cout << "Commands:\n"; + for (const auto& pair : subCommands) { + const SubCommand& subcmd = pair.second; + std::cout << " " << subcmd.name; + if (!subcmd.description.empty()) { + std::cout << " - " << subcmd.description; + } + std::cout << "\n"; - for (auto const& argument : args_) { - if (std::find(argument.options_.begin(), argument.options_.end(), flag) != std::end(argument.options_)) { - foundArgument = true; - if (std::holds_alternative(argument.value_)) { - if (!value.empty() && value != "true" && value != "false") { - valueIsSeparate = false; + // 显示子命令参数 + if (!subcmd.arguments.empty()) { + for (const auto& arg_pair : subcmd.arguments) { + const Argument& arg = arg_pair.second; + std::cout << " "; + if (!arg.shortName.empty()) { + std::cout << "-" << arg.shortName << ", "; } - - *std::get(argument.value_) = (value != "false"); - } - else if (value.empty()) { - throw std::runtime_error("Failed to parse command line arguments: Missing value for argument \"" + flag + "\"!"); - } - else if (std::holds_alternative(argument.value_)) { - *std::get(argument.value_) = value; - } - else { - std::visit([&value](auto&& arg) { - std::stringstream sstr(value); - sstr >> *arg; - }, argument.value_); + std::cout << "--" << arg.name; + if (arg.required) { + std::cout << " (required)"; + } + std::cout << "\n " << arg.description << "\n"; } + } + std::cout << "\n"; + } + } +} + +bool NahidaProject::CommandLineParser::ParseArgument(const std::vector& args, size_t& i, std::map& arguments) { + const std::string& arg = args[i]; + + if (arg.substr(0, 2) == "--") { + // 长参数 + std::string name = arg.substr(2); + std::string value; + + // 检查是否有等号 + size_t eq_pos = name.find('='); + if (eq_pos != std::string::npos) { + value = name.substr(eq_pos + 1); + name = name.substr(0, eq_pos); + } + + auto it = arguments.find(name); + if (it == arguments.end()) { + throw std::runtime_error("Unknown argument: --" + name); + } + + Argument& argument = it->second; + if (argument.type == ArgumentType::BOOLEAN) { + argument.value = "true"; + argument.parsed = true; + return true; + } + else { + if (value.empty() && i + 1 < args.size()) { + argument.value = args[++i]; + } + else if (!value.empty()) { + argument.value = value; + } + else { + throw std::runtime_error("Missing value for argument: --" + name); + } + argument.parsed = true; + return true; + } + } + else if (arg.substr(0, 1) == "-") { + // 短参数 + std::string shortName = arg.substr(1); + + // 查找对应的参数 + Argument* argument = nullptr; + for (auto& pair : arguments) { + if (pair.second.shortName == shortName) { + argument = &pair.second; break; } } - if (!foundArgument) { - std::cerr << "Unknown argument: \"" << flag << "\"." << std::endl; + if (!argument) { + throw std::runtime_error("Unknown argument: -" + shortName); } - ++i; - if (foundArgument && valueIsSeparate) { - ++i; + if (argument->type == ArgumentType::BOOLEAN) { + argument->value = "true"; + argument->parsed = true; + return true; + } + else { + if (i + 1 < args.size()) { + argument->value = args[++i]; + } + else { + throw std::runtime_error("Missing value for argument: -" + shortName); + } + argument->parsed = true; + return true; + } + } + + return false; +} + +// 验证必需参数 +void NahidaProject::CommandLineParser::ValidateRequiredArguments(const std::map& arguments) const { + for (const auto& pair : arguments) { + const Argument& arg = pair.second; + if (arg.required && !arg.parsed && !arg.hasDefault) { + throw std::runtime_error("Required argument missing: --" + arg.name); } } } \ No newline at end of file diff --git a/NahidaProject.Console/Sources/CommandLineParser.h b/NahidaProject.Console/Sources/CommandLineParser.h index af3ceb1..85524b6 100644 --- a/NahidaProject.Console/Sources/CommandLineParser.h +++ b/NahidaProject.Console/Sources/CommandLineParser.h @@ -1,50 +1,76 @@ -/* -Copyright (c) 2025 HeZongLun -NahidaProject is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan -PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -*/ - -#include -#include -#include #include #include -#include #include +#include +#include +#include +#include +#include namespace NahidaProject { - class __declspec(dllexport) CommandLineParser { + class CommandLineParser { public: - typedef std::variant Value; - struct Argument { std::vector options_; Value value_; std::string desc_; }; - explicit CommandLineParser() = default; - void Add(std::vector const& opts, Value const& value, std::string const& desc); - void PrintHelp(std::ostream& os = std::cout) const; - void Parse(int argc, char* argv[]) const; + // 参数类型枚举 + enum class ArgumentType { + STRING, + INTEGER, + FLOAT, + BOOLEAN + }; + + // 参数信息结构 + struct Argument { + std::string name; + std::string shortName; + std::string description; + ArgumentType type; + bool required; + bool hasDefault; + std::string defaultValue; + std::string value; + bool parsed; + }; + + // 子命令信息结构 + struct SubCommand { + std::string name; + std::string description; + std::map arguments; + std::function callback; + bool parsed; + }; private: - uint32_t MaxOptionsLength(void) const { - constexpr auto COMMA_LENGTH = 1U; - constexpr auto SPACE_LENTH = 1U; - uint32_t max_len = 0; - - for (auto const& arg : args_) { - uint32_t len = 0; - for (auto const& opt : arg.options_) { - len += static_cast(opt.size()) + COMMA_LENGTH + SPACE_LENTH; - } - max_len = std::max(max_len, len); - } - return max_len; + std::string programName; + std::string description; + std::map subCommands; + std::map globalArguments; + std::vector positionalArguments; + std::set usedShortNames; + std::set usedLongNames; + + public: + CommandLineParser(const std::string& programName = "", const std::string& description = "") : programName(programName), description(description) { + } + + CommandLineParser& AddArgument(const std::string& name, const std::string& shortName = "", const std::string& description = "", ArgumentType type = ArgumentType::STRING, bool required = false); + CommandLineParser& AddArgumentWithDefault(const std::string& name, const std::string& shortName, const std::string& description, ArgumentType type, const std::string& defaultValue); + CommandLineParser& AddSubCommand(const std::string& name, const std::string& description = ""); + CommandLineParser& AddSubCommandArgument(const std::string& subcommandName, const std::string& name, const std::string& shortName = "", const std::string& description = "", ArgumentType type = ArgumentType::STRING, bool required = false); + CommandLineParser& SetSubCommandCallback(const std::string& subcommandName, std::function callback); + void Parse(int argc, char* argv[]); + void Parse(const std::vector& args); + bool IsSubcommandParsed(const std::string& name) const; + std::string GetValue(const std::string& name) const; + int GetIntegerValue(const std::string& name) const; + float GetFloatValue(const std::string& name) const; + bool GetBoolValue(const std::string& name) const; + const std::vector& GetPositionalArguments() const; + void ShowHelp() const; + private: - std::vector args_; + bool ParseArgument(const std::vector& args, size_t& i, std::map& arguments); + void ValidateRequiredArguments(const std::map& arguments) const; }; } \ No newline at end of file diff --git a/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp b/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp index b83aa3c..2d87198 100644 --- a/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp +++ b/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp @@ -1,6 +1,7 @@ #include "..\..\NahidaProject.UnitTest\Sources\NahidaUnitTest.h" #include "..\Sources\ChooseDialog.h" +#include "..\Sources\CommandLineParser.h" #include "..\Sources\TablePrint.h" #include "..\Sources\TerminalColor.h" @@ -48,6 +49,50 @@ Test(ChooseDialogTest) { } +Test(CommandLineParserTest) { + NahidaProject::CommandLineParser parser("myapp", "A sample application with subcommands"); + + // 添加全局参数 + parser.AddArgument("verbose", "v", "Enable verbose output", NahidaProject::CommandLineParser::ArgumentType::BOOLEAN) + .AddArgument("config", "c", "Configuration file path", NahidaProject::CommandLineParser::ArgumentType::STRING); + + // 添加子命令 + parser.AddSubCommand("build", "Build the project") + .AddSubCommandArgument("build", "target", "t", "Build target", NahidaProject::CommandLineParser::ArgumentType::STRING, true) + .AddSubCommandArgument("build", "debug", "d", "Enable debug build", NahidaProject::CommandLineParser::ArgumentType::BOOLEAN) + .SetSubCommandCallback("build", [&parser]() { + std::cout << "Build target: " << parser.GetValue("target") << "\n"; + if (parser.GetBoolValue("debug")) { + std::cout << "Debug build enabled\n"; + } + }); + + parser.AddSubCommand("deploy", "Deploy the application") + .AddSubCommandArgument("deploy", "host", "h", "Target host", NahidaProject::CommandLineParser::ArgumentType::STRING, true) + .AddSubCommandArgument("deploy", "port", "p", "Target port", NahidaProject::CommandLineParser::ArgumentType::INTEGER) + .AddSubCommandArgument("deploy", "force", "f", "Force deployment", NahidaProject::CommandLineParser::ArgumentType::BOOLEAN) + .SetSubCommandCallback("deploy", [&parser]() { + std::cout << "Executing deploy command...\n"; + std::cout << "Deploying to: " << parser.GetValue("host") << "\n"; + if (parser.GetValue("port").empty()) { + std::cout << "Using default port\n"; + } + else { + std::cout << "Port: " << parser.GetIntegerValue("port") << "\n"; + } + if (parser.GetBoolValue("force")) { + std::cout << "Force deployment enabled\n"; + } + }); + + parser.AddSubCommand("help", "Show help information") + .SetSubCommandCallback("help", [&parser]() { + parser.ShowHelp(); + }); + + parser.Parse({"help"}); +} + Test(TablePrintTest) { NahidaProject::TablePrint tablePrint({ "Column1", "Column2", "Column3", "Column4", "Column5"}, { { "Data1", "Data2", "Data3", "Data4", "Data5"}, {"Data6", "Data7", "Data8", "Data9", "Data10"}}); tablePrint.PrintTable(); @@ -60,6 +105,8 @@ Test(TerminalColorTest) { std::cout << NahidaProject::TerminalColor::Cyan << "各民族," << NahidaProject::TerminalColor::Yellow << "齐奋发。" << NahidaProject::TerminalColor::Red << "争朝夕," << "兴中华。" << NahidaProject::TerminalColor::Reset << std::endl; } + + int main() { RunAllTestCase(); return 0; diff --git a/NahidaProject.Database/CMakeLists.txt b/NahidaProject.Database/CMakeLists.txt index a2e48a0..299920b 100644 --- a/NahidaProject.Database/CMakeLists.txt +++ b/NahidaProject.Database/CMakeLists.txt @@ -7,7 +7,7 @@ INCLUDE(RegisterSubproject) REGISTER_SUBPROJECT("Database" FALSE) IF(WIN32) - TARGET_LINK_LIBRARIES(NahidaProject.DatabaseTests PUBLIC odbc32) - TARGET_LINK_LIBRARIES(NahidaProject.Database.IMPLEMENT PUBLIC odbc32) - TARGET_LINK_LIBRARIES(NahidaProject.Database PUBLIC odbc32) + TARGET_LINK_LIBRARIES(NahidaProject.DatabaseTests PUBLIC odbc32 winsqlite3) + TARGET_LINK_LIBRARIES(NahidaProject.Database.IMPLEMENT PUBLIC odbc32 winsqlite3) + TARGET_LINK_LIBRARIES(NahidaProject.Database PUBLIC odbc32 winsqlite3) ENDIF() diff --git a/NahidaProject.Database/Sources/ODBC.cpp b/NahidaProject.Database/Sources/ODBC.cpp index 6db7778..f06606a 100644 --- a/NahidaProject.Database/Sources/ODBC.cpp +++ b/NahidaProject.Database/Sources/ODBC.cpp @@ -1,7 +1,7 @@ #include "ODBC.h" // 构造连接字符串 -std::string NahidaProject::ConnectionInfo::ToConnectionString() const { +std::string NahidaProject::ODBC::ConnectionInfo::ToConnectionString() const { std::string connectString; #ifdef _WIN32 @@ -48,33 +48,33 @@ std::string NahidaProject::ConnectionInfo::ToConnectionString() const { return connectString; } -void NahidaProject::RowData::AddColumn(const std::string& name, const std::string& value) { +void NahidaProject::ODBC::RowData::AddColumn(const std::string& name, const std::string& value) { columnNames.push_back(name); data[name] = value; } -std::string NahidaProject::RowData::Get(const std::string& columnName) const { +std::string NahidaProject::ODBC::RowData::Get(const std::string& columnName) const { auto it = data.find(columnName); return (it != data.end()) ? it->second : ""; } -bool NahidaProject::RowData::HasColumn(const std::string& columnName) const { +bool NahidaProject::ODBC::RowData::HasColumn(const std::string& columnName) const { return data.find(columnName) != data.end(); } -size_t NahidaProject::RowData::GetColumnCount() const { +size_t NahidaProject::ODBC::RowData::GetColumnCount() const { return columnNames.size(); } -const std::vector& NahidaProject::RowData::GetColumnNames() const { +const std::vector& NahidaProject::ODBC::RowData::GetColumnNames() const { return columnNames; } -const std::map& NahidaProject::RowData::GetData() const { +const std::map& NahidaProject::ODBC::RowData::GetData() const { return data; } -void NahidaProject::Connection::CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const std::string& operation) { +void NahidaProject::ODBC::Connection::CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const std::string& operation) { if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { return; } @@ -90,7 +90,7 @@ void NahidaProject::Connection::CheckError(SQLRETURN ret, SQLHANDLE handle, SQLS throw ODBCException(errorMessage, ret); } -void NahidaProject::Connection::Connect(const ConnectionInfo& info) { +void NahidaProject::ODBC::Connection::Connect(const ConnectionInfo& info) { if (isConnected) { throw ODBCException("Already connected to database"); } @@ -105,7 +105,7 @@ void NahidaProject::Connection::Connect(const ConnectionInfo& info) { isConnected = true; } -void NahidaProject::Connection::Connect(const std::string& connectionString) { +void NahidaProject::ODBC::Connection::Connect(const std::string& connectionString) { if (isConnected) { throw ODBCException("Already connected to database"); } @@ -119,18 +119,18 @@ void NahidaProject::Connection::Connect(const std::string& connectionString) { isConnected = true; } -void NahidaProject::Connection::Disconnect() { +void NahidaProject::ODBC::Connection::Disconnect() { if (isConnected && handleOfDatabaseConnect != SQL_NULL_HANDLE) { SQLDisconnect(handleOfDatabaseConnect); isConnected = false; } } -bool NahidaProject::Connection::IsConnected() const { +bool NahidaProject::ODBC::Connection::IsConnected() const { return isConnected; } -void NahidaProject::Statement::CheckError(SQLRETURN ret, const std::string& operation) { +void NahidaProject::ODBC::Statement::CheckError(SQLRETURN ret, const std::string& operation) { if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) { return; } @@ -146,7 +146,7 @@ void NahidaProject::Statement::CheckError(SQLRETURN ret, const std::string& oper throw ODBCException(errorMessage, ret); } -std::string NahidaProject::Statement::GetColumnData(SQLSMALLINT columnNumber) { +std::string NahidaProject::ODBC::Statement::GetColumnData(SQLSMALLINT columnNumber) { SQLCHAR buffer[1024] = { 0 }; SQLLEN indicator; @@ -163,7 +163,7 @@ std::string NahidaProject::Statement::GetColumnData(SQLSMALLINT columnNumber) { return std::string(reinterpret_cast(buffer)); } -int NahidaProject::Statement::Execute(const std::string& sql) { +int NahidaProject::ODBC::Statement::Execute(const std::string& sql) { SQLCHAR* sqlPoint = reinterpret_cast(const_cast(sql.c_str())); SQLRETURN ret = SQLExecDirect(handleOfStatement, sqlPoint, SQL_NTS); @@ -177,7 +177,7 @@ int NahidaProject::Statement::Execute(const std::string& sql) { } // 执行查询语句(SELECT) -std::vector NahidaProject::Statement::Query(const std::string& sql) { +std::vector NahidaProject::ODBC::Statement::Query(const std::string& sql) { SQLCHAR* sqlPoint = reinterpret_cast(const_cast(sql.c_str())); SQLRETURN ret = SQLExecDirect(handleOfStatement, sqlPoint, SQL_NTS); @@ -205,7 +205,7 @@ std::vector NahidaProject::Statement::Query(const std::s } // 获取结果数据 - std::vector results; + std::vector results; while ((ret = SQLFetch(handleOfStatement)) != SQL_NO_DATA) { CheckError(ret, "SQLFetch"); @@ -220,32 +220,32 @@ std::vector NahidaProject::Statement::Query(const std::s return results; } -void NahidaProject::Database::Connect(const ConnectionInfo& info) { +void NahidaProject::ODBC::Database::Connect(const NahidaProject::ODBC::ConnectionInfo& info) { connection->Connect(info); } -void NahidaProject::Database::Connect(const std::string& connectionString) { +void NahidaProject::ODBC::Database::Connect(const std::string& connectionString) { connection->Connect(connectionString); } -void NahidaProject::Database::Disconnect() { +void NahidaProject::ODBC::Database::Disconnect() { connection->Disconnect(); } -bool NahidaProject::Database::IsConnected() const { +bool NahidaProject::ODBC::Database::IsConnected() const { return connection->IsConnected(); } -std::vector NahidaProject::Database::Query(const std::string& sql) { +std::vector NahidaProject::ODBC::Database::Query(const std::string& sql) { Statement statement(*connection); return statement.Query(sql); } -int NahidaProject::Database::Execute(const std::string& sql) { +int NahidaProject::ODBC::Database::Execute(const std::string& sql) { Statement statement(*connection); return statement.Execute(sql); } -std::unique_ptr NahidaProject::Database::CreateStatement() { +std::unique_ptr NahidaProject::ODBC::Database::CreateStatement() { return std::make_unique(*connection); } \ No newline at end of file diff --git a/NahidaProject.Database/Sources/ODBC.h b/NahidaProject.Database/Sources/ODBC.h index ea72516..014a079 100644 --- a/NahidaProject.Database/Sources/ODBC.h +++ b/NahidaProject.Database/Sources/ODBC.h @@ -14,129 +14,131 @@ #include namespace NahidaProject { - class ODBCException : public std::runtime_error { - public: - explicit ODBCException(const std::string& message) : std::runtime_error(message) { - - } - - ODBCException(const std::string& message, SQLRETURN ret) : std::runtime_error(message + " (SQL Return Code: " + std::to_string(ret) + ")") { - - } - }; - - struct ConnectionInfo { - std::string server; - std::string database; - std::string username; - std::string password; - std::string driver; - int port = 0; - - std::string ToConnectionString() const; - }; - - class RowData { - private: - std::vector columnNames; - std::map data; - - public: - RowData() = default; - - void AddColumn(const std::string& name, const std::string& value); - std::string Get(const std::string& columnName) const; - bool HasColumn(const std::string& columnName) const; - size_t GetColumnCount() const; - const std::vector& GetColumnNames() const; - const std::map& GetData() const; - }; - - class Connection { - private: - SQLHENV handleOfEnvironment = SQL_NULL_HANDLE; - SQLHDBC handleOfDatabaseConnect = SQL_NULL_HANDLE; - bool isConnected = false; - - void CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const std::string& operation); - - public: - Connection() { - // 分配环境句柄 - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handleOfEnvironment); - CheckError(ret, SQL_NULL_HANDLE, SQL_HANDLE_ENV, "SQLAllocHandle(ENV)"); - - // 设置 ODBC 版本为 3.x - ret = SQLSetEnvAttr(handleOfEnvironment, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); - CheckError(ret, handleOfEnvironment, SQL_HANDLE_ENV, "SQLSetEnvAttr"); - - // 分配连接句柄 - ret = SQLAllocHandle(SQL_HANDLE_DBC, handleOfEnvironment, &handleOfDatabaseConnect); - CheckError(ret, handleOfEnvironment, SQL_HANDLE_ENV, "SQLAllocHandle(DBC)"); - } - - ~Connection() { - Disconnect(); - - if (handleOfDatabaseConnect != SQL_NULL_HANDLE) { - SQLFreeHandle(SQL_HANDLE_DBC, handleOfDatabaseConnect); + namespace ODBC { + class ODBCException : public std::runtime_error { + public: + explicit ODBCException(const std::string& message) : std::runtime_error(message) { + } - if (handleOfEnvironment != SQL_NULL_HANDLE) { - SQLFreeHandle(SQL_HANDLE_ENV, handleOfEnvironment); + + ODBCException(const std::string& message, SQLRETURN ret) : std::runtime_error(message + " (SQL Return Code: " + std::to_string(ret) + ")") { + + } + }; + + struct ConnectionInfo { + std::string server; + std::string database; + std::string username; + std::string password; + std::string driver; + int port = 0; + + std::string ToConnectionString() const; + }; + + class RowData { + private: + std::vector columnNames; + std::map data; + + public: + RowData() = default; + + void AddColumn(const std::string& name, const std::string& value); + std::string Get(const std::string& columnName) const; + bool HasColumn(const std::string& columnName) const; + size_t GetColumnCount() const; + const std::vector& GetColumnNames() const; + const std::map& GetData() const; + }; + + class Connection { + private: + SQLHENV handleOfEnvironment = SQL_NULL_HANDLE; + SQLHDBC handleOfDatabaseConnect = SQL_NULL_HANDLE; + bool isConnected = false; + + void CheckError(SQLRETURN ret, SQLHANDLE handle, SQLSMALLINT type, const std::string& operation); + + public: + Connection() { + // 分配环境句柄 + SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handleOfEnvironment); + CheckError(ret, SQL_NULL_HANDLE, SQL_HANDLE_ENV, "SQLAllocHandle(ENV)"); + + // 设置 ODBC 版本为 3.x + ret = SQLSetEnvAttr(handleOfEnvironment, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + CheckError(ret, handleOfEnvironment, SQL_HANDLE_ENV, "SQLSetEnvAttr"); + + // 分配连接句柄 + ret = SQLAllocHandle(SQL_HANDLE_DBC, handleOfEnvironment, &handleOfDatabaseConnect); + CheckError(ret, handleOfEnvironment, SQL_HANDLE_ENV, "SQLAllocHandle(DBC)"); } - } - void Connect(const ConnectionInfo& info); - void Connect(const std::string& connectionString); - void Disconnect(); - bool IsConnected() const; + ~Connection() { + Disconnect(); - friend class Statement; - }; + if (handleOfDatabaseConnect != SQL_NULL_HANDLE) { + SQLFreeHandle(SQL_HANDLE_DBC, handleOfDatabaseConnect); + } + if (handleOfEnvironment != SQL_NULL_HANDLE) { + SQLFreeHandle(SQL_HANDLE_ENV, handleOfEnvironment); + } + } - class Statement { - private: - SQLHSTMT handleOfStatement = SQL_NULL_HANDLE; - Connection* connection = nullptr; + void Connect(const ConnectionInfo& info); + void Connect(const std::string& connectionString); + void Disconnect(); + bool IsConnected() const; - void CheckError(SQLRETURN ret, const std::string& operation); - std::string GetColumnData(SQLSMALLINT columnNumber); + friend class Statement; + }; - public: - explicit Statement(Connection& connect) : connection(&connect) { - if (!connect.IsConnected()) { - throw ODBCException("Connection is not established"); - } + class Statement { + private: + SQLHSTMT handleOfStatement = SQL_NULL_HANDLE; + Connection* connection = nullptr; + + void CheckError(SQLRETURN ret, const std::string& operation); + std::string GetColumnData(SQLSMALLINT columnNumber); - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, connect.handleOfDatabaseConnect, &handleOfStatement); - if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { - throw ODBCException("Failed to allocate statement handle", ret); + public: + explicit Statement(Connection& connect) : connection(&connect) { + if (!connect.IsConnected()) { + throw ODBCException("Connection is not established"); + } + + SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, connect.handleOfDatabaseConnect, &handleOfStatement); + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { + throw ODBCException("Failed to allocate statement handle", ret); + } } - } - ~Statement() { - if (handleOfStatement != SQL_NULL_HANDLE) { - SQLFreeHandle(SQL_HANDLE_STMT, handleOfStatement); + ~Statement() { + if (handleOfStatement != SQL_NULL_HANDLE) { + SQLFreeHandle(SQL_HANDLE_STMT, handleOfStatement); + } } - } - - int Execute(const std::string& sql); - std::vector Query(const std::string& sql); - }; - - class Database { - private: - std::unique_ptr connection; - - public: - Database() : connection(std::make_unique()) {} - - void Connect(const ConnectionInfo& info); - void Connect(const std::string& connectionString); - void Disconnect(); - bool IsConnected() const; - std::vector Query(const std::string& sql); - int Execute(const std::string& sql); - std::unique_ptr CreateStatement(); - }; + + int Execute(const std::string& sql); + std::vector Query(const std::string& sql); + }; + + class Database { + private: + std::unique_ptr connection; + + public: + Database() : connection(std::make_unique()) {} + + void Connect(const ConnectionInfo& info); + void Connect(const std::string& connectionString); + void Disconnect(); + bool IsConnected() const; + std::vector Query(const std::string& sql); + int Execute(const std::string& sql); + std::unique_ptr CreateStatement(); + }; + } } diff --git a/NahidaProject.Database/Sources/SQLite.cpp b/NahidaProject.Database/Sources/SQLite.cpp new file mode 100644 index 0000000..847550b --- /dev/null +++ b/NahidaProject.Database/Sources/SQLite.cpp @@ -0,0 +1,265 @@ +#include "SQLite.h" + +// 检查数据库连接状态 +void NahidaProject::SQLite::SQLiteWrapper::CheckConnection() const { + if (!_isConnected || !database) { + throw SQLiteException("Database not connected"); + } +} + +// 执行 SQL 语句的回调函数 +int NahidaProject::SQLite::SQLiteWrapper::Callback(void* data, int argc, char** argv, char** azColName) { + DataTable* result = static_cast(data); + DataRow row; + + for (int i = 0; i < argc; i++) { + if (argv[i]) { + row.push_back(std::string(argv[i])); + } + else { + row.push_back(""); // NULL 值处理 + } + } + result->push_back(row); + return 0; +} + +// 移动赋值操作符 +NahidaProject::SQLite::SQLiteWrapper& NahidaProject::SQLite::SQLiteWrapper::operator=(SQLiteWrapper&& other) noexcept { + if (this != &other) { + Close(); + database = other.database; + _isConnected = other._isConnected; + other.database = nullptr; + other._isConnected = false; + } + return *this; +} + +// 连接数据库 +void NahidaProject::SQLite::SQLiteWrapper::Connect(const std::string& databasePath) { + if (_isConnected) { + Close(); + } + + int rc = sqlite3_open(databasePath.c_str(), &database); + if (rc != SQLITE_OK) { + std::string errorMessage = "Cannot open database: " + std::string(sqlite3_errmsg(database)); + sqlite3_close(database); + database = nullptr; + throw SQLiteException(errorMessage, rc); + } + + _isConnected = true; +} + +// 关闭数据库连接 +void NahidaProject::SQLite::SQLiteWrapper::Close() { + if (database) { + sqlite3_close(database); + database = nullptr; + _isConnected = false; + } +} + +// 执行非查询 SQL 语句(INSERT, UPDATE, DELETE, CREATE等) +int NahidaProject::SQLite::SQLiteWrapper::Execute(const std::string& sql) { + CheckConnection(); + + char* errorMessage = nullptr; + int rc = sqlite3_exec(database, sql.c_str(), nullptr, nullptr, &errorMessage); + + if (rc != SQLITE_OK) { + std::string error_msg = "SQL execution failed: "; + if (errorMessage) { + error_msg += std::string(errorMessage); + sqlite3_free(errorMessage); + } + else { + error_msg += std::string(sqlite3_errmsg(database)); + } + throw SQLiteException(error_msg, rc); + } + + return sqlite3_changes(database); // 返回受影响的行数 +} + +// 执行查询语句并返回结果 +NahidaProject::SQLite::DataTable NahidaProject::SQLite::SQLiteWrapper::Query(const std::string& sql) { + CheckConnection(); + + DataTable result; + char* errorMessage = nullptr; + int rc = sqlite3_exec(database, sql.c_str(), Callback, &result, &errorMessage); + + if (rc != SQLITE_OK) { + std::string error_msg = "Query execution failed: "; + if (errorMessage) { + error_msg += std::string(errorMessage); + sqlite3_free(errorMessage); + } + else { + error_msg += std::string(sqlite3_errmsg(database)); + } + throw SQLiteException(error_msg, rc); + } + + return result; +} + +// 绑定整数值 +void NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::BindInteger(int index, int value) { + int rc = sqlite3_bind_int(statement, index, value); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to bind integer", rc); + } +} + +// 绑定文本值 +void NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::BindText(int index, const std::string& value) { + int rc = sqlite3_bind_text(statement, index, value.c_str(), -1, SQLITE_TRANSIENT); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to bind text", rc); + } +} + +// 绑定双精度浮点值 +void NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::BindDouble(int index, double value) { + int rc = sqlite3_bind_double(statement, index, value); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to bind double", rc); + } +} + +// 绑定 NULL 值 +void NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::BindNull(int index) { + int rc = sqlite3_bind_null(statement, index); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to bind null", rc); + } +} + +// 执行预编译语句(用于 INSERT, UPDATE, DELETE) +int NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::Execute() { + int rc = sqlite3_step(statement); + if (rc != SQLITE_DONE) { + throw SQLiteException("Failed to execute statement: " + std::string(sqlite3_errmsg(database)), rc); + } + + int changes = sqlite3_changes(database); + Reset(); + return changes; +} + +// 查询数据(用于 SELECT) +NahidaProject::SQLite::DataTable NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::Query() { + DataTable result; + + while (true) { + int rc = sqlite3_step(statement); + if (rc == SQLITE_ROW) { + DataRow row; + int columnCount = sqlite3_column_count(statement); + + for (int i = 0; i < columnCount; i++) { + const char* text = reinterpret_cast(sqlite3_column_text(statement, i)); + if (text) { + row.emplace_back(text); + } + else { + row.emplace_back(""); + } + } + result.push_back(std::move(row)); + } + else if (rc == SQLITE_DONE) { + break; + } + else { + throw SQLiteException("Failed to step through results: " + std::string(sqlite3_errmsg(database)), rc); + } + } + + Reset(); + return result; +} + +// 重置预编译语句 +void NahidaProject::SQLite::SQLiteWrapper::PreparedStatement::Reset() { + sqlite3_reset(statement); + sqlite3_clear_bindings(statement); +} +// 创建预编译语句 +std::unique_ptr NahidaProject::SQLite::SQLiteWrapper::PrepareStatement(const std::string& sql) { + CheckConnection(); + return std::make_unique(database, sql); +} + +// 开始事务 +void NahidaProject::SQLite::SQLiteWrapper::BeginTransaction() { + Execute("BEGIN TRANSACTION;"); +} + +// 提交事务 +void NahidaProject::SQLite::SQLiteWrapper::CommitTransaction() { + Execute("COMMIT;"); +} + +// 回滚事务 +void NahidaProject::SQLite::SQLiteWrapper::RollbackTransaction() { + Execute("ROLLBACK;"); +} + +// 获取最后插入的行ID +long long NahidaProject::SQLite::SQLiteWrapper::GetLastInsertId() const { + CheckConnection(); + return sqlite3_last_insert_rowid(database); +} + +// 获取错误信息 +std::string NahidaProject::SQLite::SQLiteWrapper::GetErrorMessage() const { + CheckConnection(); + return std::string(sqlite3_errmsg(database)); +} + +// 检查是否已连接 +bool NahidaProject::SQLite::SQLiteWrapper::IsConnected() const { + return _isConnected; +} + +// 获取 SQLite 版本 +std::string NahidaProject::SQLite::SQLiteWrapper::GetVersion() { + return std::string(sqlite3_libversion()); +} + +// 设置超时时间(毫秒) +void NahidaProject::SQLite::SQLiteWrapper::SetTimeout(int milliseconds) { + CheckConnection(); + int rc = sqlite3_busy_timeout(database, milliseconds); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to set timeout", rc); + } +} + +// 启用/禁用外键约束 +void NahidaProject::SQLite::SQLiteWrapper::EnableForeignKey(bool enable) { + Execute(enable ? "PRAGMA foreign_keys = ON;" : "PRAGMA foreign_keys = OFF;"); +} + +// 打印查询结果(调试用) +void NahidaProject::SQLite::SQLiteWrapper::PrintCurrentTable(const DataTable& table) { + if (table.empty()) { + std::cout << "No data found." << std::endl; + return; + } + + for (const auto& row : table) { + for (size_t i = 0; i < row.size(); ++i) { + std::cout << row[i]; + if (i < row.size() - 1) { + std::cout << " | "; + } + } + std::cout << std::endl; + } +} \ No newline at end of file diff --git a/NahidaProject.Database/Sources/SQLite.h b/NahidaProject.Database/Sources/SQLite.h new file mode 100644 index 0000000..b251c92 --- /dev/null +++ b/NahidaProject.Database/Sources/SQLite.h @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#pragma comment(lib, "winsqlite3.lib") +#else +#include +#endif + +namespace NahidaProject { + namespace SQLite { + class SQLiteException : public std::runtime_error { + public: + explicit SQLiteException(const std::string& message) : std::runtime_error(message) { + + } + + SQLiteException(const std::string& message, int errorCode) : std::runtime_error(message + " (Error code: " + std::to_string(errorCode) + ")") { + + } + }; + + using DataRow = std::vector; + using DataTable = std::vector; + + class SQLiteWrapper { + private: + sqlite3* database; + bool _isConnected; + + void CheckConnection() const; + static int Callback(void* data, int argc, char** argv, char** azColName); + + public: + // 构造函数 + SQLiteWrapper() : database(nullptr), _isConnected(false) {} + + ~SQLiteWrapper() { + Close(); + } + + SQLiteWrapper(const SQLiteWrapper&) = delete; + SQLiteWrapper& operator=(const SQLiteWrapper&) = delete; + + SQLiteWrapper(SQLiteWrapper&& other) noexcept : database(other.database), _isConnected(other._isConnected) { + other.database = nullptr; + other._isConnected = false; + } + + SQLiteWrapper& operator=(SQLiteWrapper&& other) noexcept; + void Connect(const std::string& databasePath); + void Close(); + int Execute(const std::string& sql); + DataTable Query(const std::string& sql); + + class PreparedStatement { + private: + sqlite3_stmt* statement; + sqlite3* database; + + public: + explicit PreparedStatement(sqlite3* database, const std::string& sql) : database(database), statement(nullptr) { + int rc = sqlite3_prepare_v2(database, sql.c_str(), -1, &statement, nullptr); + if (rc != SQLITE_OK) { + throw SQLiteException("Failed to prepare statement: " + std::string(sqlite3_errmsg(database)), rc); + } + } + + ~PreparedStatement() { + if (statement) { + sqlite3_finalize(statement); + } + } + + PreparedStatement(const PreparedStatement&) = delete; + PreparedStatement& operator=(const PreparedStatement&) = delete; + + void BindInteger(int index, int value); + void BindText(int index, const std::string& value); + void BindDouble(int index, double value); + void BindNull(int index); + int Execute(); + DataTable Query(); + void Reset(); + }; + + std::unique_ptr PrepareStatement(const std::string& sql); + void BeginTransaction(); + void CommitTransaction(); + void RollbackTransaction(); + long long GetLastInsertId() const; + std::string GetErrorMessage() const; + bool IsConnected() const; + static std::string GetVersion(); + void SetTimeout(int milliseconds); + void EnableForeignKey(bool enable); + static void PrintCurrentTable(const DataTable& table); + }; + } +} \ No newline at end of file diff --git a/NahidaProject.Database/Tests/ModuleUnitTestFile.cpp b/NahidaProject.Database/Tests/ModuleUnitTestFile.cpp index 14f8355..a251e84 100644 --- a/NahidaProject.Database/Tests/ModuleUnitTestFile.cpp +++ b/NahidaProject.Database/Tests/ModuleUnitTestFile.cpp @@ -1,13 +1,14 @@ #include "..\..\NahidaProject.UnitTest\Sources\NahidaUnitTest.h" #include "..\Sources\ODBC.h" +#include "..\Sources\SQLite.h" Test(ODBCTest) { try { - NahidaProject::Database database; + NahidaProject::ODBC::Database database; // 方式1:使用 ConnectionInfo 结构体 - NahidaProject::ConnectionInfo info; + NahidaProject::ODBC::ConnectionInfo info; info.driver = "SQL Server"; // 根据实际驱动调整 info.server = "localhost"; info.database = "TestDatabase"; @@ -42,7 +43,7 @@ Test(ODBCTest) { */ database.Disconnect(); } - catch (const NahidaProject::ODBCException& e) { + catch (const NahidaProject::ODBC::ODBCException& e) { std::cerr << "ODBC Error: " << e.what() << std::endl; } catch (const std::exception& e) { @@ -50,6 +51,98 @@ Test(ODBCTest) { } } +Test(SQLiteBasicTest) { + try { + NahidaProject::SQLite::SQLiteWrapper db; + + // 连接数据库 + db.Connect("test.db"); + std::cout << "Connected to database. SQLite version: " << NahidaProject::SQLite::SQLiteWrapper::GetVersion() << std::endl; + + // 创建表 + db.Execute(R"( + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT UNIQUE, + age INTEGER + ) + )"); + + // 插入数据 + db.Execute("INSERT INTO users (name, email, age) VALUES ('Alice', 'alice@example.com', 25)"); + db.Execute("INSERT INTO users (name, email, age) VALUES ('Bob', 'bob@example.com', 30)"); + + // 查询数据 + auto results = db.Query("SELECT * FROM users WHERE age > 20"); + std::cout << "Query results:" << std::endl; + NahidaProject::SQLite::SQLiteWrapper::PrintCurrentTable(results); + + // 使用预编译语句插入数据 + auto insertStmt = db.PrepareStatement("INSERT INTO users (name, email, age) VALUES (?, ?, ?)"); + insertStmt->BindText(1, "Charlie"); + insertStmt->BindText(2, "charlie@example.com"); + insertStmt->BindInteger(3, 35); + insertStmt->Execute(); + + // 使用预编译语句查询数据 + auto selectStmt = db.PrepareStatement("SELECT name, age FROM users WHERE age >= ?"); + selectStmt->BindInteger(1, 30); + auto preparedResults = selectStmt->Query(); + std::cout << "\nPrepared statement results:" << std::endl; + NahidaProject::SQLite::SQLiteWrapper::PrintCurrentTable(preparedResults); + + // 更新数据 + int updatedRows = db.Execute("UPDATE users SET age = 26 WHERE name = 'Alice'"); + std::cout << "\nUpdated " << updatedRows << " rows" << std::endl; + + // 删除数据 + int deletedRows = db.Execute("DELETE FROM users WHERE name = 'Bob'"); + std::cout << "Deleted " << deletedRows << " rows" << std::endl; + + // 最后插入的ID + std::cout << "Last insert ID: " << db.GetLastInsertId() << std::endl; + + } + catch (const NahidaProject::SQLite::SQLiteException& e) { + std::cerr << "SQLite Error: " << e.what() << std::endl; + } + catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } +} + +Test(SQLiteTransactionTest) { + try { + NahidaProject::SQLite::SQLiteWrapper db; + db.Connect("test.db"); + + // 开始事务 + db.BeginTransaction(); + + try { + db.Execute("INSERT INTO users (name, email, age) VALUES ('David', 'david@example.com', 40)"); + db.Execute("INSERT INTO users (name, email, age) VALUES ('Eve', 'eve@example.com', 35)"); + + // 提交事务 + db.CommitTransaction(); + std::cout << "Transaction committed successfully" << std::endl; + } + catch (...) { + // 回滚事务 + db.RollbackTransaction(); + std::cout << "Transaction rolled back" << std::endl; + throw; + } + + } + catch (const NahidaProject::SQLite::SQLiteException& e) { + std::cerr << "Transaction Error: " << e.what() << std::endl; + } + + ::remove("test.db"); +} + int main() { RunAllTestCase(); return 0; diff --git a/NahidaProject.Generic/Sources/DynamicLibraryLoader.cpp b/NahidaProject.Generic/Sources/DynamicLibraryLoader.cpp new file mode 100644 index 0000000..23988a2 --- /dev/null +++ b/NahidaProject.Generic/Sources/DynamicLibraryLoader.cpp @@ -0,0 +1,118 @@ +#include "DynamicLibraryLoader.h" + +inline NahidaProject::DynamicLibraryLoader::DynamicLibraryLoader(const std::string& libraryPath) : handle(nullptr), libraryPath(libraryPath), isLoaded(false) { + if (!libraryPath.empty()) { + Load(libraryPath); + } +} + +inline NahidaProject::DynamicLibraryLoader::~DynamicLibraryLoader() { + Unload(); +} + +inline NahidaProject::DynamicLibraryLoader::DynamicLibraryLoader(DynamicLibraryLoader&& other) noexcept : handle(other.handle), libraryPath(std::move(other.libraryPath)), isLoaded(other.isLoaded) { + other.handle = nullptr; + other.isLoaded = false; +} + +inline NahidaProject::DynamicLibraryLoader& NahidaProject::DynamicLibraryLoader::operator=(DynamicLibraryLoader&& other) noexcept { + if (this != &other) { + Unload(); + handle = other.handle; + libraryPath = std::move(other.libraryPath); + isLoaded = other.isLoaded; + + other.handle = nullptr; + other.isLoaded = false; + } + return *this; +} + +inline bool NahidaProject::DynamicLibraryLoader::Load(const std::string& libraryPath) { + if (isLoaded) { + Unload(); + } + + libraryPath = libraryPath; + isLoaded = LoadImplement(libraryPath); + return isLoaded; +} + +inline bool NahidaProject::DynamicLibraryLoader::Unload() { + if (!isLoaded) { + return true; + } + + bool result = UnloadImplement(); + if (result) { + handle = nullptr; + isLoaded = false; + } + return result; +} + + +inline std::string NahidaProject::DynamicLibraryLoader::GetLastError() const { +#ifdef _WIN32 + DWORD errorCode = ::GetLastError(); + if (errorCode == 0) { + return "No error"; + } + + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + LocalFree(messageBuffer); + return message; +#else + const char* error = dlerror(); + return error ? std::string(error) : "No error"; +#endif +} + +// Windows 实现 +#ifdef _WIN32 +inline bool NahidaProject::DynamicLibraryLoader::LoadImplement(const std::string& path) { + handle = LoadLibraryA(path.c_str()); + return handle != nullptr; +} + +inline bool NahidaProject::DynamicLibraryLoader::UnloadImplement() { + if (handle != nullptr) { + return FreeLibrary(handle) != FALSE; + } + return true; +} + +inline void* NahidaProject::DynamicLibraryLoader::GetSymbolImplement(const std::string& symbolName) { + if (handle == nullptr) { + return nullptr; + } + return (void*)GetProcAddress(handle, symbolName.c_str()); +} +#else +// Unix/Linux/macOS 实现 +inline bool NahidaProject::DynamicLibraryLoader::LoadImplement(const std::string& path) { + // RTLD_LAZY: 延迟加载符号 + // RTLD_LOCAL: 符号不对其他库可见 + handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); + return handle != nullptr; +} + +inline bool NahidaProject::DynamicLibraryLoader::UnloadImplement() { + if (handle != nullptr) { + return dlclose(handle) == 0; + } + return true; +} + +inline void* NahidaProject::DynamicLibraryLoader::GetSymbolImplement(const std::string& symbolName) { + if (handle == nullptr) { + return nullptr; + } + dlerror(); // 清除之前的错误 + void* symbol = dlsym(handle, symbolName.c_str()); + return symbol; +} +#endif diff --git a/NahidaProject.Generic/Sources/DynamicLibraryLoader.h b/NahidaProject.Generic/Sources/DynamicLibraryLoader.h new file mode 100644 index 0000000..25020bf --- /dev/null +++ b/NahidaProject.Generic/Sources/DynamicLibraryLoader.h @@ -0,0 +1,53 @@ +#include +#include + +#ifdef _WIN32 +#include +typedef HMODULE LibraryHandle; +#else +#include +typedef void* LibraryHandle; +#endif + +namespace NahidaProject { + class DynamicLibraryLoader { + private: + LibraryHandle handle; + std::string libraryPath; + bool isLoaded; + + public: + explicit DynamicLibraryLoader(const std::string& libraryPath = ""); + ~DynamicLibraryLoader(); + + DynamicLibraryLoader(const DynamicLibraryLoader&) = delete; + DynamicLibraryLoader& operator=(const DynamicLibraryLoader&) = delete; + DynamicLibraryLoader(DynamicLibraryLoader&& other) noexcept; + DynamicLibraryLoader& operator=(DynamicLibraryLoader&& other) noexcept; + + bool Load(const std::string& libraryPath); + bool Unload(); + + template T GetFunction(const std::string& functionName) { + if (!isLoaded) { + throw std::runtime_error("Library not loaded"); + } + + void* symbol = GetSymbolImplement(functionName); + if (symbol == nullptr) { + throw std::runtime_error("Function '" + functionName + "' not found: " + GetLastError()); + } + + return reinterpret_cast(symbol); + } + + bool IsLoaded() const { return isLoaded; } + std::string GetLastError() const; + const std::string& GetLibraryPath() const { return libraryPath; } + + private: + bool LoadImplement(const std::string& path); + bool UnloadImplement(); + void* GetSymbolImplement(const std::string& symbolName); + }; +} diff --git a/NahidaProject.Mathematics/Sources/Complex.cpp b/NahidaProject.Mathematics/Sources/Complex.cpp new file mode 100644 index 0000000..1546e41 --- /dev/null +++ b/NahidaProject.Mathematics/Sources/Complex.cpp @@ -0,0 +1,116 @@ +#include "Complex.h" + +double NahidaProject::Complex::GetReal() const { + return real; +} + +double NahidaProject::Complex::GetImaginary() const { + return imaginary; +} + +void NahidaProject::Complex::SetReal(double r) { + real = r; +} + +void NahidaProject::Complex::SetImaginary(double i) { + imaginary = i; +} + +// 赋值运算符 +NahidaProject::Complex& NahidaProject::Complex::operator=(const Complex& other) { + if (this != &other) { + real = other.real; + imaginary = other.imaginary; + } + return *this; +} + +// 加法运算符 +NahidaProject::Complex NahidaProject::Complex::operator+(const Complex& other) const { + return Complex(real + other.real, imaginary + other.imaginary); +} + +// 减法运算符 +NahidaProject::Complex NahidaProject::Complex::operator-(const Complex& other) const { + return Complex(real - other.real, imaginary - other.imaginary); +} + +// 乘法运算符 +NahidaProject::Complex NahidaProject::Complex::operator*(const Complex& other) const { + return Complex( + real * other.real - imaginary * other.imaginary, + real * other.imaginary + imaginary * other.real + ); +} + +// 除法运算符 +NahidaProject::Complex NahidaProject::Complex::operator/(const Complex& other) const { + double denominator = other.real * other.real + other.imaginary * other.imaginary; + if (denominator == 0) { + throw std::runtime_error("Division by zero complex number"); + } + return Complex( + (real * other.real + imaginary * other.imaginary) / denominator, + (imaginary * other.real - real * other.imaginary) / denominator + ); +} + +// 复合赋值运算符 +NahidaProject::Complex& NahidaProject::Complex::operator+=(const Complex& other) { + real += other.real; + imaginary += other.imaginary; + return *this; +} + +NahidaProject::Complex& NahidaProject::Complex::operator-=(const Complex& other) { + real -= other.real; + imaginary -= other.imaginary; + return *this; +} + +NahidaProject::Complex& NahidaProject::Complex::operator*=(const Complex& other) { + double temp_real = real * other.real - imaginary * other.imaginary; + imaginary = real * other.imaginary + imaginary * other.real; + real = temp_real; + return *this; +} + +NahidaProject::Complex& NahidaProject::Complex::operator/=(const Complex& other) { + *this = *this / other; + return *this; +} + +// 相等比较运算符 +bool NahidaProject::Complex::operator==(const Complex& other) const { + const double epsilon = 1e-10; + return (std::abs(real - other.real) < epsilon) && + (std::abs(imaginary - other.imaginary) < epsilon); +} + +// 不等比较运算符 +bool NahidaProject::Complex::operator!=(const Complex& other) const { + return !(*this == other); +} + +// 共轭复数 +NahidaProject::Complex NahidaProject::Complex::Conjugate() const { + return Complex(real, -imaginary); +} + +// 模长(绝对值) +double NahidaProject::Complex::Modulus() const { + return std::sqrt(real * real + imaginary * imaginary); +} + +// 幅角(弧度) +double NahidaProject::Complex::Argument() const { + return std::atan2(imaginary, real); +} + +// 平方根 +NahidaProject::Complex NahidaProject::Complex::Sqrt() const { + double r = Modulus(); + double theta = Argument(); + double sqrt_r = std::sqrt(r); + return Complex(sqrt_r * std::cos(theta / 2), sqrt_r * std::sin(theta / 2)); +} \ No newline at end of file diff --git a/NahidaProject.Mathematics/Sources/Complex.h b/NahidaProject.Mathematics/Sources/Complex.h new file mode 100644 index 0000000..1a53036 --- /dev/null +++ b/NahidaProject.Mathematics/Sources/Complex.h @@ -0,0 +1,40 @@ +#include +#include +#include + +namespace NahidaProject { + class Complex { + private: + double real; // 实部 + double imaginary; // 虚部 + + public: + Complex(double r = 0.0, double i = 0.0) : real(r), imaginary(i) {} + Complex(const Complex& other) : real(other.real), imaginary(other.imaginary) {} + ~Complex() = default; + + double GetReal() const; + double GetImaginary() const; + void SetReal(double r); + void SetImaginary(double i); + + Complex& operator=(const Complex& other); + Complex operator+(const Complex& other) const; + Complex operator-(const Complex& other) const; + Complex operator*(const Complex& other) const; + Complex operator/(const Complex& other) const; + Complex& operator+=(const Complex& other); + Complex& operator-=(const Complex& other); + Complex& operator*=(const Complex& other); + Complex& operator/=(const Complex& other); + bool operator==(const Complex& other) const; + bool operator!=(const Complex& other) const; + Complex Conjugate() const; + double Modulus() const; + double Argument() const; + Complex Sqrt() const; + }; + // 常用复数常量 + const Complex I(0, 1); // 虚数单位 + const Complex ZERO(0, 0); // 零复数 +} \ No newline at end of file diff --git a/NahidaProject.Mathematics/Sources/Matrix.cpp b/NahidaProject.Mathematics/Sources/Matrix.cpp new file mode 100644 index 0000000..41a049e --- /dev/null +++ b/NahidaProject.Mathematics/Sources/Matrix.cpp @@ -0,0 +1,236 @@ +#include "Matrix.h" + +// 赋值运算符 +NahidaProject::Matrix& NahidaProject::Matrix::operator=(const Matrix& other) { + if (this != &other) { + data = other.data; + rows = other.rows; + column = other.column; + } + return *this; +} + +// 获取矩阵维度 +size_t NahidaProject::Matrix::GetRows() const { return rows; } +size_t NahidaProject::Matrix::GetColumn() const { return column; } + +// 访问元素(带边界检查) +double& NahidaProject::Matrix::At(size_t i, size_t j) { + if (i >= rows || j >= column) { + throw std::out_of_range("Matrix index out of range"); + } + return data[i][j]; +} + +const double& NahidaProject::Matrix::At(size_t i, size_t j) const { + if (i >= rows || j >= column) { + throw std::out_of_range("Matrix index out of range"); + } + return data[i][j]; +} + +// 矩阵加法 +NahidaProject::Matrix NahidaProject::Matrix::operator+(const Matrix& other) const { + if (rows != other.rows || column != other.column) { + throw std::invalid_argument("Matrix dimensions must match for addition"); + } + + Matrix result(rows, column); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + result(i, j) = data[i][j] + other.data[i][j]; + } + } + return result; +} + +// 矩阵减法 +NahidaProject::Matrix NahidaProject::Matrix::operator-(const Matrix& other) const { + if (rows != other.rows || column != other.column) { + throw std::invalid_argument("Matrix dimensions must match for subtraction"); + } + + Matrix result(rows, column); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + result(i, j) = data[i][j] - other.data[i][j]; + } + } + return result; +} + +// 矩阵乘法 +NahidaProject::Matrix NahidaProject::Matrix::operator*(const Matrix& other) const { + if (column != other.rows) { + throw std::invalid_argument("Number of columns in first matrix must equal number of rows in second matrix"); + } + + Matrix result(rows, other.column); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < other.column; ++j) { + for (size_t k = 0; k < column; ++k) { + result(i, j) += data[i][k] * other.data[k][j]; + } + } + } + return result; +} + +// 标量乘法 +NahidaProject::Matrix NahidaProject::Matrix::operator*(double scalar) const { + Matrix result(rows, column); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + result(i, j) = data[i][j] * scalar; + } + } + return result; +} + +// 复合赋值运算符 +NahidaProject::Matrix& NahidaProject::Matrix::operator+=(const Matrix& other) { + *this = *this + other; + return *this; +} + +NahidaProject::Matrix& NahidaProject::Matrix::operator-=(const Matrix& other) { + *this = *this - other; + return *this; +} + +NahidaProject::Matrix& NahidaProject::Matrix::operator*=(const Matrix& other) { + *this = *this * other; + return *this; +} + +NahidaProject::Matrix& NahidaProject::Matrix::operator*=(double scalar) { + *this = *this * scalar; + return *this; +} + +// 比较运算符 +bool NahidaProject::Matrix::operator==(const Matrix& other) const { + if (rows != other.rows || column != other.column) { + return false; + } + + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + if (std::abs(data[i][j] - other.data[i][j]) > 1e-9) { + return false; + } + } + } + return true; +} + +bool NahidaProject::Matrix::operator!=(const Matrix& other) const { + return !(*this == other); +} + +// 创建单位矩阵 +NahidaProject::Matrix NahidaProject::Matrix::Identity(size_t n) { + Matrix result(n, n); + for (size_t i = 0; i < n; ++i) { + result(i, i) = 1.0; + } + return result; +} + +// 创建零矩阵 +NahidaProject::Matrix NahidaProject::Matrix::Zero(size_t rows, size_t cols) { + return Matrix(rows, cols, 0.0); +} + +// 创建全1矩阵 +NahidaProject::Matrix NahidaProject::Matrix::Ones(size_t rows, size_t cols) { + return Matrix(rows, cols, 1.0); +} + +// 转置矩阵 +NahidaProject::Matrix NahidaProject::Matrix::Transpose() const { + Matrix result(column, rows); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + result(j, i) = data[i][j]; + } + } + return result; +} + +// 获取子矩阵(删除第row行和第col列) +NahidaProject::Matrix NahidaProject::Matrix::SubMatrix(size_t row, size_t col) const { + if (rows <= 1 || column <= 1) { + throw std::invalid_argument("Cannot create submatrix from matrix with dimension <= 1"); + } + if (row >= rows || col >= column) { + throw std::out_of_range("Submatrix indices out of range"); + } + + Matrix result(rows - 1, column - 1); + size_t r = 0; + for (size_t i = 0; i < rows; ++i) { + if (i == row) continue; + size_t c = 0; + for (size_t j = 0; j < column; ++j) { + if (j == col) continue; + result(r, c) = data[i][j]; + ++c; + } + ++r; + } + return result; +} + +// 计算行列式(仅适用于方阵) +double NahidaProject::Matrix::Determinant() const { + if (rows != column) { + throw std::invalid_argument("Determinant can only be calculated for square matrices"); + } + + if (rows == 1) { + return data[0][0]; + } + + if (rows == 2) { + return data[0][0] * data[1][1] - data[0][1] * data[1][0]; + } + + double det = 0.0; + for (size_t j = 0; j < column; ++j) { + Matrix sub = SubMatrix(0, j); + double cofactor = ((j % 2 == 0) ? 1 : -1) * data[0][j] * sub.Determinant(); + det += cofactor; + } + return det; +} + +// 判断是否为方阵 +bool NahidaProject::Matrix::IsSquare() const { + return rows == column; +} + +// 判断是否为对称矩阵 +bool NahidaProject::Matrix::IsSymmetric() const { + if (!IsSquare()) return false; + + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + if (std::abs(data[i][j] - data[j][i]) > 1e-9) { + return false; + } + } + } + return true; +} + +// 打印矩阵 +void NahidaProject::Matrix::PrintMatrix(int precision = 4) const { + std::cout << std::fixed << std::setprecision(precision); + for (size_t i = 0; i < rows; ++i) { + for (size_t j = 0; j < column; ++j) { + std::cout << std::setw(precision + 6) << data[i][j] << " "; + } + std::cout << "" << std::endl; + } +} \ No newline at end of file diff --git a/NahidaProject.Mathematics/Sources/Matrix.h b/NahidaProject.Mathematics/Sources/Matrix.h new file mode 100644 index 0000000..3949b0a --- /dev/null +++ b/NahidaProject.Mathematics/Sources/Matrix.h @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +namespace NahidaProject { + class Matrix { + private: + std::vector> data; + size_t rows, column; + + public: + // 构造函数 + Matrix() : rows(0), column(0) {} + + Matrix(size_t r, size_t c) : rows(r), column(c) { + if (r == 0 || c == 0) { + throw std::invalid_argument("Matrix dimensions must be positive"); + } + data.resize(rows, std::vector(column, 0.0)); + } + + Matrix(size_t r, size_t c, double value) : rows(r), column(c) { + if (r == 0 || c == 0) { + throw std::invalid_argument("Matrix dimensions must be positive"); + } + data.resize(rows, std::vector(column, value)); + } + + // 从二维数组构造 + Matrix(const std::vector>& matrixData) { + if (matrixData.empty() || matrixData[0].empty()) { + throw std::invalid_argument("Matrix data cannot be empty"); + } + + rows = matrixData.size(); + column = matrixData[0].size(); + + // 检查所有行的列数是否一致 + for (const auto& row : matrixData) { + if (row.size() != column) { + throw std::invalid_argument("All rows must have the same number of columns"); + } + } + + data = matrixData; + } + + // 拷贝构造函数 + Matrix(const Matrix& other) : data(other.data), rows(other.rows), column(other.column) {} + + // 赋值运算符 + Matrix& operator=(const Matrix& other); + // 获取矩阵维度 + size_t GetRows() const { return rows; } + size_t GetColumn() const { return column; } + + // 访问元素(带边界检查) + double& At(size_t i, size_t j); + const double& At(size_t i, size_t j) const; + + // 访问元素(不带边界检查,用于性能关键场景) + double& operator()(size_t i, size_t j) { return data[i][j]; } + const double& operator()(size_t i, size_t j) const { return data[i][j]; } + + // 矩阵加法 + Matrix operator+(const Matrix& other) const; + Matrix operator-(const Matrix& other) const; + Matrix operator*(const Matrix& other) const; + Matrix operator*(double scalar) const; + Matrix& operator+=(const Matrix& other); + Matrix& operator-=(const Matrix& other); + Matrix& operator*=(const Matrix& other); + Matrix& operator*=(double scalar); + bool operator==(const Matrix& other) const; + bool operator!=(const Matrix& other) const; + static Matrix Identity(size_t n); + static Matrix Zero(size_t rows, size_t cols); + static Matrix Ones(size_t rows, size_t cols); + Matrix Transpose() const; + Matrix SubMatrix(size_t row, size_t col) const; + double Determinant() const; + bool IsSquare() const; + bool IsSymmetric() const; + void PrintMatrix(int precision = 4) const; + }; +} \ No newline at end of file diff --git a/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp b/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp index dce21b7..a7d5ff0 100644 --- a/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp +++ b/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp @@ -1,6 +1,8 @@ #include "..\..\NahidaProject.UnitTest\Sources\NahidaUnitTest.h" #include "..\Sources\BigInteger.h" +#include "..\Sources\Complex.h" +#include "..\Sources\Matrix.h" #include "..\Sources\RandomGenerator.h" #include "..\Sources\Statistics.h" @@ -15,6 +17,67 @@ Test(BigIntegerTest) { AssertEqual((std::string)(bigInteger1 * bigInteger2), "6463933789952062400179139910666398683694552102531682740773920"); } +Test(ComplexTest) { + +} + +Test(MatrixTest) { + try { + // 创建矩阵 + NahidaProject::Matrix A({ + {1, 2, 3}, + {4, 5, 6} + }); + + NahidaProject::Matrix B({ + {7, 8}, + {9, 10}, + {11, 12} + }); + + std::cout << "Matrix A:" << std::endl; + A.PrintMatrix(); + + std::cout << "Matrix B:" << std::endl; + B.PrintMatrix(); + + // 矩阵乘法 + NahidaProject::Matrix C = A * B; + std::cout << "A * B:" << std::endl; + C.PrintMatrix(); + + // 创建方阵并计算行列式 + NahidaProject::Matrix D({ + {2, -3, 1}, + {2, 0, -1}, + {1, 4, 5} + }); + + std::cout << "Matrix D:" << std::endl; + std::cout << D << std::endl; + + std::cout << "Determinant of D: " << D.determinant() << std::endl; + + // 单位矩阵 + NahidaProject::Matrix I = NahidaProject::Matrix::identity(3); + std::cout << "Identity matrix:" << std::endl; + std::cout << I << std::endl; + + // 矩阵转置 + std::cout << "Transpose of A:" << std::endl; + std::cout << A.transpose() << std::endl; + + // 标量乘法 + NahidaProject::Matrix E = 2.5 * A; + std::cout << "2.5 * A:" << std::endl; + E.PrintMatrix(); + + } + catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } +} + Test(RandomGeneratorTest) { NahidaProject::RandomGenerator random; -- Gitee From c997c237ca9fabd009e54ece464da884ab58e95d Mon Sep 17 00:00:00 2001 From: HeZongLun <13425468+hezonglun@user.noreply.gitee.com> Date: Sat, 8 Nov 2025 20:52:29 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=A4=8D?= =?UTF-8?q?=E6=95=B0=E7=B1=BB=E5=9E=8B=E5=92=8C=E7=9F=A9=E9=98=B5=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ConsoleColor.cpp | 164 ++++++++++++++++++ NahidaProject.Console/Sources/ConsoleColor.h | 132 ++++++++++++++ .../Sources/TerminalColor.cpp | 103 ----------- NahidaProject.Console/Sources/TerminalColor.h | 41 ----- .../Tests/ModuleUnitTestFile.cpp | 68 ++++++-- NahidaProject.Mathematics/Sources/Complex.cpp | 19 ++ NahidaProject.Mathematics/Sources/Complex.h | 9 +- NahidaProject.Mathematics/Sources/Matrix.h | 6 +- .../Tests/ModuleUnitTestFile.cpp | 69 ++++++-- README.md | 4 +- 10 files changed, 442 insertions(+), 173 deletions(-) create mode 100644 NahidaProject.Console/Sources/ConsoleColor.cpp create mode 100644 NahidaProject.Console/Sources/ConsoleColor.h delete mode 100644 NahidaProject.Console/Sources/TerminalColor.cpp delete mode 100644 NahidaProject.Console/Sources/TerminalColor.h diff --git a/NahidaProject.Console/Sources/ConsoleColor.cpp b/NahidaProject.Console/Sources/ConsoleColor.cpp new file mode 100644 index 0000000..dfb4727 --- /dev/null +++ b/NahidaProject.Console/Sources/ConsoleColor.cpp @@ -0,0 +1,164 @@ +#include "ConsoleColor.h" + +// 获取ANSI转义序列 +std::string NahidaProject::ConsoleColor::GetANSICode() const { + if (!useColors || !IsTerminal()) { + return ""; + } + + std::string code = "\033["; + bool hasCode = false; + + if (textStyle != RESET) { + code += std::to_string(textStyle); + hasCode = true; + } + + if (foregroundColor != DEFAULT) { + if (hasCode) code += ";"; + code += std::to_string(30 + foregroundColor); + hasCode = true; + } + + if (backgroundColor != DEFAULT) { + if (hasCode) code += ";"; + code += std::to_string(40 + backgroundColor); + hasCode = true; + } + + if (!hasCode) { + code += "0"; + } + + code += "m"; + return code; +} + +// 检查是否为终端输出 +bool NahidaProject::ConsoleColor::IsTerminal() const { + return isatty(fileno(stdout)); +} + +// Windows平台的颜色设置 +void NahidaProject::ConsoleColor::SetWindowsColor() const { +#ifdef _WIN32 + if (!useColors || !IsTerminal()) return; + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) return; + + WORD color = 0; + + // 设置前景色 + switch (foregroundColor) { + case RED: color |= FOREGROUND_RED; break; + case GREEN: color |= FOREGROUND_GREEN; break; + case YELLOW: color |= FOREGROUND_RED | FOREGROUND_GREEN; break; + case BLUE: color |= FOREGROUND_BLUE; break; + case MAGENTA: color |= FOREGROUND_RED | FOREGROUND_BLUE; break; + case CYAN: color |= FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case WHITE: color |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case DEFAULT: color |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + default: break; + } + + // 设置背景色 + switch (backgroundColor) { + case RED: color |= BACKGROUND_RED; break; + case GREEN: color |= BACKGROUND_GREEN; break; + case YELLOW: color |= BACKGROUND_RED | BACKGROUND_GREEN; break; + case BLUE: color |= BACKGROUND_BLUE; break; + case MAGENTA: color |= BACKGROUND_RED | BACKGROUND_BLUE; break; + case CYAN: color |= BACKGROUND_GREEN | BACKGROUND_BLUE; break; + case WHITE: color |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break; + default: break; + } + + // 设置样式 + if (textStyle == BOLD) { + color |= FOREGROUND_INTENSITY; + } + + SetConsoleTextAttribute(hConsole, color); +#endif +} + +// 重置Windows颜色 +void NahidaProject::ConsoleColor::ResetWindowsColor() const { +#ifdef _WIN32 + if (!useColors || !IsTerminal()) return; + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole != INVALID_HANDLE_VALUE) { + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + } +#endif +} + +// 设置前景色 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::SetForegroundColor(Color color) { + foregroundColor = static_cast(color); + return *this; +} + +// 设置背景色 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::SetBackgroundColor(Color color) { + backgroundColor = static_cast(color); + return *this; +} + +// 设置文本样式 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::SetStyle(Style s) { + textStyle = static_cast(s); + return *this; +} + +// 设置粗体 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::SetBold() { + return SetStyle(BOLD); +} + +// 设置斜体 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::SetItalic() { + return SetStyle(ITALIC); +} + +// 设置下划线 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::Underline() { + return SetStyle(UNDERLINE); +} + +// 启用/禁用颜色输出 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::EnableColors(bool enable) { + useColors = enable; + return *this; +} + +// 重置所有格式 - 链式方法 +NahidaProject::ConsoleColor& NahidaProject::ConsoleColor::Reset() { + foregroundColor = DEFAULT; + backgroundColor = DEFAULT; + textStyle = RESET; + return *this; +} + +// 静态方法创建预定义样式 +NahidaProject::ConsoleColor NahidaProject::ConsoleColor::Error() { + return ConsoleColor().SetForegroundColor(RED).SetBold(); +} + +NahidaProject::ConsoleColor NahidaProject::ConsoleColor::Warning() { + return ConsoleColor().SetForegroundColor(YELLOW).SetBold(); +} + +NahidaProject::ConsoleColor NahidaProject::ConsoleColor::Success() { + return ConsoleColor().SetForegroundColor(GREEN).SetBold(); +} + +NahidaProject::ConsoleColor NahidaProject::ConsoleColor::Information() { + return ConsoleColor().SetForegroundColor(BLUE).SetBold(); +} + + NahidaProject::ConsoleColor NahidaProject::ConsoleColor::Debug() { + return ConsoleColor().SetForegroundColor(CYAN); +} \ No newline at end of file diff --git a/NahidaProject.Console/Sources/ConsoleColor.h b/NahidaProject.Console/Sources/ConsoleColor.h new file mode 100644 index 0000000..e5ae613 --- /dev/null +++ b/NahidaProject.Console/Sources/ConsoleColor.h @@ -0,0 +1,132 @@ +#include +#include + +#ifdef _WIN32 +#include +#include +#define isatty _isatty +#define fileno _fileno +#else +#include +#endif + +namespace NahidaProject { + class ConsoleColor { + public: + // 颜色枚举 + enum Color { + BLACK = 0, + RED = 1, + GREEN = 2, + YELLOW = 3, + BLUE = 4, + MAGENTA = 5, + CYAN = 6, + WHITE = 7, + DEFAULT = 9 + }; + + // 样式枚举 + enum Style { + RESET = 0, + BOLD = 1, + DIM = 2, + ITALIC = 3, + UNDERLINE = 4, + BLINK = 5, + REVERSE = 7, + STRIKETHROUGH = 9 + }; + + private: + int foregroundColor; + int backgroundColor; + int textStyle; + bool useColors; + + // 获取ANSI转义序列 + std::string GetANSICode() const; + bool IsTerminal() const; + void SetWindowsColor() const; + void ResetWindowsColor() const; + + public: + // 构造函数 + ConsoleColor() : foregroundColor(DEFAULT), backgroundColor(DEFAULT), textStyle(RESET), useColors(true) { + + } + + // 复制构造函数 + ConsoleColor(const ConsoleColor& other) : foregroundColor(other.foregroundColor), backgroundColor(other.backgroundColor), textStyle(other.textStyle), useColors(other.useColors) { + + } + + ConsoleColor& SetForegroundColor(Color color); + ConsoleColor& SetBackgroundColor(Color color); + ConsoleColor& SetStyle(Style s); + ConsoleColor& SetBold(); + ConsoleColor& SetItalic(); + ConsoleColor& Underline(); + ConsoleColor& EnableColors(bool enable); + ConsoleColor& Reset(); + + // 输出操作符重载 + + // 特化处理std::endl等操纵器 + ConsoleColor& operator<<(std::ostream& (*manip)(std::ostream&)) { +#ifdef _WIN32 + ResetWindowsColor(); + std::cout << manip; + SetWindowsColor(); +#else + std::cout << "\033[0m" << manip; + std::cout << GetANSICode(); +#endif + return *this; + } + + // 特化处理std::flush + ConsoleColor& operator<<(std::ios_base& (*manip)(std::ios_base&)) { + std::cout << manip; + return *this; + } + + template ConsoleColor& operator<<(const T& value) { +#ifdef _WIN32 + SetWindowsColor(); + std::cout << value; + ResetWindowsColor(); +#else + std::cout << GetANSICode() << value << "\033[0m"; +#endif + return *this; + } + + // 快捷颜色方法 + ConsoleColor& Red() { return SetForegroundColor(RED); } + ConsoleColor& Green() { return SetForegroundColor(GREEN); } + ConsoleColor& Blue() { return SetForegroundColor(BLUE); } + ConsoleColor& Yellow() { return SetForegroundColor(YELLOW); } + ConsoleColor& Magenta() { return SetForegroundColor(MAGENTA); } + ConsoleColor& Cyan() { return SetForegroundColor(CYAN); } + ConsoleColor& White() { return SetForegroundColor(WHITE); } + ConsoleColor& Black() { return SetForegroundColor(BLACK); } + + // 快捷背景色方法 + ConsoleColor& BackgroundRed() { return SetBackgroundColor(RED); } + ConsoleColor& BackgroundGreen() { return SetBackgroundColor(GREEN); } + ConsoleColor& BackgroundBlue() { return SetBackgroundColor(BLUE); } + ConsoleColor& BackgroundYellow() { return SetBackgroundColor(YELLOW); } + ConsoleColor& BackgroundMagenta() { return SetBackgroundColor(MAGENTA); } + ConsoleColor& BackgroundCyan() { return SetBackgroundColor(CYAN); } + ConsoleColor& BackgroundWhite() { return SetBackgroundColor(WHITE); } + ConsoleColor& BackgroundBlack() { return SetBackgroundColor(BLACK); } + + // 静态方法创建预定义样式 + static ConsoleColor Error(); + static ConsoleColor Warning(); + static ConsoleColor Success(); + static ConsoleColor Information(); + static ConsoleColor Debug(); + }; +} \ No newline at end of file diff --git a/NahidaProject.Console/Sources/TerminalColor.cpp b/NahidaProject.Console/Sources/TerminalColor.cpp deleted file mode 100644 index d617f2b..0000000 --- a/NahidaProject.Console/Sources/TerminalColor.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright (c) 2025 HeZongLun -NahidaProject is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan -PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -*/ - -#include "TerminalColor.h" - - -HANDLE handleOfConsole = GetStdHandle(STD_OUTPUT_HANDLE); - -std::ostream& NahidaProject::TerminalColor::Reset(std::ostream& os) { -#if defined(_WIN32) || defined(WIN32) -#include -#endif - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::Red(std::ostream& os) { -#if defined(_WIN32) || defined(WIN32) - SetConsoleTextAttribute(handleOfConsole, 0x0004 | 0x0006); -#else - os << "\033[01m"; -#endif - return os; -} - -std::ostream& NahidaProject::TerminalColor::Orange(std::ostream& os) { -#if defined(_WIN32) || defined(WIN32) - SetConsoleTextAttribute(handleOfConsole, 0x0004 | 0x0006); -#else - os << "\033[03m"; -#endif - return os; -} - -std::ostream& NahidaProject::TerminalColor::Yellow(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0004 | 0x0002); - return os; -} - -std::ostream& NahidaProject::TerminalColor::Green(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0002); - return os; -} - -std::ostream& NahidaProject::TerminalColor::Cyan(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::Blue(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::Purple(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x000D); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundRed(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0040 | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundOrange(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0040 | 0x0020 | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundYellow(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x00EE | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundGreen(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0022); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundCyan(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0020 | 0x0010 | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundBlue(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x0010 | 0x0004 | 0x0002 | 0x0001); - return os; -} - -std::ostream& NahidaProject::TerminalColor::BackgroundPurple(std::ostream& os) { - SetConsoleTextAttribute(handleOfConsole, 0x0008 | 0x00DD | 0x0004 | 0x0002 | 0x0001); - return os; -} diff --git a/NahidaProject.Console/Sources/TerminalColor.h b/NahidaProject.Console/Sources/TerminalColor.h deleted file mode 100644 index 75e96d5..0000000 --- a/NahidaProject.Console/Sources/TerminalColor.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright (c) 2025 HeZongLun -NahidaProject is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan -PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -*/ - -#pragma once - -#include -#if defined(_WIN32) || defined(WIN32) -#include -#endif - -namespace NahidaProject { - class __declspec(dllexport) TerminalColor { - public: - static std::ostream& Reset(std::ostream& os); - static std::ostream& Red(std::ostream& os); - static std::ostream& Orange(std::ostream& os); - static std::ostream& Yellow(std::ostream& os); - static std::ostream& Green(std::ostream& os); - static std::ostream& Cyan(std::ostream& os); - static std::ostream& Blue(std::ostream& os); - static std::ostream& Purple(std::ostream& os); - - static std::ostream& BackgroundRed(std::ostream& os); - static std::ostream& BackgroundOrange(std::ostream& os); - static std::ostream& BackgroundYellow(std::ostream& os); - static std::ostream& BackgroundGreen(std::ostream& os); - static std::ostream& BackgroundCyan(std::ostream& os); - static std::ostream& BackgroundBlue(std::ostream& os); - static std::ostream& BackgroundPurple(std::ostream& os); - }; -} diff --git a/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp b/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp index 2d87198..e144710 100644 --- a/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp +++ b/NahidaProject.Console/Tests/ModuleUnitTestFile.cpp @@ -2,8 +2,8 @@ #include "..\Sources\ChooseDialog.h" #include "..\Sources\CommandLineParser.h" +#include "..\Sources\ConsoleColor.h" #include "..\Sources\TablePrint.h" -#include "..\Sources\TerminalColor.h" Test(ChooseDialogTest) { NahidaProject::ChooseDialog::CheckboxMenu cbm(L"Choose 1", 5); @@ -93,20 +93,68 @@ Test(CommandLineParserTest) { parser.Parse({"help"}); } +Test(ConsoleColorTest) { + NahidaProject::ConsoleColor cc; + + cc.Red() << "红色文字" << std::endl; + cc.Green() << "绿色文字" << std::endl; + cc.Blue() << "蓝色文字" << std::endl; + cc.Yellow() << "黄色文字" << std::endl; + cc.Magenta() << "洋红色文字" << std::endl; + cc.Cyan() << "青色文字" << std::endl; + cc.White() << "白色文字" << std::endl; + cc.Black() << "黑色文字(可能看不清)" << std::endl; + + std::cout << std::endl; + + // 背景色输出 + cc.BackgroundRed() << "红色背景" << std::endl; + cc.BackgroundGreen() << "绿色背景" << std::endl; + cc.BackgroundBlue() << "蓝色背景" << std::endl; + cc.BackgroundYellow().BackgroundBlack() << "黄色背景+黑色文字" << std::endl; + + std::cout << std::endl; + + // 样式输出 + cc.SetBold() << "粗体文字" << std::endl; + cc.SetItalic() << "斜体文字" << std::endl; + cc.Underline() << "下划线文字" << std::endl; + + std::cout << std::endl; + + // 组合使用 - 链式调用的魅力 + cc.Red().BackgroundYellow().SetBold() << "红色文字 + 黄色背景 + 粗体" << std::endl; + cc.Green().Underline().SetItalic() << "绿色文字 + 下划线 + 斜体" << std::endl; + cc.Blue().BackgroundWhite().SetBold() << "蓝色文字 + 白色背景 + 粗体" << std::endl; + + std::cout << "" << std::endl; + + cc.Success() << "成功信息" << std::endl; + cc.Error() << "错误信息" << std::endl; + cc.Warning() << "警告信息" << std::endl; + cc.Information() << "提示信息" << std::endl; + cc.Debug() << "调试信息" << std::endl; + + std::cout << "" << std::endl; + + cc.SetForegroundColor(NahidaProject::ConsoleColor::MAGENTA).SetBackgroundColor(NahidaProject::ConsoleColor::CYAN).SetStyle(NahidaProject::ConsoleColor::BOLD).Underline() << "复杂的链式调用示例" << std::endl; + + cc.Reset(); + // 测试禁用颜色 + std::cout << std::endl << "禁用颜色后的输出:" << std::endl; + cc.EnableColors(false).Red().SetBold() << "这段文字应该没有颜色" << std::endl; + + // 重新启用颜色 + cc.EnableColors(true).Green() << "重新启用颜色" << std::endl; + cc.Reset(); + +} + Test(TablePrintTest) { NahidaProject::TablePrint tablePrint({ "Column1", "Column2", "Column3", "Column4", "Column5"}, { { "Data1", "Data2", "Data3", "Data4", "Data5"}, {"Data6", "Data7", "Data8", "Data9", "Data10"}}); tablePrint.PrintTable(); } -Test(TerminalColorTest) { - std::cout << "我神州," << "称华夏。" << NahidaProject::TerminalColor::Yellow << "山川美," << NahidaProject::TerminalColor::Green << "可入画。" << NahidaProject::TerminalColor::Reset << std::endl; - std::cout << "黄河奔," << "长江涌。" << "长城长," << "珠峰耸。" << std::endl; - std::cout << NahidaProject::TerminalColor::BackgroundRed << "台湾岛," << "隔海峡。" << "与大陆," << "是一家。" << NahidaProject::TerminalColor::Reset << std::endl; - std::cout << NahidaProject::TerminalColor::Cyan << "各民族," << NahidaProject::TerminalColor::Yellow << "齐奋发。" << NahidaProject::TerminalColor::Red << "争朝夕," << "兴中华。" << NahidaProject::TerminalColor::Reset << std::endl; -} - - - int main() { RunAllTestCase(); return 0; diff --git a/NahidaProject.Mathematics/Sources/Complex.cpp b/NahidaProject.Mathematics/Sources/Complex.cpp index 1546e41..8b30f5e 100644 --- a/NahidaProject.Mathematics/Sources/Complex.cpp +++ b/NahidaProject.Mathematics/Sources/Complex.cpp @@ -113,4 +113,23 @@ NahidaProject::Complex NahidaProject::Complex::Sqrt() const { double theta = Argument(); double sqrt_r = std::sqrt(r); return Complex(sqrt_r * std::cos(theta / 2), sqrt_r * std::sin(theta / 2)); +} + +NahidaProject::Complex NahidaProject::Complex::Zero(){ + return Complex(0, 0); +} + +NahidaProject::Complex NahidaProject::Complex::I(){ + return Complex(0, 1); +} + +std::string NahidaProject::Complex::ToString() { + std::stringstream oss; + if (this->imaginary >= 0) { + oss << this->real << " + " << this->imaginary << "i"; + } + else { + oss << this->real << " - " << std::abs(this->imaginary) << "i"; + } + return oss.str(); } \ No newline at end of file diff --git a/NahidaProject.Mathematics/Sources/Complex.h b/NahidaProject.Mathematics/Sources/Complex.h index 1a53036..a5af9a9 100644 --- a/NahidaProject.Mathematics/Sources/Complex.h +++ b/NahidaProject.Mathematics/Sources/Complex.h @@ -1,6 +1,7 @@ #include #include #include +#include namespace NahidaProject { class Complex { @@ -33,8 +34,10 @@ namespace NahidaProject { double Modulus() const; double Argument() const; Complex Sqrt() const; + + static Complex Zero(); + static Complex I(); + + std::string ToString(); }; - // 常用复数常量 - const Complex I(0, 1); // 虚数单位 - const Complex ZERO(0, 0); // 零复数 } \ No newline at end of file diff --git a/NahidaProject.Mathematics/Sources/Matrix.h b/NahidaProject.Mathematics/Sources/Matrix.h index 3949b0a..5602000 100644 --- a/NahidaProject.Mathematics/Sources/Matrix.h +++ b/NahidaProject.Mathematics/Sources/Matrix.h @@ -53,8 +53,8 @@ namespace NahidaProject { // 赋值运算符 Matrix& operator=(const Matrix& other); // 获取矩阵维度 - size_t GetRows() const { return rows; } - size_t GetColumn() const { return column; } + size_t GetRows() const; + size_t GetColumn() const; // 访问元素(带边界检查) double& At(size_t i, size_t j); @@ -83,6 +83,6 @@ namespace NahidaProject { double Determinant() const; bool IsSquare() const; bool IsSymmetric() const; - void PrintMatrix(int precision = 4) const; + void PrintMatrix(int precision) const; }; } \ No newline at end of file diff --git a/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp b/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp index a7d5ff0..2b7629a 100644 --- a/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp +++ b/NahidaProject.Mathematics/Tests/ModuleUnitTestFile.cpp @@ -18,7 +18,56 @@ Test(BigIntegerTest) { } Test(ComplexTest) { + try { + // 创建复数 + NahidaProject::Complex c1(3, 4); + NahidaProject::Complex c2(1, -2); + + std::cout << "复数 c1: " << c1.ToString() << std::endl; + std::cout << "复数 c2: " << c2.ToString() << std::endl; + + // 基本运算 + std::cout << "\n=== 基本运算 ===" << std::endl; + std::cout << "c1 + c2 = " << (c1 + c2).ToString() << std::endl; + std::cout << "c1 - c2 = " << (c1 - c2).ToString() << std::endl; + std::cout << "c1 * c2 = " << (c1 * c2).ToString() << std::endl; + std::cout << "c1 / c2 = " << (c1 / c2).ToString() << std::endl; + + // 复合赋值运算 + NahidaProject::Complex c3 = c1; + c3 += c2; + std::cout << "\nc1 += c2 后的结果: " << c3.ToString() << std::endl; + + // 其他函数 + std::cout << "\n=== 其他函数 ===" << std::endl; + std::cout << "c1 的共轭: " << c1.Conjugate().ToString() << std::endl; + std::cout << "c1 的模长: " << c1.Modulus() << std::endl; + std::cout << "c1 的幅角: " << c1.Argument() << " 弧度" << std::endl; + std::cout << "c1 的平方根: " << c1.Sqrt().ToString() << std::endl; + + // 比较运算 + std::cout << "\n=== 比较运算 ===" << std::endl; + NahidaProject::Complex c4(3, 4); + std::cout << "c1 == c4: " << (c1 == c4 ? "true" : "false") << std::endl; + std::cout << "c1 != c2: " << (c1 != c2 ? "true" : "false") << std::endl; + + // 标量运算 + std::cout << "\n=== 标量运算 ===" << std::endl; + std::cout << "5 + c1 = " << (c1 + 5).ToString() << std::endl; + std::cout << "5 * c1 = " << (c1 * 5).ToString() << std::endl; + + // 特殊常量 + std::cout << "\n=== 特殊常量 ===" << std::endl; + std::cout << "虚数单位 i: " << NahidaProject::Complex::I().ToString() << std::endl; + std::cout << "零复数: " << NahidaProject::Complex::Zero().ToString() << std::endl; + + // 验证 i^2 = -1 + std::cout << "\n验证 i^2 = -1: " << (NahidaProject::Complex::I() * NahidaProject::Complex::Zero()).ToString() << std::endl; + } + catch (const std::exception& e) { + std::cerr << "错误: " << e.what() << std::endl; + } } Test(MatrixTest) { @@ -36,15 +85,15 @@ Test(MatrixTest) { }); std::cout << "Matrix A:" << std::endl; - A.PrintMatrix(); + A.PrintMatrix(4); std::cout << "Matrix B:" << std::endl; - B.PrintMatrix(); + B.PrintMatrix(4); // 矩阵乘法 NahidaProject::Matrix C = A * B; std::cout << "A * B:" << std::endl; - C.PrintMatrix(); + C.PrintMatrix(4); // 创建方阵并计算行列式 NahidaProject::Matrix D({ @@ -54,23 +103,21 @@ Test(MatrixTest) { }); std::cout << "Matrix D:" << std::endl; - std::cout << D << std::endl; - - std::cout << "Determinant of D: " << D.determinant() << std::endl; + D.PrintMatrix(4); // 单位矩阵 - NahidaProject::Matrix I = NahidaProject::Matrix::identity(3); + NahidaProject::Matrix I = NahidaProject::Matrix::Identity(3); std::cout << "Identity matrix:" << std::endl; - std::cout << I << std::endl; + I.PrintMatrix(4); // 矩阵转置 std::cout << "Transpose of A:" << std::endl; - std::cout << A.transpose() << std::endl; + A.Transpose().PrintMatrix(4); // 标量乘法 - NahidaProject::Matrix E = 2.5 * A; + NahidaProject::Matrix E = A * 2.5; std::cout << "2.5 * A:" << std::endl; - E.PrintMatrix(); + E.PrintMatrix(4); } catch (const std::exception& e) { diff --git a/README.md b/README.md index a3f1f6a..0b25736 100644 --- a/README.md +++ b/README.md @@ -199,14 +199,14 @@ NahidaProject 涓殑浠g爜锛屽彲浠ョ洿鎺ュ紩鍏ヤ娇鐢ㄣ備絾鏄垜浠洿寤鸿缂 - 绫诲懡鍚嶄笌鍑芥暟鍛藉悕涓庢枃浠跺懡鍚嶏細甯曟柉鍗″懡鍚嶆硶锛 `class TestClass { }; void TestFunction() { } TestClass.cpp TestClass.h`銆 - 鍙互涓嶅啓娉ㄩ噴锛屼絾鏄竴瀹氳鏈夋枃妗c - 鎸夌収妯″潡鐢ㄩ斿仛濂藉垎绫伙紝骞舵斁缃湪瀵瑰簲鐨勫瓙妯″潡涓 -- 濡傛灉浣犵殑绫诲绗笁鏂瑰簱杩涜浜嗗皝瑁咃紝璇峰皢鍏舵斁缃湪 `NahidaProject-ThirdParty` 瀛愰」鐩笅銆 +- 濡傛灉浣犵殑绫诲绗笁鏂瑰簱杩涜浜嗗皝瑁咃紝璇峰皢鍏舵斁缃湪 `NahidaProject.ThirdParty` 瀛愰」鐩笅銆 - 灏芥儏鍙戞尌浣犵殑鍒涢犲姏銆 鍒繕浜嗙洰褰曞竷灞 ``` 鈹溾攢NahidaProject.XXX 鈹 鈹溾攢Sources ---> 鏀剧疆婧愪唬鐮 -鈹 鈹斺攢Tests ---> 鏀剧疆鍗曞厓娴嬭瘯浠g爜 +鈹 鈹斺攢Tests ---> 鏀剧疆鍗曞厓娴嬭瘯浠g爜 ``` 褰撶劧涔熶笉瑕佸繕浜嗗湪 [NahidaProject.cpp](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Generic/Sources/NahidaProject.cpp) 涓暀涓嬩綘鐨 Gitee ID銆 -- Gitee From 7f3d692c3b1b0e553cc06d5e920da21a5fdfeb73 Mon Sep 17 00:00:00 2001 From: HeZongLun <13425468+hezonglun@user.noreply.gitee.com> Date: Sat, 8 Nov 2025 21:01:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0b25736..69e90bc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # NahidaProject -璀﹀憡锛氭枃涓儴鍒嗛摼鎺ユ殏鏃跺け鏁 - [![star](https://gitee.com/hezonglun/nahida-project/badge/star.svg?theme=dark)](https://gitee.com/hezonglun/nahida-project/stargazers) [![fork](https://gitee.com/hezonglun/nahida-project/badge/fork.svg?theme=dark)](https://gitee.com/hezonglun/nahida-project/members) @@ -28,13 +26,13 @@ NahidaProject 鍗佸垎灏忓阀锛屽嵈鍖呭惈浜嗗父鐢ㄧ殑妯″潡锛 ## 鍙傝冩枃妗 -- [寮鍙戞枃妗(https://gitee.com/nahida-project-develop/NahidaProject.Documents) +- [寮鍙戞枃妗(https://gitee.com/NahidaProjectDevelop/NahidaProject.Documents) ## 鍊煎緱浠嬬粛鐨勬ā鍧 ### 甯冮殕杩囨护鍣ㄧ被 -[BloomFilter.h](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Generic/Sources/BloomFilter.h) 鎻愪緵甯冮殕杩囨护鍣ㄧ殑瀹炵幇銆 +[BloomFilter.h](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.Generic/Sources/BloomFilter.h) 鎻愪緵甯冮殕杩囨护鍣ㄧ殑瀹炵幇銆 ```cpp #include "BloomFilter.h" @@ -63,7 +61,7 @@ int main(int argc, char** argv) { ``` ### 鏃ュ織绫 -**[Logger.h](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Generic/Sources/Logger.h)** 鏄竴涓紓姝ユ墽琛岀殑鏃ュ織缁勪欢銆 +**[Logger.h](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.Generic/Sources/Logger.h)** 鏄竴涓紓姝ユ墽琛岀殑鏃ュ織缁勪欢銆 鍙互蹇熸彁渚涙棩蹇楁敮鎸併 @@ -98,10 +96,10 @@ int main(int argc, char** argv) { ### 鍗曞厓娴嬭瘯绫 -**[NahidaUnitTest.h](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.UnitTest/Sources/NahidaUnitTest.h)** 鏄竴涓畝鍗曠殑鍗曞厓娴嬭瘯妗嗘灦锛孨ahidaProject 涓殑鎵鏈夌粍浠剁敤瀹冨啓鍗曞厓娴嬭瘯浠g爜銆 +**[NahidaUnitTest.h](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.UnitTest/Sources/NahidaUnitTest.h)** 鏄竴涓畝鍗曠殑鍗曞厓娴嬭瘯妗嗘灦锛孨ahidaProject 涓殑鎵鏈夌粍浠剁敤瀹冨啓鍗曞厓娴嬭瘯浠g爜銆 ```cpp -#include "NahidaTest.h" +#include "NahidaUnitTest.h" Test(TestPoint1) { AssertEqual(1 + 1, 2); // 娴嬭瘯閫氳繃 @@ -125,17 +123,14 @@ int main(int argc, char** argv) { NahidaProject 鍚勪釜妯″潡鐨勬簮浠g爜锛岀紪璇戜細鐢熸垚锛 - 鍙湁绗﹀彿閾炬帴鐨 NahidaProject.XXX.lib銆 + - 鍙湁绗﹀彿閾炬帴鐨 NahidaProject.XXX.lib銆 - 鍖呭惈鍑芥暟瀹氫箟鐨 NahidaProject.XXX.IMPLEMENT.lib銆 + - 鍖呭惈鍑芥暟瀹氫箟鐨 NahidaProject.XXX.IMPLEMENT.lib銆 - 鍖呭惈鍑芥暟瀹氫箟鐨 NahidaProject.XXX.dll銆 - - 鐢ㄤ簬娴嬭瘯鐨 NahidaProject.XXXTest.exe銆 + - 鍖呭惈鍑芥暟瀹氫箟鐨 NahidaProject.XXX.dll銆 -- [Documents](https://gitee.com/hezonglun/nahida-project/tree/master/Documents) + - 鐢ㄤ簬娴嬭瘯鐨 NahidaProject.XXXTest.exe銆 - NahidaProject 鐨勪娇鐢ㄦ枃妗c ## 缂栬瘧涓庡畨瑁 @@ -151,7 +146,7 @@ NahidaProject 涓殑浠g爜锛屽彲浠ョ洿鎺ュ紩鍏ヤ娇鐢ㄣ備絾鏄垜浠洿寤鸿缂 寤鸿浣跨敤 Microsoft Visual Studio Community 鑷甫鐨 CMake銆 -鎻愮ず锛氬湪 Windows 涓婄紪璇 NahidaProject 娌℃湁鏍囧噯绛旀锛屽彲浠ュ灏濊瘯鍑犵宸ュ叿閾俱 +鎻愮ず锛氱紪璇 NahidaProject 娌℃湁鏍囧噯绛旀锛屽彲浠ュ灏濊瘯鍑犵宸ュ叿閾俱 ### 缂栬瘧姝ラ @@ -190,13 +185,18 @@ NahidaProject 涓殑浠g爜锛屽彲浠ョ洿鎺ュ紩鍏ヤ娇鐢ㄣ備絾鏄垜浠洿寤鸿缂 - 鎺ㄩ佸埌浣犲悕涓嬬殑鏈粨搴撳垎鏀 - 鏂板缓 Pull Request锛岃姹傚悎骞躲 -寮鍙戝皬閿﹀泭锛氭渶濂界殑鏍锋湰鏄 [CowSay.cpp](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Other/Sources/CowSay.cpp) 鍜 [CowSay.h](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Other/Sources/CowSay.h) +寮鍙戝皬閿﹀泭锛氭渶濂界殑鏍锋湰鏄 [CowSay.cpp](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.Other/Sources/CowSay.cpp) 鍜 [CowSay.h](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.Other/Sources/CowSay.h) ### 璐$尞瑙勫畾 姝ら」鐩崄鍒嗚嚜鐢憋紝浣嗗湪寮濮嬩綘鐨勮础鐚墠锛屾湁涓浜涜瀹氳閬靛畧锛 -- 绫诲懡鍚嶄笌鍑芥暟鍛藉悕涓庢枃浠跺懡鍚嶏細甯曟柉鍗″懡鍚嶆硶锛 `class TestClass { }; void TestFunction() { } TestClass.cpp TestClass.h`銆 +- 绫诲懡鍚嶄笌鍑芥暟鍛藉悕涓庢枃浠跺懡鍚嶏細甯曟柉鍗″懡鍚嶆硶 + - 渚嬪锛 + - `class TestClass { };` + - `void TestFunction() { }` + - ` TestClass.cpp` + - ` TestClass.h` - 鍙互涓嶅啓娉ㄩ噴锛屼絾鏄竴瀹氳鏈夋枃妗c - 鎸夌収妯″潡鐢ㄩ斿仛濂藉垎绫伙紝骞舵斁缃湪瀵瑰簲鐨勫瓙妯″潡涓 - 濡傛灉浣犵殑绫诲绗笁鏂瑰簱杩涜浜嗗皝瑁咃紝璇峰皢鍏舵斁缃湪 `NahidaProject.ThirdParty` 瀛愰」鐩笅銆 @@ -206,10 +206,10 @@ NahidaProject 涓殑浠g爜锛屽彲浠ョ洿鎺ュ紩鍏ヤ娇鐢ㄣ備絾鏄垜浠洿寤鸿缂 ``` 鈹溾攢NahidaProject.XXX 鈹 鈹溾攢Sources ---> 鏀剧疆婧愪唬鐮 -鈹 鈹斺攢Tests ---> 鏀剧疆鍗曞厓娴嬭瘯浠g爜 +鈹 鈹斺攢Tests ---> 鏀剧疆鍗曞厓娴嬭瘯浠g爜 ``` -褰撶劧涔熶笉瑕佸繕浜嗗湪 [NahidaProject.cpp](https://gitee.com/hezonglun/nahida-project/blob/master/NahidaProject.Generic/Sources/NahidaProject.cpp) 涓暀涓嬩綘鐨 Gitee ID銆 +褰撶劧涔熶笉瑕佸繕浜嗗湪 [NahidaProject.cpp](https://gitee.com/NahidaProjectDevelop/NahidaProject/blob/master/NahidaProject.Generic/Sources/NahidaProject.cpp) 涓暀涓嬩綘鐨 Gitee ID銆 ## 寮鏀炬簮浠g爜璁稿彲涓庤鏄 -- Gitee