diff --git a/final/final.sln b/final/final.sln new file mode 100644 index 0000000..a3929dd --- /dev/null +++ b/final/final.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests\tests.vcxproj", "{DD752318-EA08-4316-9EFE-C1A42F5526F4}" + ProjectSection(ProjectDependencies) = postProject + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} = {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tool", "tool\tool.vcxproj", "{6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x64.ActiveCfg = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x86.ActiveCfg = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x86.Build.0 = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x64.ActiveCfg = Release|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x86.ActiveCfg = Release|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x86.Build.0 = Release|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x64.ActiveCfg = Debug|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x64.Build.0 = Debug|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x86.ActiveCfg = Debug|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x86.Build.0 = Debug|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x64.ActiveCfg = Release|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x64.Build.0 = Release|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x86.ActiveCfg = Release|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FDE21BA4-D7CE-47C7-96EA-E00553A83F88} + EndGlobalSection +EndGlobal diff --git a/final/tests/tests.cpp b/final/tests/tests.cpp new file mode 100644 index 0000000..d6d2c25 --- /dev/null +++ b/final/tests/tests.cpp @@ -0,0 +1,8 @@ +#include +#include +#include "tests.h" +#include +#include +#include +#include + diff --git a/final/tests/tests.h b/final/tests/tests.h new file mode 100644 index 0000000..60bea48 --- /dev/null +++ b/final/tests/tests.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include +#include +#include diff --git a/final/tests/tests.vcxproj b/final/tests/tests.vcxproj new file mode 100644 index 0000000..704ffb4 --- /dev/null +++ b/final/tests/tests.vcxproj @@ -0,0 +1,105 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {DD752318-EA08-4316-9EFE-C1A42F5526F4} + Win32Proj + e01 + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + Unicode + false + + + DynamicLibrary + false + v141 + true + Unicode + false + + + + + + + + + + + + + true + ..\$(Configuration)\$(Platform) + + + true + ..\$(Configuration)\$(Platform) + + + + NotUsing + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + NotUsing + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/final/tests/tool_test.cpp b/final/tests/tool_test.cpp new file mode 100644 index 0000000..705b7f1 --- /dev/null +++ b/final/tests/tool_test.cpp @@ -0,0 +1,129 @@ +#include "CppUnitTest.h" +#include +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +#include "tests.h" +#include +#include +#include "..\tool\block.h" + +TEST_CLASS(test_tool) +{ +public: + TEST_METHOD(test_add) + { + block_processor bp; + //std::vector p; + //p.push_back(5); + std::unique_ptr block_add = block_factory::create("add", { 5 }); + bp.add_block(std::move(block_add)); + double res = bp.calc_all(5.); + Assert::AreEqual(10., res); + } + + TEST_METHOD(test_lim_lower) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(4.); + Assert::AreEqual(5., res); + } + + TEST_METHOD(test_lim_between) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(7.); + Assert::AreEqual(7., res); + } + + TEST_METHOD(test_lim_upper) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(17.); + Assert::AreEqual(10., res); + } + TEST_METHOD(test_calc_all) + { + block_processor bp; + std::unique_ptr block_add = block_factory::create("add", { 2 }); + std::unique_ptr block_mult = block_factory::create("mult", { 2 }); + bp.add_block(std::move(block_add)); + bp.add_block(std::move(block_mult)); + double res = bp.calc_all(1.); + Assert::AreEqual(6., res); + } + + TEST_METHOD(test_get_block_infos) + { + std::vector abl = block_factory::get_block_infos(); + Assert::IsTrue(std::any_of(abl.begin(), abl.end(), + [](std::string const& s) + { + return s.compare(0, 3, "add") == 0; + })); + } + + TEST_METHOD(test_bulk_calc_with_add_block) + { + //create block processor with 'add' block + const double add_parameter = 5.; + block_processor bp; + std::unique_ptr bl = block_factory::create("add", { add_parameter }); + bp.add_block(std::move(bl)); + + //prepare input values + std::vector inputs{1,2,3}; + std::stringstream input_ss; + for (auto it = inputs.begin(); it != inputs.end(); ++it) + input_ss << *it << std::endl; + + //validate return value from bulk_calc + std::stringstream output_ss; + Assert::AreEqual(bp.bulk_calc(input_ss, output_ss), true); + + //validate output values + double output_value; + std::vector outputs; + std::string output_line; + + //check that all output values are doubles + std::istringstream line_stream; + while (std::getline(output_ss, output_line)) { + std::stringstream output_value_ss(output_line); + if (output_value_ss >> output_value) + outputs.push_back(output_value); + else + Assert::Fail(L"output is wrong"); + } + + //check input and output sizes + Assert::AreEqual(outputs.size() == 3, true); + + //compare and validate each value + const bool is_equal = std::equal(outputs.begin(), outputs.end(), inputs.begin(), + [add_parameter](double &vo, double &vi) + { + return add_parameter + vi == vo; + }); + + Assert::AreEqual(is_equal, true); + } + + TEST_METHOD(test_load_sequence) + { + const double add_parameter = 5.; + block_processor bp; + + std::stringstream input_ss("add 5\nmult 2"); + Assert::AreEqual(bp.load_from_stream(input_ss), true); + + //check bp size + auto seq = bp.get_sequence(); + Assert::AreEqual(seq.size() == 2, true); + } +}; diff --git a/final/tool/block.cpp b/final/tool/block.cpp new file mode 100644 index 0000000..604805c --- /dev/null +++ b/final/tool/block.cpp @@ -0,0 +1,198 @@ +#include "stdafx.h" +#include "block.h" +#include +//#include +#include +#include +#include +#include "menus.h" + +#pragma region block +block::block(std::string block_type_name, int number_of_parameters, const std::vector& parameters) +{ + if (parameters.size() != number_of_parameters) + throw std::invalid_argument("Wrong number of arguments"); + parameters_ = parameters; + block_type_name_ = block_type_name; +} + +std::string block::get_command() +{ + std::string arguments = std::accumulate(parameters_.begin(), parameters_.end(), std::string(), + [](std::string &args, double &par) + { + return args.empty() ? std::to_string(par) : args + " " + std::to_string(par); + }); + + return block_type_name_ + (arguments.empty() ? "" : " " + arguments); +} + +//block::~block() {}; +#pragma endregion block + +#pragma region block_processor +block_processor::block_processor() +{ + +} + +void block_processor::add_block(std::unique_ptr b) +{ + blocks_.push_back(std::move(b)); +} + +double block_processor::calc_all(double input) const +{ + const double calculated_value = std::accumulate(blocks_.begin(), blocks_.end(), input, + //with below 'const' we are promising that variable 'b' will not (and cannot) be changed. + [](double intermediate_value, const std::unique_ptr &b) + { + return intermediate_value = (*b).calc(intermediate_value); + }); + + return calculated_value; +} + +std::vector block_processor::get_sequence() +{ + std::vector temp_vector; + temp_vector.reserve(blocks_.size()); + std::transform(blocks_.begin(), blocks_.end(), std::back_inserter(temp_vector), [](std::unique_ptr const &b) + { + return (*b).get_command(); + }); + return temp_vector; +} + +void block_processor::load(const sequence_list& sl) +{ + blocks_.clear(); + for (sequence_list::const_iterator it = sl.begin(); it != sl.end(); ++it) { + const std::string block_name = (*it).first; + const std::vector parameter_values = it->second; + std::unique_ptr bl = block_factory::create(block_name, parameter_values); + add_block(std::move(bl)); + } +} + +bool block_processor::load_from_stream(std::istream &is) +{ + //check streams + if (!is.good()) { return false; } + + sequence_list stored_blocks; + std::string block_line; + std::istringstream line_stream; + while (std::getline(is, block_line)) { + const std::pair> block_command = get_block_command_from_text(block_line); + stored_blocks.push_back(block_command); + } + load(stored_blocks); + return true; +} + +bool block_processor::remove_block(unsigned int index) +{ + if (index > blocks_.size()) + return false; + blocks_.erase(blocks_.begin() + index - 1); + return true; +} + +bool block_processor::bulk_calc(std::istream &is, std::ostream &os) const +{ + //check streams + if (!os.good() || !is.good()) { return false; } + + //load inputs from input stream + double input_value; + std::vector inputs_for_calc; + std::string input_line; + + std::istringstream line_stream; + while (getline(is, input_line)) { + std::stringstream input_ss(input_line); + if (input_ss >> input_value) + inputs_for_calc.push_back(input_value); + else + return false; + } + + //calculate each input value and send to output stream + for (std::vector::iterator it = inputs_for_calc.begin(); it != inputs_for_calc.end(); ++it) + os << calc_all(*it) << std::endl; + + return true; +} + +std::pair> block_processor::get_block_command_from_text(std::string command_text) +{ + bool block_name_taken = false; + std::string block_name; + std::vector parameters; + std::istringstream line_ss(command_text); + do + { + std::string value; + line_ss >> value; + if (value == "") break; + if (!block_name_taken) + { + block_name = value; + block_name_taken = true; + } + else + parameters.push_back(std::stof(value)); + //parameters.push_back(atof(value.c_str())); + } while (!line_ss.eof()); + return std::pair>(block_name, parameters); +} +#pragma endregion block_processor + +#pragma region block_factory +using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + +std::map block_factory::create_methods_; + +bool block_factory::register_block(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock) +{ + if (auto it = create_methods_.find(block_name); it == create_methods_.end()) + { + create_methods_[block_name] = block_meta(block_description, funcCreateBlock); + //std::cout << "LOG: block_factory::register_block for block: " + block_name << std::endl; + return true; + } + return false; +} + +std::unique_ptr block_factory::create(const std::string& block_name, const std::vector& parameters) +{ + if (auto it = create_methods_.find(block_name); it != create_methods_.end()) + { + //std::cout << "LOG: block_factory::Create for block: " + block_name << std::endl; + return it->second.GetCreateMethod()(parameters); // call the createFunc + } + return nullptr; +} + +std::vector block_factory::get_block_infos() +{ + //Vector of int to store values + std::vector temp_vector; + temp_vector.reserve(create_methods_.size()); + + //First solution: Copy all value fields from map to a vector using transform() & Lambda function + std::transform(create_methods_.begin(), create_methods_.end(), std::back_inserter(temp_vector), [](std::pair const &m) + { + return m.first + "\t" + m.second.Getdescription() + "\n"; + }); + + //Second solution: Copy all value fields from map to a vector using Lambda function + //std::for_each(create_methods_.begin(), create_methods_.end(), [&](std::pair const &m) { + // temp_vector.push_back(m.first + "\t" + m.second.Getdescription() + "\n"); + //}); + + return temp_vector; +} + +#pragma endregion block_factory diff --git a/final/tool/block.h b/final/tool/block.h new file mode 100644 index 0000000..a40cd19 --- /dev/null +++ b/final/tool/block.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include "self_registered_in_factory.h" + +class block +{ +public: + block(std::string block_type_name, int number_of_parameters, const std::vector& parameters); + virtual ~block() = default; + virtual double calc(double input) = 0; + std::string get_command(); +protected: + std::vector parameters_; +private: + std::string block_type_name_; +}; + +//holder for block meta data: description and create method +class block_meta +{ +public: + using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + explicit block_meta(std::string description = "", TBlockCreateMethod block_create_method = nullptr) + : description_{ std::move(description) }, block_create_method_{ block_create_method } { } + //{ + // description_ = description; + // block_create_method_ = block_create_method; + //} + TBlockCreateMethod GetCreateMethod() const + { + return block_create_method_; + } + std::string Getdescription() const + { + return description_; + } + +private: + std::string description_; + TBlockCreateMethod block_create_method_{}; +}; + +class block_processor +{ +private: + std::vector> blocks_; +public: + block_processor(); + void add_block(std::unique_ptr b); + double calc_all(double input) const; + using sequence_list = std::vector>>; + void load(const sequence_list& sl); + bool load_from_stream(std::istream &is); + bool remove_block(unsigned int index); + std::vector get_sequence(); + bool bulk_calc(std::istream &is, std::ostream &os) const; + static std::pair> get_block_command_from_text(std::string command_text); +}; + +//prevent class derivation with 'final' +class block_factory final +{ +public: + using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + //prevent class instantiation + block_factory() = delete; + //all members are static + //wrap static methods in this separate class because I want to have it in one place and 'create_methods_' has to be hidden + static bool register_block(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock); + static std::unique_ptr create(const std::string& block_name, const std::vector& parameters); + static std::vector get_block_infos(); +private: + static std::map create_methods_; +}; \ No newline at end of file diff --git a/final/tool/block_add.cpp b/final/tool/block_add.cpp new file mode 100644 index 0000000..3953759 --- /dev/null +++ b/final/tool/block_add.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "block_add.h" +#include "block.h" +#include "self_registered_in_factory.h" + +#pragma region add +add::add(const std::vector& parameters) : block(GetBlockTypeName(), 1, parameters) +{ + initial_value_ = parameters[0]; +} + +double add::calc(double input) +{ + //we have to have line below in order to trigger registration during class instantiation (before main) + is_blocktype_registered_; + return initial_value_ + input; +}; + +#pragma endregion add \ No newline at end of file diff --git a/final/tool/block_add.h b/final/tool/block_add.h new file mode 100644 index 0000000..dbdeb5a --- /dev/null +++ b/final/tool/block_add.h @@ -0,0 +1,24 @@ +#pragma once +#include "stdafx.h" +#include "block.h" +#include + +class add : + //derived from base block class + public block, + //derived from base block class + public self_registered_in_factory +{ +private: + double initial_value_{}; +public: + add(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + + inline static const char* name = "add"; + inline static const char* description = "Summarize input with parameter value. Example: add 5"; +}; diff --git a/final/tool/block_lim.cpp b/final/tool/block_lim.cpp new file mode 100644 index 0000000..ac8ae7f --- /dev/null +++ b/final/tool/block_lim.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "block_lim.h" +#include "block.h" +#include "self_registered_in_factory.h" +#include + +lim::lim(const std::vector& parameters) : block(GetBlockTypeName(), 2, parameters) +{ + initial_value_1_ = parameters[0]; + initial_value_2_ = parameters[1]; +} + +double lim::calc(double input) +{ + is_blocktype_registered_; + double ret = std::clamp(input, initial_value_1_, initial_value_2_); + //if (input < initial_value_1_) + // ret = initial_value_1_; + + //if (input > initial_value_2_) + // ret = initial_value_2_; + return ret; +} diff --git a/final/tool/block_lim.h b/final/tool/block_lim.h new file mode 100644 index 0000000..a2a1fdf --- /dev/null +++ b/final/tool/block_lim.h @@ -0,0 +1,21 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class lim : public block, public self_registered_in_factory +{ +private: + double initial_value_1_; + double initial_value_2_; +public: + lim(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + + inline static const char* name = "lim"; + inline static const char* description = "Limit between numbers. Example: lim 0 100"; +}; diff --git a/final/tool/block_mult.cpp b/final/tool/block_mult.cpp new file mode 100644 index 0000000..ac1844c --- /dev/null +++ b/final/tool/block_mult.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "block_mult.h" +#include "block.h" +#include "self_registered_in_factory.h" + +#pragma region mult +mult::mult(const std::vector& parameters) : block(GetBlockTypeName(), 1, parameters) +{ + initial_value_ = parameters[0]; +} +double mult::calc(double input) +{ + is_blocktype_registered_; + return initial_value_ * input; +} +#pragma endregion mult diff --git a/final/tool/block_mult.h b/final/tool/block_mult.h new file mode 100644 index 0000000..62b1891 --- /dev/null +++ b/final/tool/block_mult.h @@ -0,0 +1,20 @@ +#pragma once +#include "stdafx.h" +#include "block.h" +#include + +class mult : public block, public self_registered_in_factory +{ +private: + double initial_value_{}; +public: + mult(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + + inline static const char* name = "mult"; + inline static const char* description = "Multiply input value with parameter value. Example: mult 5"; +}; \ No newline at end of file diff --git a/final/tool/block_neg.cpp b/final/tool/block_neg.cpp new file mode 100644 index 0000000..b76f259 --- /dev/null +++ b/final/tool/block_neg.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "block_neg.h" +#include "block.h" +#include "self_registered_in_factory.h" + +neg::neg(const std::vector& parameters) : block(GetBlockTypeName(), 0, parameters) +{ +} + +double neg::calc(double input) +{ + is_blocktype_registered_; + return -1 * input; +} diff --git a/final/tool/block_neg.h b/final/tool/block_neg.h new file mode 100644 index 0000000..cd48c5d --- /dev/null +++ b/final/tool/block_neg.h @@ -0,0 +1,18 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class neg : public block, public self_registered_in_factory +{ +public: + neg(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + + inline static const char* name = "neg"; + inline static const char* description = "Negation of input value. Example: neg"; +}; diff --git a/final/tool/block_queq.cpp b/final/tool/block_queq.cpp new file mode 100644 index 0000000..1ac0d7a --- /dev/null +++ b/final/tool/block_queq.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "block_queq.h" +#include "block.h" +#include "self_registered_in_factory.h" + +queq::queq(const std::vector& parameters) : block(GetBlockTypeName(), 3, parameters) +{ + initial_value_a_ = parameters[0]; + initial_value_b_ = parameters[1]; + initial_value_c_ = parameters[2]; +} + +double queq::calc(double input) +{ + is_blocktype_registered_; + return initial_value_a_ * input * input + initial_value_b_ * input + initial_value_c_; +} + +//std::string self_registered_in_factory::description_ = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; diff --git a/final/tool/block_queq.h b/final/tool/block_queq.h new file mode 100644 index 0000000..6b9a545 --- /dev/null +++ b/final/tool/block_queq.h @@ -0,0 +1,22 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class queq : public block, public self_registered_in_factory +{ +private: + double initial_value_a_; + double initial_value_b_; + double initial_value_c_; +public: + queq(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + + inline static const char* name = "queq"; + inline static const char* description = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; +}; diff --git a/final/tool/menus.cpp b/final/tool/menus.cpp new file mode 100644 index 0000000..8168db2 --- /dev/null +++ b/final/tool/menus.cpp @@ -0,0 +1,209 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include +#include "block.h" +#include +#include "menus.h" +#include +#include + +void press_enter_to_go_back() +{ + std::cout << "\nPress ENTER to go back...\n"; + std::cin.ignore(); + system("cls"); +} + +unsigned int get_number_from_console(std::string caption) +{ + unsigned int user_number = 0; + std::string input = ""; + while (true) { + std::cout << caption; + getline(std::cin, input); + // This code converts from string to number safely. + std::stringstream input_ss(input); + //instead, we could use: + //https://en.cppreference.com/w/cpp/string/basic_string/stol + //https://en.cppreference.com/w/cpp/string/basic_string/stof + //but in that case we would have to catch exception. + //with current code we have 'try' approach + if (input_ss >> user_number) + break; + std::cout << "Invalid number, please try again..." << std::endl; + } + return user_number; +} + +double get_input_from_console(std::string caption) +{ + double input_value = 0.; + std::string input; + while (true) { + std::cout << caption; + getline(std::cin, input); + // This code converts from string to number safely. + std::stringstream input_ss(input); + if (input_ss >> input_value) + break; + std::cout << "Invalid input value, please try again..." << std::endl; + } + return input_value; +} + +std::string get_text_from_console(std::string caption) +{ + std::string text_input; + std::cout << caption << "\n"; + getline(std::cin, text_input); + return text_input; +} + +void show_available_block_types() +{ + //show available block types + system("cls"); + std::string block_list_menu = "Available block types:\n"; + std::vector block_list = block_factory::get_block_infos(); + //for (std::vector::iterator it = block_list.begin(); it != block_list.end(); ++it) + // block_list_menu += *it; + block_list_menu = std::accumulate(block_list.begin(), block_list.end(), block_list_menu); + + std::cout << block_list_menu << std::endl; +} + +void show_block_sequence(block_processor &bp) +{ + //show block sequence + system("cls"); + std::cout << "Block sequence:\n" << std::endl; + + std::vector temp_v = bp.get_sequence(); + std::for_each(temp_v.begin(), temp_v.end(), [index = 0](std::string const &c) mutable { index++; std::cout << index << ":\t" << c << std::endl; }); + + //int index = 0; + //std::vector temp_v = bp.get_sequence(); + //std::for_each(temp_v.begin(), temp_v.end(), [&](std::string const &c) + //{ + // index++; + // std::cout << index << ":\t" << c << std::endl; + //}); +} + +bool add_new_block(block_processor &bp) +{ + //add new block + system("cls"); + std::string block_command; + std::string input = ""; + while (true) { + std::cout << "Enter new block (or just ENTER to go back):\ "; + getline(std::cin, input); + if (input == "") + return false; + try + { + std::pair> p = block_processor::get_block_command_from_text(input); + std::unique_ptr bl = block_factory::create(p.first, p.second); + if (bl == nullptr) return false; + bp.add_block(std::move(bl)); + return true; + } + catch (...) + { + std::cout << "Invalid entry, please try again..." << std::endl; + return false; + } + } +} +void remove_block_from_sequence(block_processor &bp) +{ + system("cls"); + const unsigned int block_index = get_number_from_console("Enter index of block to be removed:"); + if (bp.remove_block(block_index)) + std::cout << "Block on index" << block_index << " is removed." << std::endl; + else + std::cout << "ERROR: Block on index" << block_index << " is NOT removed." << std::endl; +} +// +void calc_sequence(const block_processor &bp) +{ + system("cls"); + const double input_value = get_input_from_console("Please enter a valid number: "); + const double result = bp.calc_all(input_value); + std::cout << "Result: " << result << std::endl; +} + +void load_from_file(block_processor &bp, std::string &file_name) +{ + system("cls"); + block_processor::sequence_list stored_blocks; + + std::ifstream parameters_stream(file_name, std::ios::in); + std::string block_line; + + if (!parameters_stream.is_open()) + { + std::cout << "INFO: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + return; + } + try + { + bp.load_from_stream(parameters_stream); + std::cout << "Block sequence is loaded from '" << file_name << "' file.\n" << std::endl; + } + catch (const std::exception&) + { + std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + } + parameters_stream.close(); +} + +void load_and_calc_inputs_from_file(block_processor &bp) +{ + system("cls"); + + //Load inputs from file + double input_value; + std::vector inputs_for_calc; + const std::string file_with_inputs = get_text_from_console("Please enter file name with input values (each value in new row): "); + std::ifstream inputs_is(file_with_inputs); + std::string input_line; + if (!inputs_is.is_open()) { std::cout << "\nERROR: Cannot open input file: " << file_with_inputs; return; } + + //Prepare result stream + const std::size_t lastindex = file_with_inputs.find_last_of("."); + const std::string raw_file_name = file_with_inputs.substr(0, lastindex); + const std::string result_file_name = raw_file_name + "_results.txt"; + std::ofstream results_os(result_file_name, std::ios::out); + if (!results_os.is_open()) { std::cout << "\nERROR: Cannot open output file:: " << result_file_name; return; } + + // Calculate and save results + if (!bp.bulk_calc(inputs_is, results_os)) + std::cout << "\nERROR: Input file: '" << file_with_inputs << "' has one or more invalid values."; + else + std::cout << "\nResults are saved to file: " << result_file_name; + + //close streams + inputs_is.close(); + results_os.close(); +} + +void save_sequence_to_file(block_processor &bp, std::string file_name) +{ + system("cls"); + + std::ofstream parameters_stream(file_name, std::ios::out); + if (!parameters_stream.is_open()) { std::cout << "\nERROR: Block sequence is NOT saved to file: " << file_name; } + + auto command_sequence = bp.get_sequence(); + for (std::vector::iterator it = command_sequence.begin(); it != command_sequence.end(); ++it) + parameters_stream << *it << std::endl; + parameters_stream.close(); + std::cout << "\nBlock sequence is saved to file: " << file_name; +} \ No newline at end of file diff --git a/final/tool/menus.h b/final/tool/menus.h new file mode 100644 index 0000000..8a20106 --- /dev/null +++ b/final/tool/menus.h @@ -0,0 +1,30 @@ +#pragma once +#include + +inline const std::string MAIN_MENU = +"MAIN MENU:\n\n" +"Chose command (enter number):\n" +"1:\tShow available block types.\n" +"2:\tShow block sequence.\n" +"3:\tAdd new block.\n" +"4:\tRun block sequence.\n" +"5:\tRemove block from sequence.\n" +"6:\tSave block sequence to file.\n" +"7:\tLoad block sequence from file.\n" +"8:\tLoad and calculate inputs from file.\n" +"0:\tExit.\n"; + +inline const std::string BLOCK_SEQUENCE_FILE = "block_sequence.txt"; + +void press_enter_to_go_back(); +void show_available_block_types(); +unsigned int get_number_from_console(std::string caption); +double get_input_from_console(std::string caption); +std::string get_text_from_console(std::string caption); +void show_block_sequence(block_processor &bp); +bool add_new_block(block_processor &bp); +void calc_sequence(const block_processor &bp); +void remove_block_from_sequence(block_processor &bp); +void load_from_file(block_processor &bp, std::string &file_name); +void save_sequence_to_file(block_processor &bp, std::string file_name); +void load_and_calc_inputs_from_file(block_processor &bp); \ No newline at end of file diff --git a/final/tool/self_registered_in_factory.h b/final/tool/self_registered_in_factory.h new file mode 100644 index 0000000..b6176d6 --- /dev/null +++ b/final/tool/self_registered_in_factory.h @@ -0,0 +1,23 @@ +#pragma once + +//with below line we promise that definition of class WILL BE included in some files +//this is 'Forward declaration': https://en.wikipedia.org/wiki/Forward_declaration +class block_factory; + +//template +template +class self_registered_in_factory +{ +protected: + //because it is static member, it will be initialized in the moment when template is compiled + //template is compiled when concrete class (which is derived from SelfRegisteredInFactory class) is instantiated (before main method) + static bool is_blocktype_registered_; +public: + static std::string GetBlockTypeName() { return TBlock::name; } + static std::string GetDescription() { return TBlock::description; } +}; + +//this is implementation of assignment of static variable is_blocktype_registered_ which is triggered before 'main'. +template +bool self_registered_in_factory::is_blocktype_registered_ = +block_factory::register_block(TBlock::GetBlockTypeName(), TBlock::GetDescription(), TBlock::CreateMethod); diff --git a/final/tool/stdafx.cpp b/final/tool/stdafx.cpp new file mode 100644 index 0000000..5a8cdb0 Binary files /dev/null and b/final/tool/stdafx.cpp differ diff --git a/final/tool/stdafx.h b/final/tool/stdafx.h new file mode 100644 index 0000000..94d4ed8 Binary files /dev/null and b/final/tool/stdafx.h differ diff --git a/final/tool/targetver.h b/final/tool/targetver.h new file mode 100644 index 0000000..567cd34 Binary files /dev/null and b/final/tool/targetver.h differ diff --git a/final/tool/tool.cpp b/final/tool/tool.cpp new file mode 100644 index 0000000..e2d9888 Binary files /dev/null and b/final/tool/tool.cpp differ diff --git a/final/tool/tool.vcxproj b/final/tool/tool.vcxproj new file mode 100644 index 0000000..bc6ffd1 --- /dev/null +++ b/final/tool/tool.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} + Win32Proj + tool + 10.0.17134.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + ..\$(Configuration)\$(Platform) + + + true + ..\$(Configuration)\$(Platform) + + + false + ..\$(Configuration)\$(Platform) + + + false + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + \ No newline at end of file diff --git a/final/tool/tool.vcxproj.filters b/final/tool/tool.vcxproj.filters new file mode 100644 index 0000000..8740c95 --- /dev/null +++ b/final/tool/tool.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;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 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file