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를 찾기 때문에가능
'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글
[C++20] 모듈(module) (0) | 2023.03.18 |
---|---|
When does a std::vector enlarge itself when we push_back elements? (0) | 2023.03.06 |
using shared_ptr with pure virtual base class (0) | 2022.08.24 |
C++ 락(std::lock, std::unique_lock, std::lock_guard, condition_variable...) (0) | 2022.08.11 |
C++ "Virtual functions but no virtual destructors" (0) | 2022.04.27 |