04WebServer 2.0を開発していて、C++の多重継承でちょっと困った問題に遭遇しました。
04Webserver 2.0では、フィルタを用いてサーバの動作を変更できます。
フィルタはC++のクラスとして宣言するのですが、楽をしようとして多重継承を使ったら、上手く行きませんでした。
フィルタのインタフェースの定義は、だいたい下記のようになっています。
IModuleは、共通のラッパを用いてインタフェースを管理するためのもので、インタフェース開放時に呼び出すDeleteModuleを持っています。
普通にIFilterとIFilterFactoryを別のクラスで継承して実装すれば問題ないのですが、面倒なのでIFilterとIFilterFactoryを多重継承する一つのクラスで実装しようとしました。
IFilterはリクエストごとに生成・開放され、IFilterFactoryはサーバ起動時に生成、停止時に開放されます。
同じクラスで実装する場合、IFilterは当然個別のインスタンスを持たず、IFilterFactoryと同じインスタンスを共有しますから、IFilter::DeleteModuleは何もしない空メソッドで、IFilterFactory::DeleteModuleは、実装クラスを削除するメソッドでなくてはなりません。
つまり、多重に継承された同じ基底クラスの同じ名前のメソッドに別の処理を行わせる必要があります。
調べてみると、C++ではこのような事は出来ないようです
IModuleの継承にvirtualを使えば、IModuleは一つしか継承されなくなりますが、これでは問題は解決しません。
そこで考えたのが、IModule::DeleteModuleにインタフェースのポインタを引数として持たせる方法です。
具体的には、以下のような感じです。
この方法なら、利用側が削除時に削除対象を引数に入れて呼び出せば、実装側で判断できます。
ただ、これでは、もはや手段が目的になってしまっていて、無駄に複雑さと分かりにくさが増しています。
考え直し、最終的にはIModuleを継承するのをやめ、それぞれFilterDeleteとFilterFactoryDeleteと言うメソッドを用意しました。
結局、ラッパ側を改良したほうが実装が楽で、人間にも分かりやすい構造になりました。
ps.
「さくじょじ」→「裂く女児」と変換されるこのIMEは異常だと思う・・・
ネットカフェのPCだけど。
04Webserver 2.0では、フィルタを用いてサーバの動作を変更できます。
フィルタはC++のクラスとして宣言するのですが、楽をしようとして多重継承を使ったら、上手く行きませんでした。
フィルタのインタフェースの定義は、だいたい下記のようになっています。
//! ベースインタフェース class IModule { public: //! 削除 virtual void DeleteModule() = 0; }; //! フィルタインタフェース class IFilter : public IModule { // ソース参照 }; //! フィルタ生成インタフェース class IFilterFactory : public IModule { //! フィルタ取得 virtual IFilter *GetFilter() = 0; };
IModuleは、共通のラッパを用いてインタフェースを管理するためのもので、インタフェース開放時に呼び出すDeleteModuleを持っています。
普通にIFilterとIFilterFactoryを別のクラスで継承して実装すれば問題ないのですが、面倒なのでIFilterとIFilterFactoryを多重継承する一つのクラスで実装しようとしました。
IFilterはリクエストごとに生成・開放され、IFilterFactoryはサーバ起動時に生成、停止時に開放されます。
同じクラスで実装する場合、IFilterは当然個別のインスタンスを持たず、IFilterFactoryと同じインスタンスを共有しますから、IFilter::DeleteModuleは何もしない空メソッドで、IFilterFactory::DeleteModuleは、実装クラスを削除するメソッドでなくてはなりません。
つまり、多重に継承された同じ基底クラスの同じ名前のメソッドに別の処理を行わせる必要があります。
調べてみると、C++ではこのような事は出来ないようです
IModuleの継承にvirtualを使えば、IModuleは一つしか継承されなくなりますが、これでは問題は解決しません。
そこで考えたのが、IModule::DeleteModuleにインタフェースのポインタを引数として持たせる方法です。
具体的には、以下のような感じです。
//! ベースクラス class IModule { public: //! 削除 virtual void DeleteModule(void *ifPtr) = 0; }; //! インプリメント class CFilterImplement : public IFilterFactory, public IFilter { //! 削除 virtual void DeleteModule(void *ifPtr) { // 削除対象がIFilterFactoryの場合のみ削除 if(ifPtr == (IFilterFactory*)this) delete this; }; };
この方法なら、利用側が削除時に削除対象を引数に入れて呼び出せば、実装側で判断できます。
ただ、これでは、もはや手段が目的になってしまっていて、無駄に複雑さと分かりにくさが増しています。
考え直し、最終的にはIModuleを継承するのをやめ、それぞれFilterDeleteとFilterFactoryDeleteと言うメソッドを用意しました。
結局、ラッパ側を改良したほうが実装が楽で、人間にも分かりやすい構造になりました。
ps.
「さくじょじ」→「裂く女児」と変換されるこのIMEは異常だと思う・・・
ネットカフェのPCだけど。