Skip to content

Instantly share code, notes, and snippets.

@NeuroWhAI
Created July 14, 2017 05:46
Show Gist options
  • Save NeuroWhAI/1c7f90133b312bd86c67b944703be52e to your computer and use it in GitHub Desktop.
Save NeuroWhAI/1c7f90133b312bd86c67b944703be52e to your computer and use it in GitHub Desktop.
Action Net
#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;
}
};
#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