2011-11-13 15 views
3

C++で名前付きパイプサーバーを作成しようとしています。私はパイプのインスタンスのコンテナを含むclient_poolというクラスと、接続されたすべてのクライアントに非同期でデータを送信する単一のpublicメンバ関数writeを持っています。宣言後にフレンドクラスを追加する

問題は、クライアントが突然切断される傾向があることです。この場合、WriteFileExへのコールは​​で失敗します。それが起こると、私はclient_poolクラスに行き、クライアントハンドルを閉じてそれをコンテナから削除するように指示します。しかし、WriteFileExは使いにくいので、匿名の名前空間にwrite_contextというヘルパークラスを作成しました。

だから、最終的な結果は、私がclients.cppで宣言されたクラスwrite_contextから、clients.hで宣言されているclient_poolでプライベートメソッドを、呼び出したいこと、です。 (省略取り扱い詳細/エラー)そのような何か:

clients.h

class client_pool { 
    struct implementation; 
    std::unique_ptr<implementation> pimpl; 
public: 
    void write(uint8_t *data, size_t size); 
}; 

clients.cpp

明らか
struct client_pool::implementation { 
    set<HANDLE> connected; 
    // ... 
    void disconnect(HANDLE victim) 
    { 
     CloseHandle(victim); 
     connected.erase(victim); 
    } 
}; 

namespace { struct write_context { 
    OVERLAPPED overlapped; 
    client_pool *owner; 
    HANDLE target; 
    const uint8_t *buffer; 
    size_t total_size; 
    size_t written; 
    // ... 
    void next_chunk() 
    { 
     if(!WriteFileEx(/* ... */, write_context::completion_routine)) { 
      if(GetLastError() == ERROR_NO_DATA) { 
       // I want to do something like 
       owner->pimpl->disconnect(target); 
      } 
     } 
    } 
    static void CALLBACK completion_routine(DWORD errcode, DWORD transferred, LPOVERLAPPED overlapped) 
    { 
     auto self = reinterpret_cast<write_context*>(overlapped); 
     self->written += transferred; 
     if(errcode == ERROR_MORE_DATA) { 
      self->next_chunk(); 
     } else { 
      delete self; 
     } 
    } 
}; } 

void client_pool::write(uint8_t *data, size_t size) 
{ 
    for each handle in pimpl->connected { 
     auto context = new write_context(this, handle, data, size); 
     context->next_chunk(); 
    } 
} 

pimplので、コンパイルされませんowner->pimpl->disconnect(target);ラインプライベートです。私は何ができますか/私のオルタナティブは何ですか?

答えて

2

が直接pimpl->あなたclient_poolに直接接続し、write_contextアクセスが:: writeメソッドは、PIMPLイディオムのポイントにちょっと反しています。このような問題に遭遇するまでは、おそらく別のケースを作ることができます。

私は、あなたが引数とclient_poolへのポインタを渡すことができるimplementation :: writeメソッドを作成します。

1

私はあなたが名前の名前空間の代わりに、匿名の名前空間を使用する場合は、クラス定義内の行を置くことができると思う:

friend void namespace_name::next_chunk() 

それとも内部の静的関数として匿名の名前空間内のすべての余分なものをそこに置いていますクラス。静的メソッドと構造体はABIを変更しないので、プリプロセッサのトリックを介して他のすべてのインスタンスから非表示にすることができます。

それとも残忍で恐ろしいがあります:

#define class struct 
#define private public 
#define protected public 
+0

ジェラルドの解決策ほどきれいではありませんが、アップコートのために十分にハックします。 –

0

申し訳ありませんが、これはPIMPLイディオムを使用する最善の方法ではありません。

PIMPLは、所有者の実装の詳細を隠し、所有者インターフェイスを介してのみアクセスする必要があります。したがって、 "client_pool :: implementation"メソッドを "client_pool"インタフェースに移動する必要がある場合は、そのメソッドを "client_pool :: implementation"クラスに委譲する必要があります。それ以外の場合、これはデザインバグのように見えます。

+0

問題はdisconnect()メソッドがパブリックインターフェイスの一部であることですが、実際はそうではありません。 write()メソッドは、インターフェイスに必要な唯一のメソッドです。 disconnect()の目的は、死んだパイプを取り除くことです - 私の意見では、実装の詳細。 –

+0

アーキテクチャでは、 "write_context :: next_chunk"は書き込み中のすべてのジョブを実行し、そのパイプが停止したことを検出します。パイプハンドルだけが必要で、 "client_pool"とは別に動作するようです。このメソッドでは、対応する戻り値を返すか、例外をスローしてパイプが死んだことを通知し、dead pipeを切断してclient_pool :: writeでそのような状況を処理できます。このリファクタリングによって、クラス間の不要な依存関係を排除できます。 "write_context"クラスでは、自分の仕事を行うために "client_pool"は必要ありません。 – vladv

+0

いいえ、できません。 next_chunkは非同期に呼び出されます。 –

1

write_contextimplementationの友人にします。 ownerwrite_contextとしてpimplに渡します。

関連する問題