Skip to content

Instantly share code, notes, and snippets.

@kachsheev
Last active June 25, 2020 12:30
Show Gist options
  • Save kachsheev/d363fbdf36ced61796c893163a22ef0c to your computer and use it in GitHub Desktop.
Save kachsheev/d363fbdf36ced61796c893163a22ef0c to your computer and use it in GitHub Desktop.
CommandPattern.hpp
#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
#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
#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
#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