Graphics Programming

템플릿을 이용해 팩토리 함수에서 switch-case 없애기 본문

Season 1/Misc

템플릿을 이용해 팩토리 함수에서 switch-case 없애기

minseoklee 2018. 2. 21. 23:08

베이스가 되는 Packet 클래스가 있고, 이를 상속한 PacketA, PacketB, PacketC 클래스가 있다. 그리고 각 패킷 클래스에 대응하는 enum이 있다.


// packet.h

enum class PacketType : uint16_t {

PACKET_A = 0,

PACKET_B = 1,

PACKET_C = 2,

NUM_PACKETS = 3

}


struct Packet {

Packet(PacketType type);

PacketType type;

};


struct PacketA : Packet {

PacketA();

};


struct PacketB : Packet {

PacketB();

};


struct PacketC : Packet {

PacketC();

};


// packet.cpp

Packet::Packet(PacketType type_) : type(type_) {}

PacketA::PacketA() : Packet(PacketType::PACKET_A) {}

PacketB::PacketB() : Packet(PacketType::PACKET_B) {}

PacketC::PacketC() : Packet(PacketType::PACKET_C) {}


이제 PacketType을 가지고 대응하는 패킷 인스턴스를 생성하는 팩토리 함수를 만들려고 한다. 가장 단순한 방법은 switch-case이다.


Packet* fromPacketFactory(PacketType type) {

switch(type) {

case PacketType::PACKET_A:

return new PacketA;

case PacketType::PACKET_B:

return new PacketB;

case PacketType::PACKET_C:

return new PacketC;

}

return nullptr;

}


실행 속도는 빠를 지 몰라도 패킷 종류가 많아질 수록 타이핑이 귀찮아진다. 템플릿을 이용해서 팩토리 함수를 간단하게 만들어보자.


// packet.h

// ... 위와 동일


extern std::function<void*()> packetFactory[static_cast<uint16_t>(PacketType::NUM_PACKET_TYPES)];


template<typename T>

struct __factory_initializer {

__factory_initializer(PacketType typeEnum) {

auto generator = []() -> void* { return new T; };

uint16_t ix = static_cast<uint16_t>(typeEnum);

assert(packetFactory[ix] == nullptr);

packetFactory[ix] = generator;

}

};


#define REGISTER_PACKET(TypeEnum, PacketClass) \

__factory_initializer<PacketClass> __factory_initializer_##PacketClass(TypeEnum);


// packet.cpp


std::function<void*()> packetFactory[static_cast<uint16_t>(PacketType::NUM_PACKET_TYPES)] = { nullptr, };


Packet::Packet(PacketType type_) : type(type_) {}


REGISTER_PACKET(PacketType::PACKET_A, PacketA)

PacketA::PacketA() : Packet(PacketType::PACKET_A) {}


REGISTER_PACKET(PacketType::PACKET_B, PacketB)

PacketB::PacketB() : Packet(PacketType::PACKET_B) {}


REGISTER_PACKET(PacketType::PACKET_C, PacketC)

PacketC::PacketC() : Packet(PacketType::PACKET_C) {}


이러면 팩토리 함수에서 switch-case를 없앨 수 있다.


Packet* fromPacketFactory(PacketType type) {

auto generator = packetFactory[static_cast<uint16_t>(type)];

assert(generator != nullptr);

Packet* packet = reinterpret_cast<Packet*>(generator());

return packet;

}



Comments