From 22cd329eee1cba019b48dec93f024746cb3761af Mon Sep 17 00:00:00 2001 From: SHISH82 Date: Sat, 1 Nov 2025 02:42:18 +0300 Subject: [PATCH 1/6] plugins --- include/plugin_api.h | 23 +++++++++++++++++++++++ plugins/funccos.cpp | 14 ++++++++++++++ plugins/funcdeg.cpp | 16 ++++++++++++++++ plugins/funcln.cpp | 17 +++++++++++++++++ plugins/funcsin.cpp | 14 ++++++++++++++ plugins/funcsqrt.cpp | 17 +++++++++++++++++ 6 files changed, 101 insertions(+) create mode 100644 include/plugin_api.h create mode 100644 plugins/funccos.cpp create mode 100644 plugins/funcdeg.cpp create mode 100644 plugins/funcln.cpp create mode 100644 plugins/funcsin.cpp create mode 100644 plugins/funcsqrt.cpp diff --git a/include/plugin_api.h b/include/plugin_api.h new file mode 100644 index 0000000..3914e20 --- /dev/null +++ b/include/plugin_api.h @@ -0,0 +1,23 @@ +#pragma once +#include + +namespace calc::plugin { + +#if defined(_WIN32) +# define CALC_PLUGIN_EXPORT extern "C" __declspec(dllexport) +#else +# define CALC_PLUGIN_EXPORT extern "C" +#endif + + struct FunctionDescriptor { + uint32_t abi_version; + const char* name; + double (*invoke)(double); + }; + + using RegisterFunction = bool(*)(FunctionDescriptor&); + + inline constexpr uint32_t kAbiVersion = 1; + inline constexpr const char* kRegistrationSymbol = "calc_register"; + +} diff --git a/plugins/funccos.cpp b/plugins/funccos.cpp new file mode 100644 index 0000000..ec6ada8 --- /dev/null +++ b/plugins/funccos.cpp @@ -0,0 +1,14 @@ +#include +#include "plugin_api.h" + +namespace { + constexpr double kPi = 3.14159265358979323846; + double cos_deg(double d) { return std::cos(d * kPi / 180.0); } +} + +CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { + out.abi_version = calc::plugin::kAbiVersion; + out.name = "cos"; + out.invoke = &cos_deg; + return true; +} diff --git a/plugins/funcdeg.cpp b/plugins/funcdeg.cpp new file mode 100644 index 0000000..e333a3f --- /dev/null +++ b/plugins/funcdeg.cpp @@ -0,0 +1,16 @@ +#include +#include "plugin_api.h" + +namespace { + constexpr double kPi = 3.14159265358979323846; + double rad2deg(double r) { + return r * 180.0 / kPi; + } +} + +CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { + out.abi_version = calc::plugin::kAbiVersion; + out.name = "deg"; + out.invoke = &rad2deg; + return true; +} diff --git a/plugins/funcln.cpp b/plugins/funcln.cpp new file mode 100644 index 0000000..bc63b75 --- /dev/null +++ b/plugins/funcln.cpp @@ -0,0 +1,17 @@ +#include +#include +#include "plugin_api.h" + +namespace { + double myln(double x) { + if (x <= 0.0) throw std::domain_error("ln: x <= 0"); + return std::log(x); + } +} + +CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { + out.abi_version = calc::plugin::kAbiVersion; + out.name = "ln"; + out.invoke = &myln; + return true; +} diff --git a/plugins/funcsin.cpp b/plugins/funcsin.cpp new file mode 100644 index 0000000..fd47bf2 --- /dev/null +++ b/plugins/funcsin.cpp @@ -0,0 +1,14 @@ +#include +#include "plugin_api.h" + +namespace { + constexpr double kPi = 3.14159265358979323846; + double sin_deg(double d) { return std::sin(d * kPi / 180.0); } +} + +CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { + out.abi_version = calc::plugin::kAbiVersion; + out.name = "sin"; + out.invoke = &sin_deg; + return true; +} diff --git a/plugins/funcsqrt.cpp b/plugins/funcsqrt.cpp new file mode 100644 index 0000000..79f345d --- /dev/null +++ b/plugins/funcsqrt.cpp @@ -0,0 +1,17 @@ +#include +#include +#include "plugin_api.h" + +namespace { + double mysqrt(double x) { + if (x < 0.0) throw std::domain_error("sqrt: x < 0"); + return std::sqrt(x); + } +} + +CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { + out.abi_version = calc::plugin::kAbiVersion; + out.name = "sqrt"; + out.invoke = &mysqrt; + return true; +} From be65a45eed5660e92f91a2175d4f965801280380 Mon Sep 17 00:00:00 2001 From: SHISH82 Date: Sat, 1 Nov 2025 22:42:27 +0300 Subject: [PATCH 2/6] plug-ins(fixed)+core --- CMakeLists.txt | 90 +++++++++++++++++++++++++++++++++++++ include/calc/eval.hpp | 60 +++++++++++++++++++++++++ include/calc/parser.hpp | 35 +++++++++++++++ include/calc/plugin_api.h | 29 ++++++++++++ include/calc/tokenizer.hpp | 34 ++++++++++++++ plugins/funccos.cpp | 19 ++++---- plugins/funcln.cpp | 19 ++++---- plugins/funcsin.cpp | 19 ++++---- plugins/funcsqrt.cpp | 19 ++++---- src/eval.cpp | 78 ++++++++++++++++++++++++++++++++ src/parser.cpp | 91 ++++++++++++++++++++++++++++++++++++++ src/tokenizer.cpp | 77 ++++++++++++++++++++++++++++++++ 12 files changed, 534 insertions(+), 36 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 include/calc/eval.hpp create mode 100644 include/calc/parser.hpp create mode 100644 include/calc/plugin_api.h create mode 100644 include/calc/tokenizer.hpp create mode 100644 src/eval.cpp create mode 100644 src/parser.cpp create mode 100644 src/tokenizer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b6e7e79 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.16) +project(DynamicCalculator LANGUAGES CXX) + + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + + +set(PROJECT_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include") + + +add_executable(calc + src/main.cpp + src/tokenizer.cpp + src/parser.cpp + src/eval.cpp + src/plugins.cpp +) + +target_include_directories(calc PRIVATE "${PROJECT_INCLUDE_DIR}") + + +if(UNIX AND NOT APPLE) + target_link_libraries(calc PRIVATE dl) +endif() + + +foreach(cfg IN ITEMS Debug Release RelWithDebInfo MinSizeRel) + set_target_properties(calc PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${cfg} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") +endforeach() + + +set(PLUGINS_OUT "${CMAKE_BINARY_DIR}/plugins") +file(MAKE_DIRECTORY "${PLUGINS_OUT}") + +function(configure_plugin target source) + add_library(${target} SHARED ${source}) + target_include_directories(${target} PRIVATE "${PROJECT_INCLUDE_DIR}") + + + set_target_properties(${target} PROPERTIES + PREFIX "" + RUNTIME_OUTPUT_DIRECTORY "${PLUGINS_OUT}" + LIBRARY_OUTPUT_DIRECTORY "${PLUGINS_OUT}" + ARCHIVE_OUTPUT_DIRECTORY "${PLUGINS_OUT}" + ) + + + foreach(cfg IN ITEMS Debug Release RelWithDebInfo MinSizeRel) + set_target_properties(${target} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${cfg} "${PLUGINS_OUT}" + LIBRARY_OUTPUT_DIRECTORY_${cfg} "${PLUGINS_OUT}" + ARCHIVE_OUTPUT_DIRECTORY_${cfg} "${PLUGINS_OUT}" + ) + endforeach() + + + if(WIN32) + set_target_properties(${target} PROPERTIES SUFFIX ".dll") + elseif(APPLE) + set_target_properties(${target} PROPERTIES SUFFIX ".dylib") + else() + set_target_properties(${target} PROPERTIES SUFFIX ".so") + endif() + + + add_dependencies(calc ${target}) +endfunction() + + +configure_plugin(funcsin "${CMAKE_SOURCE_DIR}/plugins/funcsin.cpp") +configure_plugin(funccos "${CMAKE_SOURCE_DIR}/plugins/funccos.cpp") +configure_plugin(funcsqrt "${CMAKE_SOURCE_DIR}/plugins/funcsqrt.cpp") +configure_plugin(funcln "${CMAKE_SOURCE_DIR}/plugins/funcln.cpp") + + +option(ENABLE_ASAN "Enable AddressSanitizer for debug builds" OFF) +if(ENABLE_ASAN AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU")) + message(STATUS "AddressSanitizer enabled") + target_compile_options(calc PRIVATE -fsanitize=address -fno-omit-frame-pointer) + target_link_options(calc PRIVATE -fsanitize=address) + foreach(p funcsin funccos funcsqrt funcln) + target_compile_options(${p} PRIVATE -fsanitize=address -fno-omit-frame-pointer) + target_link_options(${p} PRIVATE -fsanitize=address) + endforeach() +endif() diff --git a/include/calc/eval.hpp b/include/calc/eval.hpp new file mode 100644 index 0000000..8c0eefa --- /dev/null +++ b/include/calc/eval.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "calc/tokenizer.hpp" +#include "calc/parser.hpp" +#include "calc/plugins.hpp" +#include +#include +#include +#include + +namespace calc { + + struct ICommand { + virtual ~ICommand() = default; + virtual void execute(std::vector& stack) const = 0; + }; + + class PushLiteralCommand : public ICommand { + public: + explicit PushLiteralCommand(double v); + void execute(std::vector& stack) const override; + private: + double v_; + }; + + class UnaryCommand : public ICommand { + public: + explicit UnaryCommand(std::function f); + void execute(std::vector& stack) const override; + private: + std::function f_; + }; + + class BinaryCommand : public ICommand { + public: + explicit BinaryCommand(std::function f); + void execute(std::vector& stack) const override; + private: + std::function f_; + }; + + class CommandBuilder { + public: + CommandBuilder(const FunctionRegistry& r, const OperatorTable& o); + std::vector> build(const std::vector& rpn) const; + private: + const FunctionRegistry& reg_; + const OperatorTable& ops_; + }; + + class Calculator { + public: + explicit Calculator(FunctionRegistry r); + double evaluate(const std::string& expr) const; + void print_functions(std::ostream& os) const; + + private: + FunctionRegistry reg_; + }; + +} // namespace calc diff --git a/include/calc/parser.hpp b/include/calc/parser.hpp new file mode 100644 index 0000000..d72209d --- /dev/null +++ b/include/calc/parser.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "calc/tokenizer.hpp" +#include +#include +#include +#include +#include + +namespace calc { + + struct OperatorInfo { + int precedence; + bool right_assoc; + int arity; + std::function binary; + std::function unary; + }; + + class OperatorTable { + public: + OperatorTable(); + const OperatorInfo& get(const std::string& k) const; + bool contains(const std::string& k) const; + private: + std::map tbl; + }; + + class RpnCompiler { + public: + std::vector compile(const std::vector& tokens); + private: + static bool is_unary(const std::optional& prev, const Token& tok); + }; + +} // namespace calc diff --git a/include/calc/plugin_api.h b/include/calc/plugin_api.h new file mode 100644 index 0000000..df711a0 --- /dev/null +++ b/include/calc/plugin_api.h @@ -0,0 +1,29 @@ +#pragma once +#include + +namespace calc::plugin { + +#if defined(_WIN32) +#define CALC_API extern "C" __declspec(dllexport) +#define CALC_CALL __cdecl +#else +#define CALC_API extern "C" +#define CALC_CALL +#endif + + using InvokeFn = double (CALC_CALL *)(double); + + struct FunctionDescriptor { + std::uint32_t abi_version; + const char* name; + InvokeFn invoke; + }; + + using RegisterFunction = bool (CALC_CALL *)(FunctionDescriptor&); + + inline constexpr std::uint32_t kAbiVersion = 1; + inline constexpr const char* kRegistrationSymbol = "calc_register"; + + static_assert(sizeof(InvokeFn) == sizeof(void*), "InvokeFn must be a plain function pointer"); + +} // namespace calc::plugin diff --git a/include/calc/tokenizer.hpp b/include/calc/tokenizer.hpp new file mode 100644 index 0000000..2a2b790 --- /dev/null +++ b/include/calc/tokenizer.hpp @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +namespace calc { + + enum class TokenType { + Number, + Operator, + Function, + LeftParen, + RightParen + }; + + struct Token { + TokenType type; + std::string text; + double number{}; + }; + + class Tokenizer { + public: + explicit Tokenizer(std::string s); + std::vector tokenize() const; + + private: + std::pair parse_number(std::size_t start) const; + std::pair parse_id(std::size_t start) const; + + std::string s_; + }; + +} // namespace calc diff --git a/plugins/funccos.cpp b/plugins/funccos.cpp index ec6ada8..6fdb221 100644 --- a/plugins/funccos.cpp +++ b/plugins/funccos.cpp @@ -1,14 +1,17 @@ +#include "calc/plugin_api.h" #include -#include "plugin_api.h" +using namespace calc::plugin; -namespace { - constexpr double kPi = 3.14159265358979323846; - double cos_deg(double d) { return std::cos(d * kPi / 180.0); } + +static double CALC_CALL f(double x_deg) { + constexpr long double PI = 3.141592653589793238462643383279502884L; + long double rad = (long double)x_deg * (PI / 180.0L); + return std::cos((double)rad); } -CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { - out.abi_version = calc::plugin::kAbiVersion; - out.name = "cos"; - out.invoke = &cos_deg; +CALC_API bool CALC_CALL calc_register(FunctionDescriptor& d) { + d.abi_version = kAbiVersion; + d.name = "cos"; + d.invoke = &f; return true; } diff --git a/plugins/funcln.cpp b/plugins/funcln.cpp index bc63b75..1357e09 100644 --- a/plugins/funcln.cpp +++ b/plugins/funcln.cpp @@ -1,17 +1,16 @@ +#include "calc/plugin_api.h" #include #include -#include "plugin_api.h" +using namespace calc::plugin; -namespace { - double myln(double x) { - if (x <= 0.0) throw std::domain_error("ln: x <= 0"); - return std::log(x); - } +static double CALC_CALL f(double x) { + if (x <= 0) throw std::domain_error("ln: x <= 0"); + return std::log(x); } -CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { - out.abi_version = calc::plugin::kAbiVersion; - out.name = "ln"; - out.invoke = &myln; +CALC_API bool CALC_CALL calc_register(FunctionDescriptor& d) { + d.abi_version = kAbiVersion; + d.name = "ln"; + d.invoke = &f; return true; } diff --git a/plugins/funcsin.cpp b/plugins/funcsin.cpp index fd47bf2..729eb28 100644 --- a/plugins/funcsin.cpp +++ b/plugins/funcsin.cpp @@ -1,14 +1,17 @@ +#include "calc/plugin_api.h" #include -#include "plugin_api.h" +using namespace calc::plugin; -namespace { - constexpr double kPi = 3.14159265358979323846; - double sin_deg(double d) { return std::sin(d * kPi / 180.0); } + +static double CALC_CALL f(double x_deg) { + constexpr long double PI = 3.141592653589793238462643383279502884L; + long double rad = (long double)x_deg * (PI / 180.0L); + return std::sin((double)rad); } -CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { - out.abi_version = calc::plugin::kAbiVersion; - out.name = "sin"; - out.invoke = &sin_deg; +CALC_API bool CALC_CALL calc_register(FunctionDescriptor& d) { + d.abi_version = kAbiVersion; + d.name = "sin"; + d.invoke = &f; return true; } diff --git a/plugins/funcsqrt.cpp b/plugins/funcsqrt.cpp index 79f345d..3b68e11 100644 --- a/plugins/funcsqrt.cpp +++ b/plugins/funcsqrt.cpp @@ -1,17 +1,16 @@ +#include "calc/plugin_api.h" #include #include -#include "plugin_api.h" +using namespace calc::plugin; -namespace { - double mysqrt(double x) { - if (x < 0.0) throw std::domain_error("sqrt: x < 0"); - return std::sqrt(x); - } +static double CALC_CALL f(double x) { + if (x < 0) throw std::domain_error("sqrt: x < 0"); + return std::sqrt(x); } -CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { - out.abi_version = calc::plugin::kAbiVersion; - out.name = "sqrt"; - out.invoke = &mysqrt; +CALC_API bool CALC_CALL calc_register(FunctionDescriptor& d) { + d.abi_version = kAbiVersion; + d.name = "sqrt"; + d.invoke = &f; return true; } diff --git a/src/eval.cpp b/src/eval.cpp new file mode 100644 index 0000000..66bdbf8 --- /dev/null +++ b/src/eval.cpp @@ -0,0 +1,78 @@ +#include "calc/eval.hpp" +#include "calc/tokenizer.hpp" + +namespace calc { + +PushLiteralCommand::PushLiteralCommand(double v) : v_(v) {} +void PushLiteralCommand::execute(std::vector& stack) const { stack.push_back(v_); } + +UnaryCommand::UnaryCommand(std::function f) : f_(std::move(f)) {} +void UnaryCommand::execute(std::vector& stack) const { + if (stack.empty()) throw std::runtime_error("Не хватает операндов"); + double a = stack.back(); stack.pop_back(); + stack.push_back(f_(a)); +} + +BinaryCommand::BinaryCommand(std::function f) : f_(std::move(f)) {} +void BinaryCommand::execute(std::vector& stack) const { + if (stack.size() < 2) throw std::runtime_error("Не хватает операндов"); + double b = stack.back(); stack.pop_back(); + double a = stack.back(); stack.pop_back(); + stack.push_back(f_(a,b)); +} + +CommandBuilder::CommandBuilder(const FunctionRegistry& r, const OperatorTable& o) : reg_(r), ops_(o) {} + +std::vector> CommandBuilder::build(const std::vector& rpn) const { + std::vector> cmds; + cmds.reserve(rpn.size()); + for (const auto& t : rpn) { + switch (t.type) { + case TokenType::Number: + cmds.push_back(std::make_unique(t.number)); + break; + case TokenType::Operator: { + const auto& op = ops_.get(t.text); + if (op.arity == 1) cmds.push_back(std::make_unique(op.unary)); + else if (op.arity == 2) cmds.push_back(std::make_unique(op.binary)); + else throw std::runtime_error("Неподдерживаемая арность оператора"); + break; + } + case TokenType::Function: { + auto fn = reg_.get_function(t.text); + cmds.push_back(std::make_unique(std::move(fn))); + break; + } + default: + throw std::runtime_error("Неожиданный токен"); + } + } + return cmds; +} + +Calculator::Calculator(FunctionRegistry r) : reg_(std::move(r)) {} + +double Calculator::evaluate(const std::string& expr) const { + Tokenizer tz(expr); + auto tokens = tz.tokenize(); + OperatorTable ops; + RpnCompiler cp; + auto rpn = cp.compile(tokens); + CommandBuilder bld(reg_, ops); + auto cmds = bld.build(rpn); + std::vector st; + st.reserve(cmds.size()); + for (auto& c : cmds) c->execute(st); + if (st.size() != 1) throw std::runtime_error("Неверное выражение"); + return st.front(); +} + +void Calculator::print_functions(std::ostream& os) const { + if (reg_.empty()) { os << " Плагиновые функции не загружены\n"; return; } + os << " Доступные функции: "; + auto n = reg_.names(); + for (std::size_t i = 0; i < n.size(); ++i) { if (i) os << ", "; os << n[i]; } + os << '\n'; +} + +} // namespace calc diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..b5bcb64 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,91 @@ +#include "calc/parser.hpp" +#include +#include +#include + +namespace calc { + +OperatorTable::OperatorTable() { + tbl.emplace("+", OperatorInfo{10, false, 2, [](double a,double b){return a+b;}, {}}); + tbl.emplace("-", OperatorInfo{10, false, 2, [](double a,double b){return a-b;}, {}}); + tbl.emplace("*", OperatorInfo{20, false, 2, [](double a,double b){return a*b;}, {}}); + tbl.emplace("/", OperatorInfo{20, false, 2, [](double a,double b){ if (b==0.0) throw std::runtime_error("Деление на ноль"); return a/b; }, {}}); + tbl.emplace("^", OperatorInfo{30, true , 2, [](double a,double b){return std::pow(a,b);}, {}}); + tbl.emplace("u-",OperatorInfo{40, true , 1, {}, [](double x){return -x;}}); + tbl.emplace("u+",OperatorInfo{40, true , 1, {}, [](double x){return x;}}); +} + +const OperatorInfo& OperatorTable::get(const std::string& k) const { + auto it = tbl.find(k); + if (it == tbl.end()) throw std::runtime_error("Неизвестный оператор: " + k); + return it->second; +} +bool OperatorTable::contains(const std::string& k) const { return tbl.count(k) != 0; } + +std::vector RpnCompiler::compile(const std::vector& tokens) { + std::vector out; + std::vector ops; + const OperatorTable opt; + std::optional prev; + for (std::size_t i = 0; i < tokens.size(); ++i) { + Token tok = tokens[i]; + switch (tok.type) { + case TokenType::Number: + out.push_back(tok); prev = tok; break; + case TokenType::Function: + ops.push_back(tok); prev = tok; break; + case TokenType::Operator: { + std::string key = tok.text; + if (is_unary(prev, tok)) { key = tok.text == "-" ? "u-" : "u+"; tok.text = key; } + const auto& cur = opt.get(key); + while (!ops.empty()) { + const Token& top = ops.back(); + if (top.type == TokenType::Function) { out.push_back(top); ops.pop_back(); continue; } + if (top.type == TokenType::Operator) { + const auto& ti = opt.get(top.text); + bool higher = (!cur.right_assoc && ti.precedence >= cur.precedence) || + ( cur.right_assoc && ti.precedence > cur.precedence); + if (higher) { out.push_back(top); ops.pop_back(); continue; } + } + break; + } + ops.push_back(tok); prev = tok; break; + } + case TokenType::LeftParen: + ops.push_back(tok); prev = tok; break; + case TokenType::RightParen: { + bool found = false; + while (!ops.empty()) { + Token top = ops.back(); ops.pop_back(); + if (top.type == TokenType::LeftParen) { found = true; break; } + out.push_back(top); + } + if (!found) throw std::runtime_error("Скобки не совпадают"); + if (!ops.empty() && ops.back().type == TokenType::Function) { + out.push_back(ops.back()); ops.pop_back(); + } + prev = tok; break; + } + } + } + while (!ops.empty()) { + Token top = ops.back(); ops.pop_back(); + if (top.type == TokenType::LeftParen || top.type == TokenType::RightParen) throw std::runtime_error("Скобки не совпадают"); + out.push_back(top); + } + return out; +} + +bool RpnCompiler::is_unary(const std::optional& prev, const Token& tok) { + if (tok.text != "-" && tok.text != "+") return false; + if (!prev.has_value()) return true; + switch (prev->type) { + case TokenType::Operator: + case TokenType::LeftParen: + return true; + default: + return false; + } +} + +} // namespace calc diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp new file mode 100644 index 0000000..8a24801 --- /dev/null +++ b/src/tokenizer.cpp @@ -0,0 +1,77 @@ +#include "calc/tokenizer.hpp" +#include +#include +#include + +namespace calc { + +Tokenizer::Tokenizer(std::string s) : s_(std::move(s)) {} + +std::vector Tokenizer::tokenize() const { + std::vector t; + std::size_t i = 0; + while (i < s_.size()) { + char ch = s_[i]; + + if (std::isspace((unsigned char)ch)) { ++i; continue; } + + if (std::isdigit((unsigned char)ch) || ch == '.') { + auto [num, next] = parse_number(i); + t.push_back(Token{TokenType::Number, "", num}); + i = next; continue; + } + + if (std::isalpha((unsigned char)ch)) { + auto [id, next] = parse_id(i); + std::string name = id; + std::transform(name.begin(), name.end(), name.begin(), + [](unsigned char c){ return char(std::tolower(c)); }); + + std::size_t j = next; + while (j < s_.size() && std::isspace((unsigned char)s_[j])) ++j; + if (j < s_.size() && s_[j] == '(') { + t.push_back(Token{TokenType::Function, name, 0.0}); + } else { + throw std::runtime_error("Ожидался вызов функции: " + name + "(...)"); + } + i = next; continue; + } + + switch (ch) { + case '+': case '-': case '*': case '/': case '^': + t.push_back(Token{TokenType::Operator, std::string(1, ch), 0.0}); ++i; break; + case '(': + t.push_back(Token{TokenType::LeftParen, "(", 0.0}); ++i; break; + case ')': + t.push_back(Token{TokenType::RightParen, ")", 0.0}); ++i; break; + default: + throw std::runtime_error(std::string("Неожиданный символ: ") + ch); + } + } + return t; +} + +std::pair Tokenizer::parse_number(std::size_t start) const { + std::size_t pos = start; bool dot = false; + while (pos < s_.size()) { + char ch = s_[pos]; + if (std::isdigit((unsigned char)ch)) { ++pos; continue; } + if (ch == '.' && !dot) { dot = true; ++pos; continue; } + break; + } + std::string v = s_.substr(start, pos - start); + if (v.empty() || v == ".") throw std::runtime_error("Некорректное число"); + return {std::stod(v), pos}; +} + +std::pair Tokenizer::parse_id(std::size_t start) const { + std::size_t pos = start; + while (pos < s_.size()) { + char ch = s_[pos]; + if (std::isalnum((unsigned char)ch) || ch == '_') { ++pos; continue; } + break; + } + return {s_.substr(start, pos - start), pos}; +} + +} // namespace calc From 886376b16b89319bf2e859decb4c301c4654bdca Mon Sep 17 00:00:00 2001 From: Sunnix718 <104847800+SHISH82@users.noreply.github.com> Date: Mon, 3 Nov 2025 22:47:39 +0300 Subject: [PATCH 3/6] Delete include/plugin_api.h --- include/plugin_api.h | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 include/plugin_api.h diff --git a/include/plugin_api.h b/include/plugin_api.h deleted file mode 100644 index 3914e20..0000000 --- a/include/plugin_api.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include - -namespace calc::plugin { - -#if defined(_WIN32) -# define CALC_PLUGIN_EXPORT extern "C" __declspec(dllexport) -#else -# define CALC_PLUGIN_EXPORT extern "C" -#endif - - struct FunctionDescriptor { - uint32_t abi_version; - const char* name; - double (*invoke)(double); - }; - - using RegisterFunction = bool(*)(FunctionDescriptor&); - - inline constexpr uint32_t kAbiVersion = 1; - inline constexpr const char* kRegistrationSymbol = "calc_register"; - -} From a08d4af7ee892db44a55ff501b069ab5ca31e1af Mon Sep 17 00:00:00 2001 From: Sunnix718 <104847800+SHISH82@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:02:03 +0300 Subject: [PATCH 4/6] Delete plugins/funcdeg.cpp --- plugins/funcdeg.cpp | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 plugins/funcdeg.cpp diff --git a/plugins/funcdeg.cpp b/plugins/funcdeg.cpp deleted file mode 100644 index e333a3f..0000000 --- a/plugins/funcdeg.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include "plugin_api.h" - -namespace { - constexpr double kPi = 3.14159265358979323846; - double rad2deg(double r) { - return r * 180.0 / kPi; - } -} - -CALC_PLUGIN_EXPORT bool calc_register(calc::plugin::FunctionDescriptor& out) { - out.abi_version = calc::plugin::kAbiVersion; - out.name = "deg"; - out.invoke = &rad2deg; - return true; -} From d377163e3dc9dadba108914715205f59b80e5395 Mon Sep 17 00:00:00 2001 From: SHISH82 Date: Mon, 3 Nov 2025 23:08:37 +0300 Subject: [PATCH 5/6] core+plugins --- include/calc/plugins.hpp | 55 +++++++++++++++ src/main.cpp | 85 ++++++++++++++++++++++ src/plugins.cpp | 148 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 include/calc/plugins.hpp create mode 100644 src/main.cpp create mode 100644 src/plugins.cpp diff --git a/include/calc/plugins.hpp b/include/calc/plugins.hpp new file mode 100644 index 0000000..bc65a84 --- /dev/null +++ b/include/calc/plugins.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace calc { + + class FunctionRegistry { + public: + using UnaryFunction = std::function; + void add_function(std::string name, UnaryFunction fn); + bool contains(const std::string& name) const; + UnaryFunction get_function(const std::string& name) const; + bool empty() const noexcept; + std::vector names() const; + + static std::string normalize(std::string s); + + private: + std::map functions_; + }; + + class DynamicLibrary { + public: + explicit DynamicLibrary(const fs::path& path); + ~DynamicLibrary(); + DynamicLibrary(DynamicLibrary&&) noexcept; + DynamicLibrary& operator=(DynamicLibrary&&) noexcept; + + void* symbol(const char* name) const; + + private: + void* handle_{}; + fs::path path_; + void swap(DynamicLibrary& other) noexcept; + }; + + class PluginLoader { + public: + explicit PluginLoader(fs::path dir); + void load(FunctionRegistry& registry); + + private: + fs::path dir_; + std::vector> libs_; + }; + +} // namespace calc diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e643a73 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +#include "calc/eval.hpp" +#include "calc/plugins.hpp" + +namespace fs = std::filesystem; + +static const char* platform_ext() { +#if defined(_WIN32) + return ".dll"; +#elif defined(__APPLE__) + return ".dylib"; +#else + return ".so"; +#endif +} + +int main(int argc, char* argv[]) { + try { + calc::FunctionRegistry reg; + + + std::vector dirs; + dirs.push_back(fs::current_path() / "plugins"); + if (argc > 0 && argv && argv[0]) { + try { + fs::path exe = fs::weakly_canonical(fs::path(argv[0])); + dirs.push_back(exe.parent_path() / "plugins"); + } catch (...) {} + } + + for (auto& d : dirs) { + try { d = fs::weakly_canonical(d); } catch (...) {} + } + std::sort(dirs.begin(), dirs.end()); + dirs.erase(std::unique(dirs.begin(), dirs.end()), dirs.end()); + + std::vector> keep_alive; + bool any_loaded = false; + std::optional last_err; + + for (const auto& d : dirs) { + try { + auto loader = std::make_unique(d); + loader->load(reg); + keep_alive.push_back(std::move(loader)); + any_loaded = true; + break; + } catch (const std::exception& ex) { + last_err = ex.what(); + } + } + if (!any_loaded && last_err) { + std::cerr << "[Внимание] " << *last_err << '\n'; + } + + calc::Calculator calc(std::move(reg)); + std::cout << "Калькулятор (динамические плагины " << platform_ext() << ")\n"; + calc.print_functions(std::cout); + std::cout << "Пример: 16 + 4 * (3 - 1) -> 24\n"; + std::cout << "Введите выражение. Пустая строка — выход.\n"; + + std::string line; + while (true) { + std::cout << "> "; + if (!std::getline(std::cin, line)) break; + if (line.empty()) break; + try { + double r = calc.evaluate(line); + std::cout << r << '\n'; + } catch (const std::exception& ex) { + std::cout << "Ошибка: " << ex.what() << '\n'; + } + } + return 0; + } catch (const std::exception& ex) { + std::cerr << "Критическая ошибка: " << ex.what() << '\n'; + return 1; + } +} diff --git a/src/plugins.cpp b/src/plugins.cpp new file mode 100644 index 0000000..906652f --- /dev/null +++ b/src/plugins.cpp @@ -0,0 +1,148 @@ +#include "calc/plugins.hpp" +#include +#include + +#if !defined(_WIN32) + #include +#endif + +#include "calc/plugin_api.h" + +namespace calc { + +DynamicLibrary::DynamicLibrary(const fs::path& path) : path_(path) { +#if defined(_WIN32) + handle_ = LoadLibraryW(path.wstring().c_str()); + if (!handle_) throw std::runtime_error("Не удалось загрузить библиотеку: " + path.string()); +#else + handle_ = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!handle_) throw std::runtime_error(std::string("Не удалось загрузить библиотеку ") + path.string() + ": " + dlerror()); +#endif +} +DynamicLibrary::~DynamicLibrary() { +#if defined(_WIN32) + if (handle_) FreeLibrary((HMODULE)handle_); +#else + if (handle_) dlclose(handle_); +#endif +} +DynamicLibrary::DynamicLibrary(DynamicLibrary&& other) noexcept { swap(other); } +DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& other) noexcept { + if (this != &other) { DynamicLibrary tmp(std::move(other)); swap(tmp); } + return *this; +} +void* DynamicLibrary::symbol(const char* name) const { +#if defined(_WIN32) + auto s = GetProcAddress((HMODULE)handle_, name); + if (!s) throw std::runtime_error("Не найден символ: " + std::string(name)); + return reinterpret_cast(s); +#else + dlerror(); + void* s = dlsym(handle_, name); + if (const char* e = dlerror()) throw std::runtime_error(std::string("Ошибка поиска символа: ") + e); + if (!s) throw std::runtime_error("Пустой символ: " + std::string(name)); + return s; +#endif +} +void DynamicLibrary::swap(DynamicLibrary& other) noexcept { + std::swap(path_, other.path_); + std::swap(handle_, other.handle_); +} + +void FunctionRegistry::add_function(std::string name, UnaryFunction fn) { + auto n = normalize(std::move(name)); + if (functions_.count(n)) throw std::runtime_error("Дублирование функции: " + n); + functions_.emplace(std::move(n), std::move(fn)); +} +bool FunctionRegistry::contains(const std::string& name) const { + return functions_.count(normalize(name)) != 0; +} +FunctionRegistry::UnaryFunction FunctionRegistry::get_function(const std::string& name) const { + auto it = functions_.find(normalize(name)); + if (it == functions_.end()) throw std::runtime_error("Неизвестная функция: " + name); + return it->second; +} +bool FunctionRegistry::empty() const noexcept { return functions_.empty(); } +std::vector FunctionRegistry::names() const { + std::vector v; + v.reserve(functions_.size()); + for (auto& [k, _] : functions_) v.push_back(k); + std::sort(v.begin(), v.end()); + return v; +} +std::string FunctionRegistry::normalize(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return char(std::tolower(c)); }); + return s; +} + +PluginLoader::PluginLoader(fs::path dir) : dir_(std::move(dir)) {} + +void PluginLoader::load(FunctionRegistry& registry) { + if (!fs::exists(dir_)) throw std::runtime_error("Нет директории с плагинами: " + dir_.string()); + + size_t total = 0, matched = 0, registered = 0; + for (const auto& e : fs::directory_iterator(dir_)) { + if (!e.is_regular_file()) continue; + ++total; + const auto& p = e.path(); + + bool ok = false; +#if defined(_WIN32) + ok = (p.extension() == ".dll"); +#elif defined(__APPLE__) + ok = (p.extension() == ".dylib"); +#else + ok = (p.extension() == ".so"); +#endif + if (!ok) continue; + ++matched; + + try { + auto lib = std::make_unique(p); + auto* regfn = reinterpret_cast( + lib->symbol(calc::plugin::kRegistrationSymbol)); + if (!regfn) throw std::runtime_error("Пустая точка регистрации"); + + calc::plugin::FunctionDescriptor d{}; + if (!regfn(d)) throw std::runtime_error("Плагин вернул неуспех"); + if (d.abi_version != calc::plugin::kAbiVersion) + throw std::runtime_error("Несовместимая версия ABI"); + if (!d.name || !d.invoke) + throw std::runtime_error("Плагин вернул пустой дескриптор"); + + if (registry.contains(d.name)) + continue; + + registry.add_function( + d.name, + [fn = d.invoke, id = std::string(d.name)](double x) { + try { return fn(x); } + catch (const std::exception& ex) { + throw std::runtime_error("Ошибка функции '" + id + "': " + ex.what()); + } catch (...) { + throw std::runtime_error("Неизвестная ошибка функции '" + id + "'"); + } + }); + libs_.push_back(std::move(lib)); + ++registered; + } catch (...) { + } + } + +#if defined(_WIN32) + const char* need = ".dll"; +#elif defined(__APPLE__) + const char* need = ".dylib"; +#else + const char* need = ".so"; +#endif + + if (total == 0) + throw std::runtime_error(std::string("Каталог пуст. Ожидались плагины с расширением ") + need); + if (matched == 0) + throw std::runtime_error(std::string("Нет файлов плагинов с расширением ") + need); + if (registered == 0) + throw std::runtime_error("Файлы плагинов найдены, но ни одна функция не зарегистрирована"); +} + +} // namespace calc From 67629e9bd57a2849950bbc33b8a536bf9e3d962d Mon Sep 17 00:00:00 2001 From: SHISH82 Date: Mon, 24 Nov 2025 00:50:49 +0300 Subject: [PATCH 6/6] Fixed the plugin system for windows + UTF-8 --- include/calc/plugins.hpp | 53 ++++++++------ src/main.cpp | 32 ++++++++- src/plugins.cpp | 148 +++++++++++++++++++++++++++++---------- 3 files changed, 170 insertions(+), 63 deletions(-) diff --git a/include/calc/plugins.hpp b/include/calc/plugins.hpp index bc65a84..374d013 100644 --- a/include/calc/plugins.hpp +++ b/include/calc/plugins.hpp @@ -1,50 +1,59 @@ #pragma once + +#include #include #include +#include #include #include #include -#include -#include -#include - -namespace fs = std::filesystem; namespace calc { + namespace fs = std::filesystem; + + class DynamicLibrary { + public: + explicit DynamicLibrary(const fs::path& path); + ~DynamicLibrary(); + + DynamicLibrary(const DynamicLibrary&) = delete; + DynamicLibrary& operator=(const DynamicLibrary&) = delete; + + DynamicLibrary(DynamicLibrary&& other) noexcept; + DynamicLibrary& operator=(DynamicLibrary&& other) noexcept; + + + void* symbol(const char* name) const; + + private: + fs::path path_; + void* handle_ = nullptr; + + void swap(DynamicLibrary& other) noexcept; + }; + + // Реестр функций-плагинов class FunctionRegistry { public: using UnaryFunction = std::function; + void add_function(std::string name, UnaryFunction fn); bool contains(const std::string& name) const; UnaryFunction get_function(const std::string& name) const; bool empty() const noexcept; std::vector names() const; - static std::string normalize(std::string s); - private: std::map functions_; - }; - class DynamicLibrary { - public: - explicit DynamicLibrary(const fs::path& path); - ~DynamicLibrary(); - DynamicLibrary(DynamicLibrary&&) noexcept; - DynamicLibrary& operator=(DynamicLibrary&&) noexcept; - - void* symbol(const char* name) const; - - private: - void* handle_{}; - fs::path path_; - void swap(DynamicLibrary& other) noexcept; + static std::string normalize(std::string s); }; class PluginLoader { public: explicit PluginLoader(fs::path dir); + void load(FunctionRegistry& registry); private: @@ -52,4 +61,4 @@ namespace calc { std::vector> libs_; }; -} // namespace calc +} diff --git a/src/main.cpp b/src/main.cpp index e643a73..7735d34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,11 @@ #include #include +#if defined(_WIN32) + #define NOMINMAX + #include +#endif + #include "calc/eval.hpp" #include "calc/plugins.hpp" @@ -22,16 +27,37 @@ static const char* platform_ext() { int main(int argc, char* argv[]) { try { +#if defined(_WIN32) + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); +#endif + calc::FunctionRegistry reg; +#if defined(_DEBUG) + const char* cfg = "Debug"; +#else + const char* cfg = "Release"; +#endif std::vector dirs; - dirs.push_back(fs::current_path() / "plugins"); + + fs::path cwd = fs::current_path(); + dirs.push_back(cwd / "plugins"); + dirs.push_back(cwd / "plugins" / cfg); + if (argc > 0 && argv && argv[0]) { try { fs::path exe = fs::weakly_canonical(fs::path(argv[0])); - dirs.push_back(exe.parent_path() / "plugins"); - } catch (...) {} + fs::path exe_dir = exe.parent_path(); + + dirs.push_back(exe_dir / "plugins"); + dirs.push_back(exe_dir / "plugins" / cfg); + + dirs.push_back(exe_dir.parent_path() / "plugins"); + dirs.push_back(exe_dir.parent_path() / "plugins" / cfg); + } catch (...) { + } } for (auto& d : dirs) { diff --git a/src/plugins.cpp b/src/plugins.cpp index 906652f..c6011d3 100644 --- a/src/plugins.cpp +++ b/src/plugins.cpp @@ -1,88 +1,142 @@ #include "calc/plugins.hpp" #include #include +#include -#if !defined(_WIN32) - #include +#if defined(_WIN32) + #define NOMINMAX + #include +#else + #include #endif #include "calc/plugin_api.h" namespace calc { + DynamicLibrary::DynamicLibrary(const fs::path& path) : path_(path) { #if defined(_WIN32) handle_ = LoadLibraryW(path.wstring().c_str()); - if (!handle_) throw std::runtime_error("Не удалось загрузить библиотеку: " + path.string()); + if (!handle_) { + throw std::runtime_error("Не удалось загрузить библиотеку: " + path.string()); + } #else handle_ = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL); - if (!handle_) throw std::runtime_error(std::string("Не удалось загрузить библиотеку ") + path.string() + ": " + dlerror()); + if (!handle_) { + throw std::runtime_error( + std::string("Не удалось загрузить библиотеку ") + + path.string() + ": " + dlerror()); + } #endif } + DynamicLibrary::~DynamicLibrary() { #if defined(_WIN32) - if (handle_) FreeLibrary((HMODULE)handle_); + if (handle_) { + FreeLibrary((HMODULE)handle_); + } #else - if (handle_) dlclose(handle_); + if (handle_) { + dlclose(handle_); + } #endif } -DynamicLibrary::DynamicLibrary(DynamicLibrary&& other) noexcept { swap(other); } + +DynamicLibrary::DynamicLibrary(DynamicLibrary&& other) noexcept { + swap(other); +} + DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& other) noexcept { - if (this != &other) { DynamicLibrary tmp(std::move(other)); swap(tmp); } + if (this != &other) { + DynamicLibrary tmp(std::move(other)); + swap(tmp); + } return *this; } + void* DynamicLibrary::symbol(const char* name) const { #if defined(_WIN32) auto s = GetProcAddress((HMODULE)handle_, name); - if (!s) throw std::runtime_error("Не найден символ: " + std::string(name)); + if (!s) { + throw std::runtime_error("Не найден символ: " + std::string(name)); + } return reinterpret_cast(s); #else dlerror(); void* s = dlsym(handle_, name); - if (const char* e = dlerror()) throw std::runtime_error(std::string("Ошибка поиска символа: ") + e); - if (!s) throw std::runtime_error("Пустой символ: " + std::string(name)); + if (const char* e = dlerror()) { + throw std::runtime_error(std::string("Ошибка поиска символа: ") + e); + } + if (!s) { + throw std::runtime_error("Пустой символ: " + std::string(name)); + } return s; #endif } + void DynamicLibrary::swap(DynamicLibrary& other) noexcept { - std::swap(path_, other.path_); + std::swap(path_, other.path_); std::swap(handle_, other.handle_); } + void FunctionRegistry::add_function(std::string name, UnaryFunction fn) { auto n = normalize(std::move(name)); - if (functions_.count(n)) throw std::runtime_error("Дублирование функции: " + n); + if (functions_.count(n)) { + throw std::runtime_error("Дублирование функции: " + n); + } functions_.emplace(std::move(n), std::move(fn)); } + bool FunctionRegistry::contains(const std::string& name) const { return functions_.count(normalize(name)) != 0; } -FunctionRegistry::UnaryFunction FunctionRegistry::get_function(const std::string& name) const { + +FunctionRegistry::UnaryFunction +FunctionRegistry::get_function(const std::string& name) const { auto it = functions_.find(normalize(name)); - if (it == functions_.end()) throw std::runtime_error("Неизвестная функция: " + name); + if (it == functions_.end()) { + throw std::runtime_error("Неизвестная функция: " + name); + } return it->second; } -bool FunctionRegistry::empty() const noexcept { return functions_.empty(); } + +bool FunctionRegistry::empty() const noexcept { + return functions_.empty(); +} + std::vector FunctionRegistry::names() const { std::vector v; v.reserve(functions_.size()); - for (auto& [k, _] : functions_) v.push_back(k); + for (auto& [k, _] : functions_) { + v.push_back(k); + } std::sort(v.begin(), v.end()); return v; } + std::string FunctionRegistry::normalize(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return char(std::tolower(c)); }); + std::transform( + s.begin(), s.end(), s.begin(), + [](unsigned char c) { return char(std::tolower(c)); }); return s; } + PluginLoader::PluginLoader(fs::path dir) : dir_(std::move(dir)) {} void PluginLoader::load(FunctionRegistry& registry) { - if (!fs::exists(dir_)) throw std::runtime_error("Нет директории с плагинами: " + dir_.string()); + if (!fs::exists(dir_)) { + throw std::runtime_error("Нет директории с плагинами: " + dir_.string()); + } + + std::size_t total = 0, matched = 0, registered = 0; - size_t total = 0, matched = 0, registered = 0; for (const auto& e : fs::directory_iterator(dir_)) { - if (!e.is_regular_file()) continue; + if (!e.is_regular_file()) { + continue; + } ++total; const auto& p = e.path(); @@ -94,35 +148,47 @@ void PluginLoader::load(FunctionRegistry& registry) { #else ok = (p.extension() == ".so"); #endif - if (!ok) continue; + if (!ok) { + continue; + } ++matched; try { auto lib = std::make_unique(p); auto* regfn = reinterpret_cast( lib->symbol(calc::plugin::kRegistrationSymbol)); - if (!regfn) throw std::runtime_error("Пустая точка регистрации"); + if (!regfn) { + } calc::plugin::FunctionDescriptor d{}; - if (!regfn(d)) throw std::runtime_error("Плагин вернул неуспех"); - if (d.abi_version != calc::plugin::kAbiVersion) + if (!regfn(d)) { + throw std::runtime_error("Плагин вернул неуспех"); + } + if (d.abi_version != calc::plugin::kAbiVersion) { throw std::runtime_error("Несовместимая версия ABI"); - if (!d.name || !d.invoke) + } + if (!d.name || !d.invoke) { throw std::runtime_error("Плагин вернул пустой дескриптор"); + } - if (registry.contains(d.name)) + if (registry.contains(d.name)) { continue; + } registry.add_function( d.name, [fn = d.invoke, id = std::string(d.name)](double x) { - try { return fn(x); } - catch (const std::exception& ex) { - throw std::runtime_error("Ошибка функции '" + id + "': " + ex.what()); + try { + return fn(x); + } catch (const std::exception& ex) { + throw std::runtime_error( + "Ошибка функции '" + id + "': " + ex.what()); } catch (...) { - throw std::runtime_error("Неизвестная ошибка функции '" + id + "'"); + throw std::runtime_error( + "Неизвестная ошибка функции '" + id + "'"); } }); + libs_.push_back(std::move(lib)); ++registered; } catch (...) { @@ -137,12 +203,18 @@ void PluginLoader::load(FunctionRegistry& registry) { const char* need = ".so"; #endif - if (total == 0) - throw std::runtime_error(std::string("Каталог пуст. Ожидались плагины с расширением ") + need); - if (matched == 0) - throw std::runtime_error(std::string("Нет файлов плагинов с расширением ") + need); - if (registered == 0) - throw std::runtime_error("Файлы плагинов найдены, но ни одна функция не зарегистрирована"); + if (total == 0) { + throw std::runtime_error( + std::string("Каталог пуст. Ожидались плагины с расширением ") + need); + } + if (matched == 0) { + throw std::runtime_error( + std::string("Нет файлов плагинов с расширением ") + need); + } + if (registered == 0) { + throw std::runtime_error( + "Файлы плагинов найдены, но ни одна функция не зарегистрирована"); + } } -} // namespace calc +}