diff --git a/CommandEngine/CMakeLists.txt b/CommandEngine/CMakeLists.txt new file mode 100644 index 0000000..1933627 --- /dev/null +++ b/CommandEngine/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.10) +project(CommandEngine) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(WIN32) + add_definitions(-D_WIN32_WINNT=0x0601) + add_definitions(-DNOMINMAX) +endif() + +# Убираем -Werror и уменьшаем строгость предупреждений +if(MSVC) + add_compile_options(/W4 /EHsc) # Убрали /WX (трактовать предупреждения как ошибки) +else() + add_compile_options(-Wall -Wextra -pedantic) # Убрали -Werror +endif() + +add_executable(CommandEngine + CommandEngine.cpp + TestFunctions.cpp +) + +set_source_files_properties( + CommandEngine.cpp + TestFunctions.cpp + PROPERTIES LANGUAGE CXX +) + +target_include_directories(CommandEngine PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +if(WIN32) + target_compile_definitions(CommandEngine PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +# Настройка для отладки +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(MSVC) + target_compile_options(CommandEngine PRIVATE /Zi /Od) + else() + target_compile_options(CommandEngine PRIVATE -g -O0) + endif() +else() + if(MSVC) + target_compile_options(CommandEngine PRIVATE /O2) + else() + target_compile_options(CommandEngine PRIVATE -O3) + endif() +endif() \ No newline at end of file diff --git a/CommandEngine/CommandEngine.cpp b/CommandEngine/CommandEngine.cpp new file mode 100644 index 0000000..b2a2ec8 --- /dev/null +++ b/CommandEngine/CommandEngine.cpp @@ -0,0 +1,17 @@ +#include "TestFunctions.hpp" +#include "locale.h" + +int main() { + setlocale(LC_ALL, "Rus"); + try { + runAssignmentTest(); + runAllTypeTests(); + testDefaultArguments(); + + return 0; + } + catch (const std::exception& e) { + std::cerr << "\nОшибка: " << e.what() << std::endl; + return 1; + } +} diff --git a/CommandEngine/CommandEngine.vcxproj b/CommandEngine/CommandEngine.vcxproj new file mode 100644 index 0000000..5283007 --- /dev/null +++ b/CommandEngine/CommandEngine.vcxproj @@ -0,0 +1,146 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {ca454cd1-3686-4938-91ba-6330fcaf4ce2} + CommandEngine + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CommandEngine/CommandEngine.vcxproj.filters b/CommandEngine/CommandEngine.vcxproj.filters new file mode 100644 index 0000000..3e38657 --- /dev/null +++ b/CommandEngine/CommandEngine.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Исходные файлы + + + Файлы заголовков + + + Исходные файлы + + + + + Файлы заголовков + + + Файлы заголовков + + + Файлы заголовков + + + \ No newline at end of file diff --git a/CommandEngine/Engine.hpp b/CommandEngine/Engine.hpp new file mode 100644 index 0000000..9ca3a07 --- /dev/null +++ b/CommandEngine/Engine.hpp @@ -0,0 +1,54 @@ +#pragma once +#ifndef ENGINE_HPP +#define ENGINE_HPP + +#include "InterfaceCommand.hpp" +#include +#include +#include +#include + +class Engine { +public: + Engine() = default; + void register_command(InterfaceCommand* cmd, const std::string& name) { + + if (name.empty()) { + throw std::invalid_argument("Command name cannot be empty"); + } + + if (commands.find(name) != commands.end()) { + throw std::runtime_error("Command already registered: " + name); + } + + commands[name] = cmd; + } + + CommandResult execute(const std::string& name, const ArgList& args = {}) { + + auto it = commands.find(name); + if (it == commands.end()) { + throw std::runtime_error("Command not found: " + name); + } + + try { + return it->second->execute(args); + } + catch (const std::exception& e) { + throw std::runtime_error("Error executing command '" + name + "': " + e.what()); + } + } + + bool has_command(const std::string& name) const { + return commands.find(name) != commands.end(); + } + + void clear() { + commands.clear(); + } + +private: + std::unordered_map commands; +}; + +#endif \ No newline at end of file diff --git a/CommandEngine/InterfaceCommand.hpp b/CommandEngine/InterfaceCommand.hpp new file mode 100644 index 0000000..833b9f3 --- /dev/null +++ b/CommandEngine/InterfaceCommand.hpp @@ -0,0 +1,35 @@ +#pragma once +#ifndef INTERFACECOMMAND_HPP +#define INTERFACECOMMAND_HPP + +#include +#include +#include +#include +#include + +struct VoidResult {}; +using ArgValue = std::variant; +using ArgList = std::vector>; +using CommandResult = std::variant; + +class InterfaceCommand { +public: + virtual ~InterfaceCommand() = default; + virtual CommandResult execute(const ArgList& args = {}) = 0; +}; + +inline std::ostream& operator<<(std::ostream& os, const CommandResult& result) { + if (std::holds_alternative(result)) { + os << "void"; + } + else { + const auto& value = std::get(result); + std::visit([&os](auto&& arg) { + os << arg; + }, value); + } + return os; +} + +#endif diff --git a/CommandEngine/TestFunctions.cpp b/CommandEngine/TestFunctions.cpp new file mode 100644 index 0000000..b4242c3 --- /dev/null +++ b/CommandEngine/TestFunctions.cpp @@ -0,0 +1,145 @@ +#include "TestFunctions.hpp" + +template +bool checkResult(const CommandResult& result, T expected) { + if (std::holds_alternative(result)) { + return false; + } + + const auto& value = std::get(result); + try { + return std::get(value) == expected; + } + catch (...) { + return false; + } +} + +bool isVoidResult(const CommandResult& result) { + return std::holds_alternative(result); +} + +void runAssignmentTest() { + + TestClass obj; + + Wrapper wrapper(&obj, &TestClass::f3, { {"arg1", 0}, {"arg2", 0} }); + + Engine engine; + + engine.register_command(&wrapper, "command1"); + + std::cout << "Результат сумма: " << engine.execute("command1", { {"arg1", "4"}, {"arg2", "5"} }) << std::endl; + + auto result = engine.execute("command1", { {"arg1", "4"}, {"arg2", "5"} }); + assert(checkResult(result, 9) && "Тест не пройден"); + +} + +void runAllTypeTests() { + + TestClass obj; + Engine engine; + + Wrapper intWrapper(&obj, &TestClass::f3, { {"a", 0}, {"b", 0} }); + engine.register_command(&intWrapper, "add"); + + Wrapper stringWrapper(&obj, &TestClass::repeat, { {"s", std::string("")}, {"n", 0} }); + engine.register_command(&stringWrapper, "repeat"); + + Wrapper avgWrapper(&obj, &TestClass::average, + { {"a", 0.0}, {"b", 0.0} }); + engine.register_command(&avgWrapper, "average"); + + Wrapper multiplyWrapper(&obj, &TestClass::multiply, + { {"a", 0.0}, {"b", 0.0} }); + engine.register_command(&multiplyWrapper, "multiply"); + + Wrapper boolWrapper(&obj, &TestClass::yes_no, { {"flag", false} }); + engine.register_command(&boolWrapper, "yes_no"); + + Wrapper voidWrapper(&obj, &TestClass::print_greeting, + { {"name", std::string("")}, {"times", 1} }); + engine.register_command(&voidWrapper, "greet"); + + Wrapper squareWrapper(&obj, &TestClass::getSquare, { {"x", 0} }); + engine.register_command(&squareWrapper, "square"); + + Wrapper multiWrapper(&obj, &TestClass::multiply5, + { {"a", 1.0}, {"b", 1.0}, {"c", 1.0}, {"d", 1.0}, {"e", 1.0} }); + engine.register_command(&multiWrapper, "multiply5"); + + + { + auto result = engine.execute("add", { {"a", "10"}, {"b", "20"} }); + assert(checkResult(result, 30) && "Int метод: add(10, 20) не вернул 30"); + std::cout << "add(10, 20) = " << std::get(std::get(result)) << "\n"; + } + + { + auto result = engine.execute("repeat", { {"s", "AB"}, {"n", "3"} }); + assert(checkResult(result, std::string("ABABAB")) && "String метод: repeat(\"AB\", 3) не вернул \"ABABAB\""); + std::cout << "repeat(\"AB\", 3) = \"" + << std::get(std::get(result)) << "\"\n"; + } + + { + auto result = engine.execute("average", { {"a", "10.0"}, {"b", "20.0"} }); + assert(checkResult(result, 15.0) && "Double метод: average(10, 20) не вернул 15.0"); + std::cout << "average(10, 20) = " + << std::get(std::get(result)) << "\n"; + } + + { + auto result = engine.execute("multiply", { {"a", "3.5"}, {"b", "2.0"} }); + assert(checkResult(result, 7.0) && "Double метод: multiply(3.5, 2.0) не вернул 7.0"); + std::cout << "multiply(3.5, 2.0) = " + << std::get(std::get(result)) << "\n"; + } + + { + auto result = engine.execute("yes_no", { {"flag", "true"} }); + assert(checkResult(result, true) && "Bool метод: yes_no(true) не вернул true"); + std::cout << "yes_no(true) = " + << std::boolalpha << std::get(std::get(result)) << "\n"; + } + + { + auto result = engine.execute("greet", { {"name", "World"}, {"times", "2"} }); + assert(isVoidResult(result) && "Void метод: greet(\"World\", 2) не вернул void результат"); + std::cout << "greet(\"World\", 2) выполнено\n"; + } + + { + auto result = engine.execute("square", { {"x", "6"} }); + assert(checkResult(result, 36) && "Метод: square(6) не вернул 36"); + std::cout << "square(6) = " + << std::get(std::get(result)) << "\n"; + } + + { + auto result = engine.execute("multiply5", + { {"a", "1.0"}, {"b", "2.0"}, {"c", "3.0"}, + {"d", "4.0"}, {"e", "5.0"} }); + assert(checkResult(result, 120.0) && "Метод с 5 аргументами: multiply5(1,2,3,4,5) не вернул 120.0"); + std::cout << "multiply5(1, 2, 3, 4, 5) = " + << std::get(std::get(result)) << "\n"; + } +} + +void testDefaultArguments() { + + TestClass obj; + Engine engine; + + Wrapper wrapper(&obj, &TestClass::f3, { {"arg1", 100}, {"arg2", 200} }); + engine.register_command(&wrapper, "default_test"); + + auto result1 = engine.execute("default_test"); + assert(checkResult(result1, 300) && "Значения по умолчанию: default_test() не вернул 300"); + std::cout << "default_test() = 300\n"; + + auto result2 = engine.execute("default_test", { {"arg1", "50"} }); + assert(checkResult(result2, 250) && "Переопределение аргумента: default_test(arg1=50) не вернул 250"); + std::cout << "default_test(arg1=50) = 250\n"; +} \ No newline at end of file diff --git a/CommandEngine/TestFunctions.hpp b/CommandEngine/TestFunctions.hpp new file mode 100644 index 0000000..971da5e --- /dev/null +++ b/CommandEngine/TestFunctions.hpp @@ -0,0 +1,53 @@ +#pragma once +#ifndef TESTFUNCTIONS_HPP +#define TESTFUNCTIONS_HPP + +#include "Engine.hpp" +#include "Wrapper.hpp" +#include +#include +#include + +class TestClass { +public: + int f3(int arg1, int arg2) { + return arg1 + arg2; + } + + std::string repeat(std::string s, int n) { + std::string result; + for (int i = 0; i < n; ++i) result += s; + return result; + } + + int getSquare(int x) { + return x * x; + } + + double average(double a, double b) { + return (a + b) / 2.0; + } + + double multiply(double a, double b) { + return a * b; + } + + bool yes_no(bool flag) { return flag; } + + void print_greeting(std::string name, int times) { + for (int i = 0; i < times; ++i) { + std::cout << "Hello, " << name << "!" << std::endl; + } + } + double multiply5(double a, double b, double c, double d, double e) { + return a * b * c * d * e; + } + + +}; + +void runAssignmentTest(); +void runAllTypeTests(); +void testDefaultArguments(); + +#endif \ No newline at end of file diff --git a/CommandEngine/Wrapper.hpp b/CommandEngine/Wrapper.hpp new file mode 100644 index 0000000..a16ab9b --- /dev/null +++ b/CommandEngine/Wrapper.hpp @@ -0,0 +1,215 @@ +#pragma once +#ifndef WRAPPER_HPP +#define WRAPPER_HPP + +#include "InterfaceCommand.hpp" +#include +#include +#include +#include +#include +#include +#include + + +namespace detail { + template + T parseValue(const std::string& str) { + std::stringstream ss(str); + T value; + ss >> value; + if (ss.fail()) { + throw std::runtime_error("Cannot parse value: " + str); + } + return value; + } + + // Специализации для разных типов + template<> + inline int parseValue(const std::string& str) { + try { + return std::stoi(str); + } + catch (...) { + throw std::runtime_error("Cannot parse int from: " + str); + } + } + + template<> +inline double parseValue(const std::string& str) { + try { + std::string localStr = str; + + std::locale current_locale(""); + char decimal_point = std::use_facet>(current_locale).decimal_point(); + + // Если разделитель - запятая, заменяем точку на запятую + if (decimal_point == ',') { + std::replace(localStr.begin(), localStr.end(), '.', ','); + } + + return std::stod(localStr); + } + catch (...) { + throw std::runtime_error("Cannot parse double from: " + str); + } +} + + template<> + inline std::string parseValue(const std::string& str) { + return str; + } + + template<> + inline bool parseValue(const std::string& str) { + std::string lower = str; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + return (lower == "true" || lower == "1" || lower == "yes" || lower == "on"); + } +} + +template +class Wrapper : public InterfaceCommand { +public: + using MethodPtr = ReturnType(ClassType::*)(ParamTypes...); + + Wrapper(ClassType* obj, MethodPtr method, + const std::vector>& defaults = {}) + : obj(obj), method(method), defaults(defaults) { + } + + CommandResult execute(const ArgList& args = {}) override { + std::vector finalValues; + + if (!defaults.empty()) { + for (const auto& def : defaults) { + finalValues.push_back(def.second); + } + } + else { + finalValues = initializeDefaults(); + } + + for (const auto& arg : args) { + const auto& name = arg.first; + const auto& strValue = arg.second; + + int index = -1; + for (size_t i = 0; i < defaults.size(); ++i) { + if (defaults[i].first == name) { + index = static_cast(i); + break; + } + } + + if (index >= 0 && index < static_cast(finalValues.size())) { + finalValues[index] = convertStringToType(strValue, defaults[index].second); + } + } + + constexpr std::size_t expected = sizeof...(ParamTypes); + if (finalValues.size() < expected) { + throw std::runtime_error("Not enough arguments for command"); + } + + return callMethod(finalValues); + } + +private: + ClassType* obj; + MethodPtr method; + std::vector> defaults; + + std::vector initializeDefaults() const { + std::vector result; + (result.push_back(getDefaultValue()), ...); + return result; + } + + template + ArgValue getDefaultValue() const { + if constexpr (std::is_same_v) { + return 0; + } + else if constexpr (std::is_same_v) { + return 0.0; + } + else if constexpr (std::is_same_v) { + return false; + } + else if constexpr (std::is_same_v) { + return std::string(""); + } + else { + throw std::runtime_error("Unsupported type"); + } + } + + ArgValue convertStringToType(const std::string& str, const ArgValue& target) const { + if (std::holds_alternative(target)) { + return detail::parseValue(str); + } + else if (std::holds_alternative(target)) { + return detail::parseValue(str); + } + else if (std::holds_alternative(target)) { + return detail::parseValue(str); + } + else if (std::holds_alternative(target)) { + return str; + } + else { + throw std::runtime_error("Unsupported argument type"); + } + } + + template + CommandResult callImpl(const std::vector& finalValues, + std::index_sequence) { + if constexpr (std::is_same_v) { + // Для void + (obj->*method)(std::get(finalValues[I])...); + return CommandResult{ VoidResult{} }; + } + else { + ReturnType result = (obj->*method)(std::get(finalValues[I])...); + return CommandResult{ ArgValue{result} }; + } + } + + CommandResult callMethod(const std::vector& finalValues) { + return callImpl(finalValues, std::index_sequence_for{}); + } +}; + +// Для методов без параметров +template +class Wrapper : public InterfaceCommand { +public: + using MethodPtr = ReturnType(ClassType::*)(); + + Wrapper(ClassType* obj, MethodPtr method) + : obj(obj), method(method) { + } + + CommandResult execute(const ArgList& args = {}) override { + if (!args.empty()) { + throw std::runtime_error("Method takes no arguments"); + } + + if constexpr (std::is_same_v) { + (obj->*method)(); + return CommandResult{ VoidResult{} }; + } + else { + ReturnType result = (obj->*method)(); + return CommandResult{ ArgValue{result} }; + } + } + +private: + ClassType* obj; + MethodPtr method; +}; + +#endif \ No newline at end of file diff --git a/README.md b/README.md index abbadf7..0322a85 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# Lab_C_plus -Лабораторные работы по C++ +# Реализовать инкапсуляцию методов класса произвольной сигнатуры +## Сборка и запуск +mkdir build +cd build +cmake .. +cmake --build . --config Release +.\Release\CommandEngine.exe