- ベストアンサー
2つのvisitorでデータを処理する
- Nodeクラスの派生クラス(データ)をPPVisitorとIRVisitorで処理する方法を考えます。
- Visitorクラスには各データに対するvisit関数を宣言していますが、PPVisitorとIRVisitorが互いのvisit関数を定義すると抽象クラスになるため、一つのvisit関数を共用する方法を探します。
- 自身のvisitor以外のvisit関数を何もしない関数として定義する方法は一つの解決策ですが、改善の余地があると考えます。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
void* IDNode::accept(Visitor* v){ return v->visit(this); } void* OpNode::accept(Visitor* v){ return v->visit(this); } ... void* IDNode::accept(Visitor* v, void* obj){ return v->visit(this, obj); } void* OpNode::accept(Visitor* v, void* obj){ return v->visit(this, obj); } ... というのであれば、オーバーロードをやめて、 ・常にvisitは(Visitor*, void*)を引数にする ・第2引数を使わないケースの場合は第2引数にはNULLを設定する のような仕様にしてはいかがでしょうか。 つまり、Node側は void* IDNode::accept(Visitor* v, void* obj=NULL){ return v->visit(this, obj); } void* OpNode::accept(Visitor* v, void* obj=NULL){ return v->visit(this, obj); } ... のようにしておき、Visitor側は全て void* visit(Node* n, void* obj)=0; とする。 もう1つの改善案は、そもそもPPVisitor、IRVisitorをそれぞれVisitorの派生クラスにしない、というのはどうでしょうか? つまり、Node側は void* IDNode::acceptForPP(PPVisitor* v){ return v->visit(this); } void* OpNode::acceptForPP(PPVisitor* v){ return v->visit(this); } ... void* IDNode::acceptForIR(IRVisitor* v, void* obj){ return v->visit(this, obj); } void* OpNode::acceptForIR(IRVisitor* v, void* obj){ return v->visit(this, obj); } ... としておき、Visitor側は class PPVisitor { public: virtual void* visit(OpNode* n)=0; virtual void* visit(IDNode* n)=0; }; class IRVisitor { public: virtual void* visit(OpNode* n, void* obj)=0; virtual void* visit(IDNode* n, void* obj)=0; }; このようにするのはどうでしょうか。
その他の回答 (1)
- qwertfk
- ベストアンサー率67% (55/81)
質問内容が良くわからないので確認させてください。 ・まず、Visitor はこのようになっている class Visitor { public: virtual void visit(Node* n)=0; }; class PPVisitor : public Visitor{ ... }; class IRVisitor : public Visitor{ ... }; ・そして、Nodeという基底クラスがあり、それらの派生クラスがある class Node{...}; class NodeTypeA : public Node{...}; class NodeTypeB : public Node{...}; ・各ノードがVisitorを受け入れるときに、 PPVisitorクラスのオブジェクトは、 virtual void* accept(Visitor* v)=0; IRVisitorクラスのオブジェクトは、 virtual void* accept(Visitor* v, void* obj)=0; で受け入れなければならないようになっている。 ・ノードの各実装クラスはPPVisitor、IRVisitorのどちらかのみを受け入れたい場合があるが その場合上記のオーバーロードのどちらかを空の実装にするのが面倒。 class NodeTypeA : public Node { public: virtual void* accept(Visitor* v) { ... 何か処理をする } virutal void* accept(Visitor* v, void* obj) { // IRVisitorは使わないが定義しないとコンパイルできないので空の関数を作る } }; ここまでで何かまちがっていますでしょうか? 以降、上記が正しいという前提の回答になります。 まず、NodeがどのVisitorが来るのかを意識するような実装になっていてはいけません。 virtual void* accept(Visitor* v)=0; という関数があるのであれば、VisitorのインタフェースにはVisitorとして必要なものだけを定義し、 NodeはVisitorのインタフェースのみを考慮するような実装でなければなりません。 それでもどうしても上記のオーバーロードの構造になる、という場合、たとえば、 class NotImplementedException : public std::exception { ... }; class Node { public: virtual void* accept(Visitor* v) { throw NotImplementedException; } virtual void* accept(Visitor* v, void* obj) { throw NotImplementedException; } }; class NodeTypeA : public Node { public: virtual void* accept(Visitor* v) { ... 処理を書く } }; class NodeTypeB : public Node { public: virtual void* accept(Visitor* v, void* obj) { ... 処理を書く } }; のようにNode自体を抽象クラスではなく、普通の基底クラスにすれば空の関数を定義する必要はなくなります。
お礼
各Nodeクラスのaccept関数は void* IDNode::accept(Visitor* v){ return v->visit(this); } void* OpNode::accept(Visitor* v){ return v->visit(this); } ... void* IDNode::accept(Visitor* v, void* obj){ return v->visit(this, obj); } void* OpNode::accept(Visitor* v, void* obj){ return v->visit(this, obj); } ... のように定義しています。また、Visitorは class Visitor{ public: virtual void* visit( OpNode* n)=0; virtual void* visit( IDNode* n)=0; ... virtual void* visit( OpNode* n, void* obj)=0; virtual void* visit( IDNode* n, void* obj)=0; ... }; 上記のように各Nodeに対応した関数を宣言しています。 PPVisitorでは引数が一つのほうのacceptおよびvisitを使えればよく、IRVisitorでは引数が2つのほうを使いたいのです。 NodeもVisitorも抽象クラスをやめれば良かったのでしょうか。
お礼
>・常にvisitは(Visitor*, void*)を引数にする >・第2引数を使わないケースの場合は第2引数にはNULLを設定する こちらの方法で綺麗に纏めることが出来そうです。 回答ありがとうございました。