2011-01-05 13 views
2

ネットワーククライアントとサーバーの両方で操作を管理するためのコードがあります。しかし、ここでは、クライアントまたはサーバーによって排他的に呼び出されるいくつかの関数があり、誤ってサーバー上でクライアント関数を呼び出す(またはその逆)ことはバグの大きな原因です。C++の派生関数にコードを自動的に追加するには

これらの種類のプログラミングエラーを減らすために、関数にタグを付けて、誤用された場合には騒ぎを起こさせようとしています。私の現在の解決策は、クライアントまたはサーバーが必要としないメンバーにアクセスする場合、アサートを呼び出す各関数の開始時に単純なマクロです。しかし、これはクラスの複数の派生インスタンスがある場合に問題になります。すべての子クラスでクライアントまたはサーバー側として実装にタグを付ける必要があります。

基本クラスの仮想メンバのシグネチャにタグを入れて、一度タグ付けするだけで、繰り返し忘れてエラーにならないようにすることができます。基本クラスの実装にチェックを入れ、base :: functionNameのようなものを参照することを検討しましたが、これは、すべての実装に関数呼び出しを手動で追加する必要がある限り、同じ問題になります。理想的には、デフォルトのコンストラクタのように、親バージョンの関数を自動的に呼び出すことができます。

誰かがC++でこれを達成する方法を知っていますか?私が検討すべき代替アプローチがありますか?

ありがとうございます!その後、Base::doit()の実装はそれが適切な環境で呼び出されていていることを確認するためのチェックを行うことができ

class Base { 
public: 
    void doit(const Something &); 
protected: 
    virtual void real_doit(const Something &); 
}; 

class Derived: public Base { 
protected: 
    virtual void real_doit(const Something &); 
}; 

、および:

+4

あなただけの基本クラスに共通のコードを考慮し、それらからのサーバークラスとクライアントクラスを派生ないのはなぜサーバー/クライアント – Falmarri

+2

のための異なるコードおよび/またはクラスを持つべきであるように聞こえますか? – Puppy

答えて

5

別のアプローチは、あなたの発信者が実際に呼び出すものとは異なるメソッドをオーバーライドするかもしれません仮想real_doit()関数を呼び出します。派生クラスは保護された仮想関数をオーバーライドし、どちらのクラスのユーザーも保護された関数を呼び出すことはできません。

Base::doit()関数はではなく、仮想です。そのため、派生クラスは誤って間違ったものを上書きすることはできません。 (人々は試してみることはできますが、うまくいけばすぐ呼び出されます)

+1

+1:これは非仮想インタフェースイディオム*と呼ばれ、事前条件と事後条件を1回だけ実行することを許可します。 –

+0

ありがとうございます。これは私の目的にとって最もクリーンで、最も破壊的なアプローチではないようです。また、イディオムの名前を投稿してくれたMatthieuに感謝します。前に見たことがありましたが、それが標準的なアプローチであるとは気付きませんでした。 – Ian

3

あなたが提案したことは信じられないほど複雑です。簡単な解決策は

class CommonStuff { 
    // all common code that anybody can safely call 
}; 

class ServerBase : public CommonStuff { 
    // only what the server is allowed to call; can safely be overwritten 
}; 

class ClientBase : public CommonStuff { 
    // only what the client is allowed to call; can safely be overwritten 
}; 

コンパイル時の施行は、ランタイムの執行の任意の並べ替えよりもはるかに優れているだろうように聞こえます。

+0

+1:NVIは知っているよいイディオムであるため、グレッグの答えをアップvしましたが、クラスに決して呼び出されるべきではないメソッドがある状況をどのように理解できるか分かりません。私が関心を持つ限り。 –

1

あなたのクラスを再設計することなく、あなたが求めていることをやるための方法はありません。最も簡単な解決策は、サーバー関数を宣言しないClientインターフェイス(純粋仮想)クラスと、クライアント関数を宣言しないインターフェイスクラスを持つServerインターフェイスを両方のインターフェイスから継承することです。次に、クライアントプログラムで、Clientインターフェイスに宣言されていないメソッドへのアクセスを許可しないClientインターフェイスへの参照(またはポインタ)を使用します。サーバー上でServerインターフェイスを使用します。

これにより、派生クラスをServerまたはClientとしても使用できます。

1

私は、このライブラリを3つのライブラリに分割することを検討します。ほとんどのものを持つ基本ライブラリ、サーバー専用ライブラリ、およびクライアント専用ライブラリ。クライアントがサーバーライブラリを使用しない限り、あなたは良いです。あなたはいくつかの余分なクラスを追加終わる可能性(クラスProcessorは、各サブクラスがベースにはない一つの追加機能を持っているBaseProcessorClientProcessor、およびServerProcessorに分割することがあります。)

をそれが動作しない場合は、可能性がありますサーバー/クライアントチェックをクラスコンストラクタに入れ、そこにアサーションを呼び出しますか? (サーバー専用またはクライアント専用の場合はメソッドにではなく、クラスに対して細分化されている場合にのみ機能します)。

これでうまくいかない場合は、ライブラリのさまざまなバージョンを実際にコンパイルするのは意味がありますかそれがサーバービルドかクライアントビルドかに基づいていますか? #ifdef SERVERBUILD#ifdef CLIENTBUILDでメソッドとその宣言を囲み、それらが両方とも定義されていないことを確認します(#if defined(SERVERBUILD) && defined(CLIENTBUILD)#error Can't define both!)。

0

私はGreg Hewgillの答えに投票しましたが、あなたがリクエストするような「側面」を追加する方法を考えました。

class Base { 
protected: 
    class Aspect { 
    public: 
     Aspect(int x) { 
      std::cout << "aspect" << std::endl; 
     } 
    }; 
public: 
    virtual void doit(const Something &arg, const Aspect hook = 0) 
    { 
     std::cout << "doit(" << arg << ")" << std::endl; 
    } 
}; 

発信者がちょうどbase.doit(arg)Aspect以降はデフォルトの引数であると言うことができます:私はここに(クラスBaseとメソッドdoitを)彼の命名規則を使用していました。コンストラクタはdoitより前に実行され、そのデストラクタ(画像は表示されません)が実行されます。残念ながら、デフォルトの引数hook = thisを作るための私の最初の考え方は許されません。

子供は同じ署名でdoitを上書きして同じ効果を得ることができます。

+0

私はあなたが何を得ているかを見ていると思います。したがって、クライアントがそれを呼び出そうとすると、インストラクターにアサーションを持つアスペクトクラス "ServerOnly"がありますか? – Ian

+0

私は本当にこのアプローチのように、と私は私が私に「仮想ボイドのdoIt(constの何かのように見える関数定義を与え、デフォルトの引数を適用する「の#define SERVER_ONLY_RESTRICTIONのconstのRestrictServerOnly _hook = 0」のようなマクロを定義した場合、それは本当にエレガントな作品見つけ&arg、SERVER_ONLY_RESTRICTION); "しかし、私が注意しなければならないことは、非公開ではなく保護する必要があるか、子クラスが基本関数から派生することができないということです。 – Ian

+0

Aspectクラスは、ベースクラスのメンバーにアクセスできるようにするために、グローバルスコープの情報にしかアクセスできないことに気付きました。すでに述べたように、ネストされたクラスにアクセスする方法は考えられません。 "hook = this"はデフォルトの引数として許可されていません。 – Ian

関連する問題