Last active
June 25, 2020 12:30
-
-
Save kachsheev/d363fbdf36ced61796c893163a22ef0c to your computer and use it in GitHub Desktop.
CommandPattern.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef COMMAND_PATTERN_HPP | |
#define COMMAND_PATTERN_HPP | |
// declaration | |
namespace command | |
{ | |
/// | |
/// \brief The Invoker class | |
/// | |
template<typename InvokerImpl> | |
class Invoker; // need CRTP | |
/// | |
/// \brief The Command class | |
/// | |
template<typename CommandImpl, typename InvokerType, typename ResultType> | |
class Command; // need CRTP | |
/// | |
/// \brief The Executor class | |
/// | |
template<typename InvokerType, typename CommandType> | |
class Executor; | |
} // namespace command | |
// difinition | |
namespace command | |
{ | |
namespace details | |
{ | |
/// | |
/// \brief The ReturnValueType struct | |
/// | |
template <typename T> | |
struct ReturnValueType; | |
/// | |
/// \brief The ReturnValueType<ReturnType(*)(Args...)> struct | |
/// | |
template <typename ReturnType, typename... Args> | |
struct ReturnValueType<ReturnType(*)(Args...)> | |
{ | |
using Type = ReturnType; | |
}; | |
/// | |
/// \brief The ReturnValueType<ReturnType(ObjectType::*)(Args...)> struct | |
/// | |
template <typename ReturnType, typename ObjectType, typename... Args> | |
struct ReturnValueType<ReturnType(ObjectType::*)(Args...)> | |
{ | |
using Type = ReturnType; | |
}; | |
/// | |
/// \brief The ReturnValueType<ReturnType(ObjectType::*)(Args...) const> struct | |
/// | |
template <typename ReturnType, typename ObjectType, typename... Args> | |
struct ReturnValueType<ReturnType(ObjectType::*)(Args...) const> | |
{ | |
using Type = ReturnType; | |
}; | |
/// | |
/// \brief The ReturnValueType<ReturnType(ObjectType::*)(Args...) volatile> struct | |
/// | |
template <typename ReturnType, typename ObjectType, typename... Args> | |
struct ReturnValueType<ReturnType(ObjectType::*)(Args...) volatile> | |
{ | |
using Type = ReturnType; | |
}; | |
/// | |
/// \brief The ReturnValueType<ReturnType(ObjectType::*)(Args...) const volatile> struct | |
/// | |
template <typename ReturnType, typename ObjectType, typename... Args> | |
struct ReturnValueType<ReturnType(ObjectType::*)(Args...) const volatile> | |
{ | |
using Type = ReturnType; | |
}; | |
/// | |
/// \brief The ReturnValue alias type | |
/// | |
template <typename T> | |
using ReturnValue = typename ReturnValueType<T>::Type; | |
/// | |
/// \brief The ExecutorMembers struct | |
/// | |
template<typename InvokerType, typename CommandType> | |
struct ExecutorMembers | |
{ | |
/// | |
/// \brief ExecutorMembers | |
/// \param invoker | |
/// \param command | |
/// | |
ExecutorMembers(InvokerType &invoker, const CommandType &command); | |
mutable InvokerType *invoker; ///< | |
const CommandType *command; ///< | |
}; | |
/// | |
/// \brief The ExecutorImpl struct | |
/// | |
template<typename InvokerType, typename CommandType, typename ReturnType> | |
class ExecutorImpl : private ExecutorMembers<InvokerType, CommandType> | |
{ | |
public: | |
/// | |
/// \brief run | |
/// \return | |
/// | |
ReturnType run() const; | |
/// | |
/// \brief operator() | |
/// \return | |
/// | |
ReturnType operator()() const; | |
protected: | |
using ExecutorMembers<InvokerType, CommandType>::ExecutorMembers; | |
private: | |
using ExecutorMembers<InvokerType, CommandType>::invoker; | |
using ExecutorMembers<InvokerType, CommandType>::command; | |
}; | |
template<typename InvokerType, typename CommandType> | |
class ExecutorImpl<InvokerType, CommandType, void> | |
: private ExecutorMembers<InvokerType, CommandType> | |
{ | |
public: | |
/// | |
/// \brief run | |
/// | |
void run() const; | |
/// | |
/// \brief operator () | |
/// | |
void operator()() const; | |
protected: | |
using ExecutorMembers<InvokerType, CommandType>::ExecutorMembers; | |
private: | |
using ExecutorMembers<InvokerType, CommandType>::invoker; | |
using ExecutorMembers<InvokerType, CommandType>::command; | |
}; | |
} // namespace details | |
// Invoker | |
template<typename InvokerImpl> | |
class Invoker | |
{ | |
public: | |
/// | |
/// \brief executionContext | |
/// \param command | |
/// \return | |
/// | |
template<typename CommandImpl, typename ResultType> | |
Executor<InvokerImpl, Command<CommandImpl, InvokerImpl, ResultType>> | |
executionContext(const Command<CommandImpl, InvokerImpl, ResultType> &command); | |
/// | |
/// \brief operator InvokerImpl & | |
/// | |
operator InvokerImpl&(); | |
}; | |
// Command | |
template<typename CommandImpl, typename InvokerType, typename ResultType> | |
class Command | |
{ | |
public: | |
/// | |
/// \brief runCommand | |
/// \param invoker | |
/// \return | |
/// | |
ResultType runCommand(InvokerType &invoker) const; | |
}; | |
// Executor | |
template<typename InvokerType, typename CommandType> | |
class Executor : protected details::ExecutorImpl<InvokerType, CommandType, details::ReturnValue<decltype(&CommandType::runCommand)>> | |
{ | |
public: | |
using ReturnValue = details::ReturnValue<decltype(&CommandType::runCommand)>; | |
using Parent = details::ExecutorImpl<InvokerType, CommandType, ReturnValue>; | |
public: | |
template<typename InvokerImpl> | |
friend class Invoker; | |
public: | |
using Parent::run; | |
using Parent::operator(); | |
protected: | |
using Parent::ExecutorImpl; | |
}; | |
} // namespace command | |
// implementation | |
namespace command | |
{ | |
namespace details | |
{ | |
// ExecutorMembers | |
template<typename InvokerType, typename CommandType> | |
ExecutorMembers<InvokerType, CommandType>::ExecutorMembers(InvokerType &invoker | |
, const CommandType &command) | |
: invoker(&invoker), command(&command) | |
{} | |
// ExecutorImpl | |
template<typename InvokerType, typename CommandType, typename ReturnType> | |
ReturnType ExecutorImpl<InvokerType, CommandType, ReturnType>::run() const | |
{ | |
return command->runCommand(*invoker); | |
} | |
template<typename InvokerType, typename CommandType, typename ReturnType> | |
ReturnType ExecutorImpl<InvokerType, CommandType, ReturnType>::operator()() const | |
{ | |
return run(); | |
} | |
template<typename InvokerType, typename CommandType> | |
void ExecutorImpl<InvokerType, CommandType, void>::run() const | |
{ | |
command->runCommand(*invoker); | |
} | |
template<typename InvokerType, typename CommandType> | |
void ExecutorImpl<InvokerType, CommandType, void>::operator()() const | |
{ | |
run(); | |
} | |
} // namespace details | |
// Invoker | |
template<typename InvokerImpl> | |
template<typename CommandImpl, typename ResultType> | |
Executor<InvokerImpl, Command<CommandImpl, InvokerImpl, ResultType>> | |
Invoker<InvokerImpl>::executionContext(const Command<CommandImpl, InvokerImpl, ResultType> &command) | |
{ | |
return Executor< | |
InvokerImpl, Command<CommandImpl, InvokerImpl, ResultType> | |
>{ operator InvokerImpl &(), command }; | |
} | |
template<typename InvokerImpl> | |
Invoker<InvokerImpl>::operator InvokerImpl&() | |
{ | |
return *static_cast<InvokerImpl *>(this); | |
} | |
// Command | |
template<typename CommandImpl, typename InvokerType, typename ResultType> | |
ResultType Command<CommandImpl, InvokerType, ResultType>::runCommand(InvokerType &invoker) const | |
{ | |
return static_cast<CommandImpl const*>(this)->runCommandImpl(invoker); | |
} | |
} // namespace command | |
#endif // COMMAND_PATTERN_HPP |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Example.hpp" | |
#include <iostream> | |
namespace command | |
{ | |
namespace example | |
{ | |
bool Invoker::print(const std::string &string) | |
{ | |
return (std::cout << string << std::endl).operator bool(); | |
} | |
bool CommandCommand1::runCommandImpl(Invoker &invoker) const | |
{ | |
std::string command = "Command1"; | |
return invoker.print(command); | |
} | |
bool CommandCommand2::runCommandImpl(Invoker &invoker) const | |
{ | |
std::string command = "Command2"; | |
return invoker.print(command); | |
} | |
} // namespace example | |
} // namespace command |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef COMMANDPATTERNEXAMPLE_H | |
#define COMMANDPATTERNEXAMPLE_H | |
#include "CommandPattern.hpp" | |
#include <string> | |
namespace command | |
{ | |
namespace example | |
{ | |
class Invoker : public command::Invoker<Invoker> | |
{ | |
public: | |
bool print(const std::string &string); | |
}; | |
class CommandCommand1 : public command::Command<CommandCommand1, Invoker, bool> | |
{ | |
friend command::Command<CommandCommand1, Invoker, bool>; | |
protected: | |
bool runCommandImpl(Invoker &invoker) const; | |
}; | |
class CommandCommand2 : public command::Command<CommandCommand2, Invoker, bool> | |
{ | |
friend command::Command<CommandCommand2, Invoker, bool>; | |
protected: | |
bool runCommandImpl(Invoker &invoker) const; | |
}; | |
} // namespace example | |
} // namespace command | |
#endif // COMMANDPATTERNEXAMPLE_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Example.hpp" | |
using command::example::Invoker; | |
using command::example::CommandCommand1; | |
using command::example::CommandCommand2; | |
int main() | |
{ | |
Invoker invoker; | |
{ | |
invoker.executionContext(CommandCommand1{}).run(); | |
} | |
{ | |
invoker.executionContext(CommandCommand2{}).run(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment