Created
July 14, 2017 05:46
-
-
Save NeuroWhAI/1c7f90133b312bd86c67b944703be52e to your computer and use it in GitHub Desktop.
Action Net
This file contains 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 <unordered_map> | |
#include <deque> | |
#include <functional> | |
#include <type_traits> | |
#include <tuple> | |
#include <utility> | |
#include <memory> | |
#include <algorithm> | |
template <typename... ARGS> | |
class Param | |
{ | |
protected: | |
template <typename... ARGS2> | |
using RawFuncTool = std::function<bool(ARGS..., ARGS2...)>; | |
using PackFunc = std::function<bool(ARGS...)>; | |
private: | |
bool m_willDie; | |
PackFunc m_packedJob; | |
PackFunc m_packedStarter; | |
public: | |
Param() | |
: m_willDie(false) | |
{ | |
} | |
protected: | |
template <typename TRawFunc, typename... ARGS2> | |
bool unpacker(const TRawFunc& job, ARGS... args, ARGS2... args2) | |
{ | |
return job(std::forward<ARGS>(args)..., *args2...); | |
} | |
template <typename TRawFunc, typename TTuple, std::size_t... I> | |
PackFunc packImpl(const TRawFunc& job, TTuple& tup, std::index_sequence<I...>) | |
{ | |
return [=](ARGS... args) -> bool { | |
return unpacker<TRawFunc, std::tuple_element_t<I, TTuple>...>(job, std::forward<ARGS>(args)..., std::get<I>(tup)...); | |
}; | |
} | |
template <typename TRawFunc, typename TTuple, typename... ARGS2> | |
void packJob(const TRawFunc& job, TTuple& tup) | |
{ | |
m_packedJob = packImpl(job, tup, std::index_sequence_for<ARGS2...>{}); | |
} | |
template <typename TRawFunc, typename TTuple, typename... ARGS2> | |
void packStarter(const TRawFunc& job, TTuple& tup) | |
{ | |
m_packedStarter = packImpl(job, tup, std::index_sequence_for<ARGS2...>{}); | |
} | |
public: | |
bool operator() (ARGS... args) | |
{ | |
bool willSurvive = m_packedJob(std::forward<ARGS>(args)...); | |
m_willDie = !willSurvive; | |
return willSurvive; | |
} | |
bool start(ARGS... args) | |
{ | |
if (m_packedStarter) | |
{ | |
bool willSurvive = m_packedStarter(std::forward<ARGS>(args)...); | |
m_willDie = !willSurvive; | |
return willSurvive; | |
} | |
return true; | |
} | |
public: | |
bool isDead() const | |
{ | |
return m_willDie; | |
} | |
public: | |
virtual std::unique_ptr<Param> clone() const = 0; | |
}; | |
namespace detail | |
{ | |
template <typename> | |
struct is_param_class : std::false_type {}; | |
template <typename... ARGS> | |
struct is_param_class<Param<ARGS...>> : std::true_type {}; | |
} | |
template <typename TParam, typename... ARGS> | |
class Action : public TParam | |
{ | |
static_assert(detail::is_param_class<TParam>::value, "TParam is not Param<...>!"); | |
private: | |
using RawFunc = typename TParam::template RawFuncTool<ARGS...>; | |
template <typename T> | |
using OrgT = std::remove_reference_t<T>; | |
template <typename T> | |
using MovC = std::remove_const_t<T>; | |
public: | |
Action(const RawFunc& job) | |
: m_job(job) | |
, m_args(std::make_shared<MovC<OrgT<ARGS>>>()...) | |
{ | |
packJobFunc(job); | |
} | |
Action(const Action& other) | |
: m_job(other.m_job) | |
, m_starter(other.m_starter) | |
, m_args(std::make_shared<MovC<OrgT<ARGS>>>()...) | |
{ | |
copyArguments(other.m_args); | |
packJobFunc(other.m_job); | |
packStarterFunc(other.m_starter); | |
} | |
Action(Action&& other) | |
: m_job(std::move(other.m_job)) | |
, m_starter(std::move(other.m_starter)) | |
, m_args(std::move(other.m_args)) | |
{ | |
packJobFunc(m_job); | |
packStarterFunc(m_starter); | |
} | |
private: | |
RawFunc m_job; | |
RawFunc m_starter; | |
std::tuple<std::shared_ptr<MovC<OrgT<ARGS>>>...> m_args; | |
private: | |
void packJobFunc(const RawFunc& job) | |
{ | |
TParam::template packJob<RawFunc, decltype(m_args), ARGS...>(job, m_args); | |
} | |
void packStarterFunc(const RawFunc& starter) | |
{ | |
if (starter) | |
{ | |
TParam::template packStarter<RawFunc, decltype(m_args), ARGS...>(starter, m_args); | |
} | |
} | |
private: | |
template <std::size_t... I> | |
void copyArgumentsImpl(const std::tuple<std::shared_ptr<MovC<OrgT<ARGS>>>...>& args, std::index_sequence<I...>) | |
{ | |
using ExpandType = int[]; | |
ExpandType{ 0, (*std::get<I>(m_args) = *std::get<I>(args), 0)... }; | |
} | |
void copyArguments(const std::tuple<std::shared_ptr<MovC<OrgT<ARGS>>>...>& args) | |
{ | |
copyArgumentsImpl(args, std::index_sequence_for<ARGS...>{}); | |
} | |
public: | |
Action& whenStart(const RawFunc& starter) | |
{ | |
m_starter = starter; | |
packStarterFunc(starter); | |
return *this; | |
} | |
public: | |
Action& operator=(const Action& other) | |
{ | |
if (this == &other) | |
return *this; | |
m_job = other.m_job; | |
m_starter = other.m_starter; | |
copyArguments(other.m_args); | |
packJobFunc(other.m_job); | |
packStarterFunc(other.m_starter); | |
return *this; | |
} | |
Action& operator=(Action&& other) | |
{ | |
if (this == &other) | |
return *this; | |
m_job = std::move(other.m_job); | |
m_starter = std::move(other.m_starter); | |
m_args = std::move(other.m_args); | |
packJobFunc(m_job); | |
packStarterFunc(m_starter); | |
return *this; | |
} | |
public: | |
virtual std::unique_ptr<TParam> clone() const override | |
{ | |
return std::make_unique<Action>(*this); | |
} | |
}; | |
template <typename TKey, typename... ARGS> | |
class ActionNet | |
{ | |
private: | |
using TParam = Param<ARGS...>; | |
using ParamPtr = std::unique_ptr<TParam>; | |
public: | |
ActionNet() | |
{ | |
} | |
ActionNet(const ActionNet& other) | |
{ | |
this->operator=(other); | |
} | |
ActionNet(ActionNet&& other) | |
: m_actionMap(std::move(other.m_actionMap)) | |
, m_net(std::move(other.m_net)) | |
, m_waitForStart(std::move(other.m_waitForStart)) | |
{ | |
} | |
private: | |
std::unordered_map<TKey, ParamPtr> m_actionMap; | |
std::deque<ParamPtr> m_net; | |
std::deque<ParamPtr> m_waitForStart; | |
public: | |
void set(const TKey& key, const TParam& action) | |
{ | |
m_actionMap[key] = std::move(action.clone()); | |
} | |
bool begin(const TKey& key) | |
{ | |
auto search = m_actionMap.find(key); | |
if (search != m_actionMap.end()) | |
{ | |
m_waitForStart.emplace_back(search->second->clone()); | |
return true; | |
} | |
return false; | |
} | |
public: | |
void update(ARGS... args) | |
{ | |
updateQueue(std::forward<ARGS>(args)...); | |
updateAction(std::forward<ARGS>(args)...); | |
removeDeadAction(); | |
} | |
private: | |
void updateQueue(ARGS... args) | |
{ | |
for (auto& action : m_waitForStart) | |
{ | |
action->start(std::forward<ARGS>(args)...); | |
m_net.emplace_back(std::move(action)); | |
} | |
m_waitForStart.clear(); | |
} | |
void updateAction(ARGS... args) | |
{ | |
for (auto& action : m_net) | |
{ | |
(*action)(std::forward<ARGS>(args)...); | |
} | |
} | |
void removeDeadAction() | |
{ | |
auto newEnd = std::remove_if(m_net.begin(), m_net.end(), [](const auto& action) { | |
return action->isDead(); | |
}); | |
m_net.erase(newEnd, m_net.end()); | |
} | |
public: | |
ActionNet& operator=(const ActionNet& other) | |
{ | |
if (this == &other) | |
return *this; | |
for (const auto& action : other.m_actionMap) | |
{ | |
set(action.first, *action.second); | |
} | |
for (const auto& action : other.m_net) | |
{ | |
m_net.emplace_back(action->clone()); | |
} | |
for (const auto& action : other.m_waitForStart) | |
{ | |
m_waitForStart.emplace_back(action->clone()); | |
} | |
return *this; | |
} | |
ActionNet& operator=(ActionNet&& other) | |
{ | |
if (this == &other) | |
return *this; | |
m_actionMap = std::move(other.m_actionMap); | |
m_net = std::move(other.m_net); | |
m_waitForStart = std::move(other.m_waitForStart); | |
return *this; | |
} | |
}; | |
This file contains 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 <iostream> | |
#include "ActionNet.hpp" | |
void testAction() | |
{ | |
Action<Param<>> action0{ []() { | |
std::cout << "Hello, World!" << std::endl; | |
return true; | |
} }; | |
action0(); | |
Action<Param<int, int>, bool&> action1{ [](int a, int b, bool& c) { | |
if (c) | |
{ | |
std::cout << (a + b) << std::endl; | |
} | |
c = !c; | |
return true; | |
} }; | |
action1(42, 1); | |
action1(42, 2); | |
action1(42, 3); | |
action1(42, 4); | |
Action<Param<>, int&, bool&> action2{ [](int& a, bool& b) { | |
if (b) | |
{ | |
std::cout << a << std::endl; | |
} | |
++a; | |
b = !b; | |
return true; | |
} }; | |
action2(); | |
action2(); | |
action2(); | |
action2(); | |
auto action3 = action2; | |
action3(); | |
action3(); | |
action3(); | |
action3(); | |
action2(); | |
action2(); | |
action2 = action3; | |
action2(); | |
action2(); | |
Action<Param<int>, const int&> action4{ [](int a, const int& b) { | |
std::cout << a << ", " << b << std::endl; | |
//b = 1234; | |
return true; | |
} }; | |
action4(2); | |
} | |
void testNet() | |
{ | |
using KeyType = int; | |
enum MyActions : KeyType | |
{ | |
SayHello, | |
PrintNum, | |
Sum, | |
}; | |
ActionNet<KeyType, int> net; | |
net.set(MyActions::SayHello, Action<Param<int>, int&>{ [&net](int a, int& b) { | |
std::cout << "Hello! " << a << std::endl; | |
net.begin(MyActions::PrintNum); | |
return false; | |
} }); | |
net.set(MyActions::PrintNum, Action<Param<int>, int&>{ [](int a, int& b) { | |
std::cout << "Print " << a << ", " << b << std::endl; | |
return false; | |
} } | |
.whenStart([](int a, int& b) { | |
b = 42; | |
return true; | |
})); | |
net.set(MyActions::Sum, Action<Param<int>, int&>{ [](int a, int& b) { | |
b += a; | |
if (a == 100) | |
{ | |
std::cout << "Sum 1~100 = " << b << std::endl; | |
} | |
return true; | |
} } | |
.whenStart([](int a, int& b) { | |
b = 0; | |
return true; | |
})); | |
net.begin(MyActions::SayHello); | |
net.update(1234); | |
net.begin(MyActions::Sum); | |
auto net2 = net; | |
for (int i = 1; i <= 100; ++i) | |
{ | |
net2.update(i); | |
} | |
} | |
int main(int argc, char* argv[]) | |
{ | |
testAction(); | |
testNet(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment