ポインタが他のクラスにキャスト可能か調べる(2)

の続き


VS2005 の dynamic_cast<> が最強すぎる件。

#include <typeinfo.h>

extern "C" void* __cdecl
    __RTDynamicCast(void* inptr, long unknown, void* SrcType, void* TargetType, int isReference);

struct A
{
    virtual ~A() { }
};
struct B : public A { };

void main()
{
    B b;
    A* pa = &b;

    void* p = pa;

    void* p2 = __RTDynamicCast(p, 0, (void*)&typeid(*pa), (void*)&typeid(B), false);
    if (p2 != NULL)
    {
        printf("成功: %s\n", typeid(*pa).name());
    }
    else
    {
        printf("失敗: %s\n", typeid(*pa).name());
    }

    void* p3 = __RTDynamicCast(p, 0, (void*)&typeid(A), (void*)&typeid(B), false);
    if (p3 != NULL)
    {
        printf("成功: %s\n", typeid(A).name());
    }
    else
    {
        printf("失敗: %s\n", typeid(A).name());
    }
}

結果

失敗: struct B
成功: struct A

ネット上でいくつか探してきた __RTDynamicCast の実装は、SrcType に &typeid(int) を渡しても平気でキャストしてくれていたのですが、本来の VS2005 の __RTDynamicCast を使ってみると、(typeid(*pa)ではなく)dynamic_cast<>(ここ) に入っている型を指定しないと NULL が返されてしまうようです


どうやってこんなの実装してるんだ……。


追記:
もうちょい調べてみると、SrcType は TargetType より上位のクラスを指定しないとダメというだけのようです。
つまりダウンキャスト以外の用途には使えないってことですね。
あと、多重継承を行っている場合は、結構いろいろとキャスト出来るみたいです。
どのみちダウンキャスト以外の用途には使えないようですが……。
試しに VS2005 で上位クラスに dynamic_cast<> してみましたが、__RTDynamicCast() は呼び出されずにそのまま代入されているだけでした。


以下のテストコード

#include <typeinfo.h>

extern "C" void* __cdecl
    __RTDynamicCast(void* inptr, long unknown, void* SrcType, void* TargetType, int isReference);

struct A { virtual ~A() { } };
struct B : public A { };
struct C : public B { };

struct D { virtual ~D() { } };

struct E : public C, public D { };

template<class TSrc, class TTarget>
void test(void* value)
{
    void* p = __RTDynamicCast(value, 0, (void*)&typeid(TSrc), (void*)&typeid(TTarget), false);
    if (p != NULL)
    {
        ::printf("SUCCEEDED: src = %s, target = %s\n", typeid(TSrc).name(), typeid(TTarget).name());
    }
    else
    {
        ::printf("FAILED   : src = %s, target = %s\n", typeid(TSrc).name(), typeid(TTarget).name());
    }
}

void main()
{
    {
        ::printf("---- 元の型が C ----\n");
        C c;
        void* p = &c;

        test<A, A>(p);
        test<B, A>(p);
        test<C, A>(p);

        ::printf("\n");

        test<A, B>(p);
        test<B, B>(p);
        test<C, B>(p);

        ::printf("\n");

        test<A, C>(p);
        test<B, C>(p);
        test<C, C>(p);

        ::printf("\n");

    }

    {
        ::printf("---- 元の型が E で、C にキャストして取得 ----\n");
        E e;
        void* p = (C*)&e;

        test<A, A>(p);
        test<B, A>(p);
        test<C, A>(p);
        test<D, A>(p);
        test<E, A>(p);

        ::printf("\n");

        test<A, B>(p);
        test<B, B>(p);
        test<C, B>(p);
        test<D, B>(p);
        test<E, B>(p);

        ::printf("\n");

        test<A, C>(p);
        test<B, C>(p);
        test<C, C>(p);
        test<D, C>(p);
        test<E, C>(p);

        ::printf("\n");

        test<A, D>(p);
        test<B, D>(p);
        test<C, D>(p);
        test<D, D>(p);
        test<E, D>(p);

        ::printf("\n");

        test<A, E>(p);
        test<B, E>(p);
        test<C, E>(p);
        test<D, E>(p);
        test<E, E>(p);

        ::printf("\n");

    }

    {
        ::printf("---- 元の型が E で、D にキャストして取得 ----\n");
        E e;
        void* p = (D*)&e;

        test<A, A>(p);
        test<B, A>(p);
        test<C, A>(p);
        test<D, A>(p);
        test<E, A>(p);

        ::printf("\n");

        test<A, B>(p);
        test<B, B>(p);
        test<C, B>(p);
        test<D, B>(p);
        test<E, B>(p);

        ::printf("\n");

        test<A, C>(p);
        test<B, C>(p);
        test<C, C>(p);
        test<D, C>(p);
        test<E, C>(p);

        ::printf("\n");

        test<A, D>(p);
        test<B, D>(p);
        test<C, D>(p);
        test<D, D>(p);
        test<E, D>(p);

        ::printf("\n");

        test<A, E>(p);
        test<B, E>(p);
        test<C, E>(p);
        test<D, E>(p);
        test<E, E>(p);

        ::printf("\n");

    }

    return 0;
}
---- 元の型が C ----
FAILED   : src = struct A, target = struct A
FAILED   : src = struct B, target = struct A
FAILED   : src = struct C, target = struct A

SUCCEEDED: src = struct A, target = struct B
FAILED   : src = struct B, target = struct B
FAILED   : src = struct C, target = struct B

SUCCEEDED: src = struct A, target = struct C
SUCCEEDED: src = struct B, target = struct C
FAILED   : src = struct C, target = struct C

---- 元の型が E で、C にキャストして取得 ----
SUCCEEDED: src = struct A, target = struct A
SUCCEEDED: src = struct B, target = struct A
SUCCEEDED: src = struct C, target = struct A
FAILED   : src = struct D, target = struct A
SUCCEEDED: src = struct E, target = struct A

SUCCEEDED: src = struct A, target = struct B
SUCCEEDED: src = struct B, target = struct B
SUCCEEDED: src = struct C, target = struct B
FAILED   : src = struct D, target = struct B
SUCCEEDED: src = struct E, target = struct B

SUCCEEDED: src = struct A, target = struct C
SUCCEEDED: src = struct B, target = struct C
SUCCEEDED: src = struct C, target = struct C
FAILED   : src = struct D, target = struct C
SUCCEEDED: src = struct E, target = struct C

SUCCEEDED: src = struct A, target = struct D
SUCCEEDED: src = struct B, target = struct D
SUCCEEDED: src = struct C, target = struct D
FAILED   : src = struct D, target = struct D
SUCCEEDED: src = struct E, target = struct D

SUCCEEDED: src = struct A, target = struct E
SUCCEEDED: src = struct B, target = struct E
SUCCEEDED: src = struct C, target = struct E
FAILED   : src = struct D, target = struct E
SUCCEEDED: src = struct E, target = struct E

---- 元の型が E で、D にキャストして取得 ----
FAILED   : src = struct A, target = struct A
FAILED   : src = struct B, target = struct A
FAILED   : src = struct C, target = struct A
SUCCEEDED: src = struct D, target = struct A
FAILED   : src = struct E, target = struct A

FAILED   : src = struct A, target = struct B
FAILED   : src = struct B, target = struct B
FAILED   : src = struct C, target = struct B
SUCCEEDED: src = struct D, target = struct B
FAILED   : src = struct E, target = struct B

FAILED   : src = struct A, target = struct C
FAILED   : src = struct B, target = struct C
FAILED   : src = struct C, target = struct C
SUCCEEDED: src = struct D, target = struct C
FAILED   : src = struct E, target = struct C

FAILED   : src = struct A, target = struct D
FAILED   : src = struct B, target = struct D
FAILED   : src = struct C, target = struct D
SUCCEEDED: src = struct D, target = struct D
FAILED   : src = struct E, target = struct D

FAILED   : src = struct A, target = struct E
FAILED   : src = struct B, target = struct E
FAILED   : src = struct C, target = struct E
SUCCEEDED: src = struct D, target = struct E
FAILED   : src = struct E, target = struct E