/*! * @file factory.h * @brief Contains a base class for a factory. * * A factory contains an array of inputs, an array of outputs and an operation. * You can update some elements in the array of inputs and then call * `Factory::Update()` to update the outputs (it will run the operation only if * some input elements are changed since last update). * * @author cathook */ #ifndef __MEOWPP_UTILITY_FACTORY_H__ #define __MEOWPP_UTILITY_FACTORY_H__ #include "operation.h" #include "pointer.h" #include "self.h" namespace meow { namespace factory_types { /*! * @brief A base class for kinds of factory classes. */ class Base { protected: struct BaseData { Pointer oper; Pointer> inputs; Pointer> outputs; //! An array with each elements points to the input elements with //! non-constant type. Pointer> non_const_inputs; //! An array with each elements points to the output elements with //! constant type. Pointer> const_outputs; /*! * @brief Constructor. * @param [in] arg_oper The operation this factory should run. * @param [in] arg_inputs A pointer points to the array of the input * elements. * @param [in] arg_outputs A pointer points to the array of the output * elements. */ BaseData(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs): oper(arg_oper), inputs(arg_inputs), outputs(arg_outputs), non_const_inputs(new Pointer[oper->inputs_size()], ARRAY, true), const_outputs( new Pointer[oper->outputs_size()], ARRAY, true) { for (int i = 0, i_max = oper->inputs_size(); i < i_max; ++i) { non_const_inputs[i] = Pointer( const_cast(inputs[i].address()), SINGLE, false); } for (int i = 0, i_max = oper->outputs_size(); i < i_max; ++i) { const_outputs[i] = Pointer( outputs[i].address(), SINGLE, false); } }; }; Base() {} public: virtual ~Base() {} /*! * @brief Updates the output elements by re-run the operation. */ virtual State Update() const = 0; /*! * @brief Returns whether the last time calling `Update()` did update really * or not. * * If the input elements were not changed before `Update()` be called, it * might be returns false (depends on how it implements). */ virtual bool HasRedo() const = 0; /*! * @brief Gets the operation. */ virtual Pointer operation() const = 0; /*! * @brief Gets the pointer points to the array of input elements. */ virtual Pointer> inputs() const = 0; /*! * @brief Gets the pointer points to the array of output elements. */ virtual Pointer> outputs() const = 0; }; /*! * An implement of the factory class. * * When the function `Update()` be called, no matter whether the input elements * has changed or not, it will do the operation. */ class CheckOff : public Base { private: struct Data : Base::BaseData { Data(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs) : BaseData(arg_oper, arg_inputs, arg_outputs) {} }; Self const self_; public: /*! * @brief Constructor. * @param [in] arg_oper The operation for this factory to run. * @param [in] arg_inputs A pointer points to the array of the pointer points * to the input elements. * @param [in] arg_outputs A pointer points to the array of the pointer points * to the output elements. */ CheckOff(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs) : self_(Data(arg_oper, arg_inputs, arg_outputs)) {} /*! * @brief Updates the output elements by running the operation. */ State Update() const { return self_->oper->Operate(self_->inputs, self_->outputs); } /*! * @brief It will always return true. */ bool HasRedo() const { return true; } /*! * @brief Gets the operation. */ virtual Pointer operation() const { return self_->oper; } /*! * @brief Gets the pointer points to the array of input elements. */ Pointer> inputs() const { return self_->non_const_inputs; } /*! * @brief Gets the pointer points to the array of output elements. */ Pointer> outputs() const { return self_->const_outputs; } }; /*! * An implement of the factory class. * * It will check whether the input elements has changed before running the * operation. */ class CheckOn : public Base { private: struct Data : Base::BaseData { //! Stores the last input elements which are using to check whether the //! input elements have changed or not. Pointer> old_inputs; //! Stores the state returned by the operation last time. State last_state; //! Stores whether the last `Update()` run the operation or not. bool has_redo; //! Stores whether it has not run the `Update()` yet. bool first_time; Data(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs) : BaseData(arg_oper, arg_inputs, arg_outputs), old_inputs(new Pointer[oper->inputs_size()], ARRAY, true), has_redo(false), first_time(true) { for (int i = 0, i_max = oper->inputs_size(); i < i_max; ++i) { old_inputs[i] = Pointer(inputs[i]->Copy(), SINGLE, true); } } }; Self const self_; public: /*! * @brief Constructor. * @param [in] arg_oper The operation this factory should run. * @param [in] arg_inputs A pointer points to the array of the input * elements. * @param [in] arg_outputs A pointer points to the array of the output * elements. */ CheckOn(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs) : self_(Data(arg_oper, arg_inputs, arg_outputs)) {} /*! * @brief Updates the output elements if needs. * * It will check whether the input elements different from the old ones first. */ State Update() const { bool needs_to_update = self_->first_time; if (!needs_to_update) { for (int i = 0, i_max = self_->oper->inputs_size(); i < i_max; ++i) { Object const* old = self_->old_inputs[i].address(); Object const* cur = self_->inputs[i].address(); if (!old->Equals(cur)) { needs_to_update = true; break; } } } if (!needs_to_update) { self_()->has_redo = false; } else { for (int i = 0, i_max = self_->oper->inputs_size(); i < i_max; ++i) { Object* old = self_->old_inputs[i].address(); Object const* cur = self_->inputs[i].address(); old->CopyFrom(cur); } self_()->last_state = self_->oper->Operate(self_->inputs, self_->outputs); self_()->has_redo = true; } self_()->first_time = false; return self_->last_state; } /*! * @brief Returns whether the output elements have been re-generated by * running the operation again. * * It will check whether the input elements different from the old ones first. */ bool HasRedo() const { return self_->has_redo; } /*! * @brief Gets the operation. */ virtual Pointer operation() const { return self_->oper; } /*! * @brief Gets the array of the input elements. */ Pointer> inputs() const { return self_->non_const_inputs; } /*! * @brief Gets the array of the output elements. */ Pointer> outputs() const { return self_->const_outputs; } }; } // factory_types /*! * @brief A class which contains input elements, output elements and an * operation. */ class Factory : public Object { private: Pointer factory_; public: /*! * @brief Constructor. */ Factory(Pointer const& arg_oper, Pointer> const& arg_inputs, Pointer> const& arg_outputs, bool arg_check_before_update) { if (arg_check_before_update) { factory_ = Pointer( new factory_types::CheckOn( arg_oper, arg_inputs, arg_outputs), SINGLE, true); } else { factory_ = Pointer( new factory_types::CheckOff( arg_oper, arg_inputs, arg_outputs), SINGLE, true); } } /*! * @brief Updates the output elements. */ State Update() const { return factory_->Update(); } /*! * @brief Returns whether the output elements have been re-generated by * running the operation again. */ bool HasRedo() const { return factory_->HasRedo(); } /*! * @brief Gets the operation. */ Pointer operation() const { return factory_->operation(); } /*! * @brief Gets the array of the input elements. */ Pointer> inputs() const { return factory_->inputs(); } /*! * @brief Gets the array of the output elements. */ Pointer> outputs() const { return factory_->outputs(); } Object* Copy() const { return NULL; } Object* CopyFrom(Object const* another_factory) { return NULL; } bool Equals(Object const* another_factory) { return false; } #ifdef MEOWPP_UTILITY_FACTORY_TESTING friend class FactoryTest; #endif }; } // meow #endif // __MEOWPP_UTILITY_FACTORY_H__