(C++ : IOCP-23) TypeCast

Posted by : at

Category : Cpp   iocp



TypeCast를 왜 써야할까?

class Player
{

};

class Knight : public Player
{

};

class Mage : public Player
{

};

Player* p1 = new Knight();
Player* p2 = new Mage();

Knight* k1 = static_cast<Knight*>(p2);  // 이런 실수가 날 수 있다.

// dynamic_cast쓰면 안되나? -> 성능상의 문제로 static_cast를 써야만 한다고 가정하자.

자신의 Type을 내부적으로 관리해보면?

enum {
    KNIGHT,
    MAGE,
};

class Player
{
    int _type;
};

class Knight : public Player
{
    Knight() { _type = KNIGHT; }
};

class Mage : public Player
{
    Mage() { _type = MAGE; }
};

Player* p1 = new Knight();
Player* p2 = new Mage();

if(p1->_type == KNIGHT)
{
    Knight* k1 = static_cast<Knight*>(p1);
}

그런데 클래스가 100개가 되면 enum도 100개로 늘려야하나?
그러다 1000, 10000, 1000000개가되면???

이 문제를 해결하기 위해 TypeCast를 사용한다.


#pragma once
#include "Types.h"

#pragma region TypeList     // TypeList와 관련된 부분
template<typename... T>
struct TypeList;

template<typename T, typename U>
struct TypeList<T, U>
{
	using Head = T;
	using Tail = U;
};

template<typename T, typename... U>
struct TypeList<T, U...>
{
	using Head = T;
	using Tail = TypeList<U...>;
};
#pragma endregion

/*
// 2개 중 하나를 선택한다면
TypeList<Mage, Knight>::Head whoAMI;        // Mage가 선택됨
TypeList<Mage, Knight>::Tail whoAMI2;       // Knight가 선택됨
*/

/*
TypeList<Mage, TypeList<Knight, Archer>>::Head whoAMI;              // Mage
TypeList<Mage, TypeList<Knight, Archer>>::Tail::Head whoAMI2;       // Knight
TypeList<Mage, TypeList<Knight, Archer>>::Tail::Tail whoAMI3;       // Archer
*/

너무 복잡한데 이걸 어떻게 쓸까?

// Length 기능 추가
#pragma region Length
template<typename T>
struct Length;

template<>
struct Length<TypeList<>>
{
	enum { value = 0 };
};

template<typename T, typename... U>
struct Length<TypeList<T, U...>>
{
	enum { value = 1 + Length<TypeList<U...>>::value };
};
#pragma endregion

/*
int32 len1 = Length<TypeList<Mage, Knight>>::value;
*/
#pragma region TypeAt
template<typename TL, int32 index>
struct TypeAt;

template<typename Head, typename... Tail>
struct TypeAt<TypeList<Head, Tail...>, 0>
{
    // index가 0인 경우 Head리턴
	using Result = Head;
};

template<typename Head, typename... Tail, int32 index>
struct TypeAt<TypeList<Head, Tail...>, index>
{
    // 1개 씩 줄여가며 Result를 찾는다
	using Result = typename TypeAt<TypeList<Tail...>, index - 1>::Result;
};
#pragma endregion

/*
using TL = TypeList<Mage, Knight, Archer>
TypeAt<TL, 0>::Result whoAMI;       // Mage
TypeAt<TL, 1>::Result whoAMI;       // Knight
TypeAt<TL, 2>::Result whoAMI;       // Archer
*/
#pragma  region IndexOf
template<typename TL, typename T>
struct IndexOf;

template<typename... Tail, typename T>
struct IndexOf<TypeList<T, Tail...>, T>
{
	enum { value = 0 };
};

template<typename T>
struct IndexOf<TypeList<>, T>
{
    // 빈타입
	enum { value = -1 };
};

template<typename Head, typename... Tail, typename T>
struct IndexOf<TypeList<Head, Tail...>, T>
{
private:
	enum { temp = IndexOf<TypeList<Tail...>, T>::value };

public:
	enum { value = (temp == -1) ? -1 : temp + 1 };
};
#pragma endregion

/*
int32 index = IndexOf<TL, Mage>::value;     // 1
*/
#pragma region Conversion
// From에서 To로 변환이 가능한지 확인
template<typename From, typename To>
class Conversion
{
private:
	using Small = __int8;
	using Big = __int32;

	static Small Test(const To&) { return 0; }  // Test(...)보다 호출의 우선순위가 높다
	static Big Test(...) { return 0; }
	static From MakeFrom() { return 0; }

public:
	enum
	{
		exists = sizeof(Test(MakeFrom())) == sizeof(Small)
        // Test(MakeFrom()) == Test(From) -> From을 const To& 혹은 ...으로 변환이 가능한지
	};
};
#pragma endregion

/*
bool canConver1 = Conversion<Player, Knight>::exists;
bool canConver2 = Conversion<Knight, Player>::exists;
*/
#pragma region TypeCast

template<int32 v>
struct Int2Type
{
	enum { value = v };
};

template<typename TL>
class TypeConversion
{
public:
	enum
	{
		length = Length<TL>::value
	};

	TypeConversion()
	{
		MakeTable(Int2Type<0>(), Int2Type<0>());
	}

	template<int32 i, int32 j>
	static void MakeTable(Int2Type<i>, Int2Type<j>)
	{
		using FromType = typename TypeAt<TL, i>::Result;
		using ToType = typename TypeAt<TL, j>::Result;

		if (Conversion<const FromType*, const ToType*>::exists)
			s_convert[i][j] = true;
		else
			s_convert[i][j] = false;

		MakeTable(Int2Type<i>(), Int2Type<j + 1>());
	}

	template<int32 i>
	static void MakeTable(Int2Type<i>, Int2Type<length>)
	{
		MakeTable(Int2Type<i + 1>(), Int2Type<0>());
	}

	template<int j>
	static void MakeTable(Int2Type<length>, Int2Type<j>)
	{
	}

	static inline bool CanConvert(int32 from, int32 to)
	{
		static TypeConversion conversion;
		return s_convert[from][to];
	}

public:
	static bool s_convert[length][length];
};

template<typename TL>
bool TypeConversion<TL>::s_convert[length][length];

template<typename To, typename From>
To TypeCast(From* ptr)
{
	if (ptr == nullptr)
		return nullptr;

	using TL = typename From::TL;

	if (TypeConversion<TL>::CanConvert(ptr->_typeId, IndexOf<TL, remove_pointer_t<To>>::value))
		return static_cast<To>(ptr);

	return nullptr;
}


template<typename To, typename From>
shared_ptr<To> TypeCast(shared_ptr<From> ptr)
{
	if (ptr == nullptr)
		return nullptr;

	using TL = typename From::TL;

	if (TypeConversion<TL>::CanConvert(ptr->_typeId, IndexOf<TL, remove_pointer_t<To>>::value))
		return static_pointer_cast<To>(ptr);

	return nullptr;
}

template<typename To, typename From>
bool CanCast(From* ptr)
{
	if (ptr == nullptr)
		return false;

	using TL = typename From::TL;
	return TypeConversion<TL>::CanConvert(ptr->_typeId, IndexOf<TL, remove_pointer_t<To>>::value);
}


template<typename To, typename From>
bool CanCast(shared_ptr<From> ptr)
{
	if (ptr == nullptr)
		return false;

	using TL = typename From::TL;
	return TypeConversion<TL>::CanConvert(ptr->_typeId, IndexOf<TL, remove_pointer_t<To>>::value);
}

#pragma endregion

#define DECLARE_TL		using TL = TL; int32 _typeId;
#define INIT_TL(Type)	_typeId = IndexOf<TL, Type>::value;

실사용 예제

using TL = TypeList<class Player,class  Mage,class  Knight,class  Archer>;

class Player
{
public:
    Player(){
        INIT_TL(Player);
        //_typeId = IndexOf<TL, Player>::value;
    }
    virtual ~Player() {}

    DECLARE_TL    
    //using TL = TL;
    //int _typeId;
};

class Knight : public Player
{
    Knight() { INIT_TL(Knight); }
};

class Mage : public Player
{
    Mage() { INIT_TL(Mage); }
};


Player* player = new Player();

bool canCast = CanCast<Knight*>(player);    // false
Knight k1 = TypeCast<Knight*>(player);      // nullptr

About Taehyung Kim

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

Star
Useful Links