(C++ : IOCP-54) JobQueue - 2

Posted by : at

Category : Cpp   iocp


// 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;
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links