環境
Visual Studio 2017
やりたいこと
RTTIは使わずに、実行時に安全なダウンキャストを行いたい。
コード
ヘッダ側
Test.h
# pragma once
// クラス識別用コード展開マクロ
# define CLASS_IDENTIFY(ThisClass, SuperClass) \
public: \
static_assert(DerivedFrom<ThisClass, SuperClass>::IsDerived, #ThisClass" must derived from "#SuperClass); \
typedef SuperClass Super; \
static ClassIdentifier* StaticClassIdentifier() \
{ \
static std::unique_ptr<ClassIdentifier> A(new ClassIdentifier(SuperClass::StaticClassIdentifier())); \
return A.get(); \
} \
virtual ClassIdentifier* GetClassIdentifier() const \
{ \
return StaticClassIdentifier(); \
} \
private: \
// 親子チェック
template<typename Derived, typename Base>
struct DerivedFrom
{
typedef int8_t False;
typedef int16_t True;
static True Func(Base*);
static True Func(const Base*);
static False Func(...);
static Derived* DerivedTypePtr() { return nullptr; }
public:
static const bool IsDerived = sizeof(Func(DerivedTypePtr())) == sizeof(True);
};
// クラス識別用クラス
class ClassIdentifier
{
public:
ClassIdentifier(ClassIdentifier* InSuperClass)
: SuperClass(InSuperClass)
{}
~ClassIdentifier()
{
SuperClass = nullptr;
}
ClassIdentifier* GetSuperClass() const
{
return SuperClass;
}
private:
ClassIdentifier* SuperClass;
};
// クラス識別ターゲットベースクラス
class IsAObject
{
public:
template<class T>
bool IsA() const
{
return IsA(T::StaticClassIdentifier());
}
private:
template<typename OtherClassType>
bool IsA(OtherClassType SomeBase) const
{
if (SomeBase == nullptr)
{
return false;
}
bool Result = false;
for (const ClassIdentifier* TempClass = GetClassIdentifier(); TempClass != nullptr; TempClass = TempClass->GetSuperClass())
{
if (TempClass == SomeBase)
{
Result = true;
break;
}
}
return Result;
}
public:
static ClassIdentifier* StaticClassIdentifier()
{
static std::unique_ptr<ClassIdentifier> A(new ClassIdentifier(nullptr));
return A.get();
}
virtual ClassIdentifier* GetClassIdentifier() const
{
return StaticClassIdentifier();
}
};
// クラス識別ターゲットクラスを利用したキャスト
template <typename To>
To* Cast(IsAObject* Src)
{
return (Src != nullptr && Src->IsA<To>()) ? (To*)Src : nullptr;
}
// テスト用クラス1
class AAA : public IsAObject
{
CLASS_IDENTIFY(AAA, IsAObject)
};
// テスト用クラス2
class BBB : public IsAObject
{
CLASS_IDENTIFY(BBB, IsAObject)
};
テスト用コード
Test.cpp
# include <iostream>
# include "CastTest.h"
int main()
{
IsAObject* ObjA = new AAA;
IsAObject* ObjB = new IsAObject;
IsAObject* ObjC = new BBB;
bool IsAAA = ObjA->IsA<AAA>();
bool IsBBB = ObjB->IsA<BBB>();
bool IsAAA2 = ObjC->IsA<AAA>();
std::cout << (IsAAA ? "ObjA is AAA" : "ObjA is not AAA") << std::endl;
std::cout << (IsBBB ? "ObjB is BBB" : "ObjB is not BBB") << std::endl;
std::cout << (IsAAA2 ? "ObjB is AAA" : "ObjB is not AAA") << std::endl;
AAA* ObjAAA = Cast<AAA>(ObjA);
BBB* ObjBBB = Cast<BBB>(ObjB);
AAA* ObjAAA2 = Cast<AAA>(ObjC);
std::cout << "Cast IsAObject(AAA)* to AAA* " << (ObjAAA ? "succeed!" : "failed!") << std::endl;
std::cout << "Cast IsAOcject(IsAObject)* to BBB* " << (ObjBBB ? "succeed!" : "failed!") << std::endl;
std::cout << "Cast IsAObject(BBB)* to AAA* " << (ObjAAA2 ? "succeed!" : "failed!") << std::endl;
return 0;
}
出力
出力
ObjA is AAA
ObjB is not BBB
ObjB is not AAA
Cast IsAObject(AAA)* to AAA* succeed!
Cast IsAOcject(IsAObject)* to BBB* failed!
Cast IsAObject(BBB)* to AAA* failed!
問題
CLASS_IDENTIFY(クラス, 親より基底にあるクラス)
と指定されるとエラーにできない。
最後に
ここをこうしたほうがいいなどあればご意見よろしくおねがいします。