// HealByValue 함수를 Job으로 넘겨보자
void HealByValue(int64 target, int32 value)
{
cout << target << "한테 힘" << value << "만큼 줌" << endl;
}
int main()
{
FuncJob<void, int64, int32> job(HealByValue);
job(100, 10);
// ...
}
Job에서 HealByValue
를 받기위해서
template<typname Ret, typename... Args>
class FuncJob
{
using FuncType = Ret(*)(Args...);
public:
FuncJob(FuncType func) : _func(func)
{
}
Ret operator()(Args... args)
{
_func(args...);
}
Ret Execute(Args... args)
{
_func(args...);
}
private:
FuncType _func;
}
이렇게만하면 JobClass를 매번 생성하는게 아니라 함수생성으로 Job을 만들수 있다
모든 문제가 해결?
또 다른 문제가 생긴다
Job을 생성하며 전달된 인자를 Job자체에서 갖고있게 하고싶다
int main()
{
// 가령 이런식
FuncJob<void, int64, int32> job(HealByValue, 100, 10);
// ...
template<typname Ret, typename... Args>
class FuncJob
{
// ...
private:
FuncType _func;
// 클래스내에 이렇게 매개변수를 갖게 만들고 싶지만
// C++에선 이런 문법을 지원하지 않는다
Args... _args;
}
tuple이 이런기능을 대체해 준다
template<typname Ret, typename... Args>
class FuncJob
{
using FuncType = Ret(*)(Args...);
public:
FuncJob(FuncType func, Args... args) : _func(func), _tuple(args...)
{
}
Ret operator()(Args... args)
{
// 이제 갖고있던 tuple로 호출하자
//_func(args...);
std::apply(_func, _tuple); // C++17
}
Ret Execute(Args... args)
{
//_func(args...);
std::apply(_func, _tuple);
}
private:
FuncType _func;
std::tuple<Args...> _tuple;
}
int main()
{
FuncJob<void, int64, int32> job(HealByValue, 100, 10);
job.Execute();
// ...
모든 문제가해결?
C++11을 써야할 상황이라면??(아래 내용보면 알겠지만 그냥 C++17 쓰자)
// C++11 apply
template<int... Remains>
struct seq
{};
template<int N, int... Remains>
struct gen_seq : gen_seq<N-1, N-1, Remains...>
{};
template<int... Remains>
struct gen_seq<0, Remains...> : seq<Remains...>
{};
template<typename Ret, typename... Args>
void xapply(Ret(*func)(Args...), std::tuple<Args...>& tup)
{
xapply_helper(func, gen_seq<sizeof...(Args)>(), tup);
}
template<typename F, typename... Args, int... ls>
void xapply_helper(F func, seq<ls...>, std::tuple<Args...>& tup)
{
(func)(std::get<ls>(tup)...);
}
template<typename T, typename Ret, typename... Args>
void xapply(T* obj, Ret(T::*func)(Args...), std::tuple<Args...>& tup)
{
xapply_helper(obj, func, gen_seq<sizeof...(Args)>(), tup);
}
template<typename T, typename F, typename... Args, int... ls>
void xapply_helper(T* obj, F func, seq<ls...>, std::tuple<Args...>& tup)
{
(obj->*func)(std::get<ls>(tup)...);
}
template<typname Ret, typename... Args>
class FuncJob
{
using FuncType = Ret(*)(Args...);
public:
FuncJob(FuncType func, Args... args) : _func(func), _tuple(args...)
{
}
Ret operator()(Args... args)
{
// 이제 갖고있던 tuple로 호출하자
// _func(args...);
// std::apply(_func, _tuple); // C++17
xapply(_func, _tuple); // C++11
}
Ret Execute(Args... args)
{
// _func(args...);
// std::apply(_func, _tuple);
xapply(_func, _tuple); // C++11
}
private:
FuncType _func;
std::tuple<Args...> _tuple;
}
대략 코드설명을 하자면
// 우선 tuple
auto tup = std::tuple<int32, int32>(1, 2);
auto val0 = std::get<0>(tup); // 이런식으로 매개변수를 받을 수 있음
auto val1 = std::get<1>(tup);
// 우리가 생성한 gen_seq는
auto s = gen_seq<3>(); // 호출할 경우
// struct gen_seq : gen_seq<N-1, N-1, Remains...> 이기에
// : gen_seq<2, 2> / Remains는 없기에 생략됨
// : gen_seq<1, 1, 2> / Remains는 2
// : gen_seq<0, 0, 1, 2> / Remains는 1, 2
// struct gen_seq<0, Remains...> : seq<Remains...> 이기에
// : seq<0, 1, 2>
// xapply에 적용해보자면
xapply(_func, _tuple);
/*
void xapply(Ret(*func)(Args...), std::tuple<Args...>& tup)
{
xapply_helper(func, gen_seq<sizeof...(Args)>(), tup);
}
template<typename F, typename... Args, int... ls>
void xapply_helper(F func, seq<ls...>, std::tuple<Args...>& tup)
{
(func)(std::get<ls>(tup)...);
}
*/
모든 문제가 해결됐을까?
클래스 안의 함수를 Job으로 등록가능한가?
class Knight
{
void HealMe(int32 value)
{
cout << "HealMe!" << value << endl;
}
};
int main()
{
Knight k1;
// FuncJob<>을 통해선 불가능 새로운 클래스의 생성이 필요하다
}
template<typename T, typename Ret, typename... Args>
class MemberJob : public IJob
{
// T클래스 안에 포함된 함수라 알려준다
using FuncType = Ret(T::*)(Args...);
public:
MemberJob(T* obj, FuncType func, Args... args) : _obj(obj), _func(func), _tuple(args...)
{
}
virtual void Execute() override
{
//std::apply(_func, _tuple); // C++17
xapply(_obj, _func, _tuple);
}
private:
T* _obj;
FuncType _func;
std::tuple<Args...> _tuple;
};
class Knight
{
void HealMe(int32 value)
{
cout << "HealMe!" << value << endl;
}
};
int main()
{
Knight k1;
MemberJob job2(&k1, &Knight::HealMe, 10);
job2.Execute();
}
이제 실제로 어떻게 쓰는지 보자
class Room
{
public:
void Enter(PlayerRef player);
void Leave(PlayerRef player);
void Broadcast(SendBufferRef sendBuffer);
public:
void FlushJob();
// Job을 넣는부분
template<typename T, typename Ret, typename... Args>
void PushJob(Ret(T::*memFunc)(Args...), Args... args)
{
auto job = MakeShared<MemberJob<T, Ret, Args...>>(static_cast<T*>(this), memFunc, args...);
_jobs.Push(job);
}
private:
map<uint64, PlayerRef> _players;
JobQueue _jobs;
};
bool Handle_C_ENTER_GAME(PacketSessionRef& session, Protocol::C_ENTER_GAME& pkt)
{
GameSessionRef gameSession = static_pointer_cast<GameSession>(session);
uint64 index = pkt.playerindex();
// TODO : Validation
PlayerRef player = gameSession->_players[index]; // READ_ONLY?
//GRoom.PushJob(MakeShared<EnterJob>(GRoom, player));
GRoom.PushJob(&Room::Enter, player);
Protocol::S_ENTER_GAME enterGamePkt;
enterGamePkt.set_success(true);
auto sendBuffer = ClientPacketHandler::MakeSendBuffer(enterGamePkt);
player->ownerSession->Send(sendBuffer);
return true;
}