Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » masarl memorial » homepage3.nifty.com » masarl » article » nifty-logs » Niftyの過去ログ集 - RTTI Visitor

Niftyの過去ログ集 - RTTI Visitor

Document Actions

RTTI Visitor

Visitor パターンの面白い応用例として,RTTI Visitor というのがあります.RTTI,つまり実行時型情報を取得するのに Vistor を使ったものです.

02869/02869 BYI20012  まさーる         RTTI Visitor
( 6)   99/04/13 01:02  02864へのコメント

真亜機伊 さん,こんにちは.

>社内の部品を使っていた当時はダウンキャストは一歩間違うと危
>険だなあと思っていました.(当時,安全にダウンキャストする
>仕組みは,その処理系にはありませんでした.)

ちょっと話がそれますが,こういう状況でdynamic_castをシュミレートするため
にRTTI Visitorパターンというものがあります.Visitorパターンの可能性を知
る上でいいんじゃないでしょうか.

RTTI Visitorは

"Design Patterns for Dual Inheritance Hierarchies in C++"
Robert C. Martin
(http://www.oma.com/Publications/publications.htmlから入手可)

で解説されています.
このアーティクルから,サンプルコードを引用します:

class BVisitor;
class D1;
class D2;
class B
{
public:
    virtual void Visit(BVisitor&) = 0;
};
class BVisitor
{
public:
    BVisitor() : itsD1(0), itsD2(0) {}
    void Visit(D1& d1) {itsD1 = &d1;}
    void Visit(D2& d2) {itsD2 = &d2;}
    D1* GetD1() const {return itsD1;}
    D2* GetD2() const {return itsD2;}
private:
    D1* itsD1;
    D2* itsD2;
};
class D1 : public B
{
public:
    virtual void Visit(BVisitor& bv)
    {bv.Visit(*this);} // class Visit(D1&)
};
class D2 : public B
{
public:
    virtual void Visit(BVisitor& bv)
    {bv.Visit(*this);} // class Visit(D2&)
};
// Global functions
D1* dynamic_cast_D1(B* b)
{
    BVisitor v;
    b->Visit(v);
    return v.GetD1();
}
D2* dynamic_cast_D2(B* b)
{
    BVisitor v;
    b->Visit(v);
    return v.GetD2();
}

いやー,こんなのよく思いつきますね,感心します.

                                     99/04/13(火) まさーる(BYI20012)

ちょっとわかりにくいかもしれませんが,最後にある dynamic_cast_D1 に D1 オブジェクトのポインタを引数として渡すと D1 ポインタが, D2 オブジェクトのポインタを渡すと NULL が返ってくることがわかると思います.今流行のテストケースで書いてみるとこんな感じでしょうか:

D1 d1;
D2 d2;

void test_dynamic_cast_D1() 
{
   assertEquals(&d1, dynamic_caset_D1(&d1));
   assertEquals(  0, dynamic_caset_D1(&d2));
}

void test_dynamic_cast_D2() 
{
   assertEquals(  0, dynamic_caset_D2(&d1));
   assertEquals(&d2, dynamic_caset_D2(&d2));
}
  

(CppUnit は使ったことがないので適当です.void* へキャストが必要かも)

もちろん,Visitor パターンなので Visitor パターンの問題点(新しい B のサブクラスを追加するという仕様変更に弱い)をそのまま受け継いでいます^^;.

参考資料