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

using TL = TypeList<player, mage,="" knight,="" archer="">;</player,>

이건 TL 들의 사이즈를 구할 수 있는 템플릿예시입니다

 

 

 

 

다음과 같은 상속 구조가 있을 경우

class Player
{
public:

	virtual ~Player() { }


};

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

class Mage : public Player
{

public:
	Mage() {  }
};

class Archer : public Player
{

public:
	Archer() {  }
};

class Dog
{
};

 

 

template<typename From, typename To>
class Conversion
{
private:
	using Small = __int8;
	using Big = __int32;

	static Small Test(const To&) { return 0; }
	static Big Test(...) { return 0; }
	static From MakeFrom() { return 0; }

public:
	enum
	{
		exists = sizeof(Test(MakeFrom())) == sizeof(Small)
	};
};

 

Conversion은 아래 처럼 사용 할수 있는데 

 

bool canConvert1 = Conversion<Player, Knight>::exists;   0
bool canConvert2 = Conversion<Knight, Player>::exists;   1
bool canConvert3 = Conversion<Knight, Dog>::exists;       0

 

이렇게 호출 할 경우 

아래 두 함수 중에서 

 

static Small Test(const To&) { return 0; }


static Big Test(...) { return 0; }

 

 

Test 함수가 호출 될때 From 타입을 To 타입으로 캐스팅 할수 있다면 즉

 

모든 타입을 다 받는 Test(...)  가 아닌 특정 지정한 To 타입으로 

Test 함수가 불릴 수 있다면 상속관계이 있는 것임으로

 

이를 이용해서

 

exists = sizeof(Test(MakeFrom())) == sizeof(Small)

 

캐스팅이 가능한지 불가능한 관계인지를 컴파일 타임때 미리 계산 할 수 있다

 

 

 

 

 

 

아래는  타입에 대한 인덱스를 구할수 있는 코드

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

 

 

define 좋진 않은데 그냥 예시라 보면 될듯합니다

 

[플레이어 코드 TL 예시]

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

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



class Player
{
public:
	Player()
	{
		INIT_TL(Player);
	}
	virtual ~Player() { }

	DECLARE_TL
};

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

class Mage : public Player
{

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

class Archer : public Player
{

public:
	Archer() { INIT_TL(Archer) }
};

class Dog
{

};

 

컴파일타임때 해당 타입이 몇번의 인덱스인지 구할수 있게 됩니다

tempalte 가변인자 특징을 활용

 

 

 

template<typename TL, int32 index>
struct TypeAt;

template<typename Head, typename... Tail>
struct TypeAt<TypeList<Head, Tail...>, 0>
{
	using Result = Head;
};

template<typename Head, typename... Tail, int32 index>
struct TypeAt<TypeList<Head, Tail...>, index>
{
	using Result = typename TypeAt<TypeList<Tail...>, index - 1>::Result;
};

이것은 컴파일타임에 타입들중에서 지정한 인덱스에 해당하는 타입을 알수 있는 코드입니다

 

 

 

 

 

 

타입 변환을 위한 클래스들간의 타입 변환이 일어날수 있는가 2중 for 문돌리듯이 템플릿을 만들어 미리 그 결과를 저장해 놓을수 있는데 

 

 

 

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];

 

이런 식으로 작성 하면 됩니다

 

s_convert 이곳에는 컴파일 타임때 타입간의 변환이 가능한지를 미리 계산해 놓은 곳이 되며

 

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

 

 

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

 

이 함수로 타입간의 결과를 리턴 받을수 있습니다 : 이건 런타임이겠지요

 

 

 

 

이제 [플레이어 코드 TL 예시] 를 기준으로

 

 

아래 처럼 작성 할수 있는데

 

타입 캐스팅이 가능하다면 static_cast 로 캐스팅 해서 반환, 그렇지 않으면 nullptr 반환(dynamic_cast 와 유사)을 하는 것입니다

 

 


//타입 캐스팅이 가능하다면 static_cast 로 캐스팅 해서 반환, 그렇지 않으면 nullptr 반환(dynamic_cast 와 유사)
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;
}

//이건 캐스팅이 가능하냐 그렇지 않냐 Is, As 캐스팅 같은 것..
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);
}

 

remove_pointer_t 는 들어온 타입의 포인터를 제거 하는 것

 

 

 

예시 Player를 Knight 로 캐스팅

 

dynamic_cast 처럼 null 이 나오는것을 알수 있습니다

 

 

예시 본체(인스턴스)가 Knight 였고 이것을 player에 담은 후 player를 Knight 로 캐스팅하면 성공

 

Knight 가 초기화 되면서 player index 가 Knight 의 index로 바뀐 상태가 되고 

멤버변수 index에 Knight의 index 가 들어가게 됨

런타입에서 Knight 에 맞는 index를 찾기 때문에가능

 

 

반응형

+ Recent posts