2011-11-13 13 views
3

我正嘗試在C++中編寫一個命名管道服務器。我有一個名爲client_pool的類,其中包含一個管道實例的容器和一個公共成員函數write,它將數據異步地發送到所有連接的客戶端。聲明後添加好友類

問題是,客戶端有意外斷開的趨勢。發生這種情況時,WriteFileEx的呼叫以​​失敗。當發生這種情況時,我想去client_pool類,並告訴它關閉客戶端句柄並將其從容器中移除。但是,由於WriteFileEx太難以使用,我在匿名命名空間中創建了一個名爲write_context的助手類。

所以最終的結果是,我想打電話給在client_pool私有方法,這是在clients.h宣佈,從類write_context,這是在clients.cpp聲明。類似的東西(詳細信息/錯誤處理省略):

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(); 
    } 
} 

顯然,線owner->pimpl->disconnect(target);沒有編譯,因爲pimpl是私人的。我能做什麼/我的替代品是什麼?

回答

2

直接訪問pimpl->連接和write_context直接在您的client_pool :: write方法是有點違背PIMPL方法的地步。否則,你可能會發生一件事,直到你遇到像這樣的問題。

我只是創建一個實現::寫入方法,您可以通過參數和指向client_pool的指針。

1

我認爲,如果你使用一個名爲命名空間,而不是匿名命名空間,你可以放置在類定義內這一行:

friend void namespace_name::next_chunk() 

或者還有的將所有多餘的東西在匿名命名空間爲靜態函數中的類。由於靜態方法和結構不會更改ABI,因此可以通過預處理技巧將其從所有其他實例中隱藏起來。

還是有殘酷和可怕的:

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

不像傑拉爾德的解決方案那麼幹淨,但對於upvote足夠的黑客。 –

0

對不起,但這不是使用PIMPL慣用語的最佳方式。

PIMPL隱藏了它的所有者的實現細節,只能通過它的所有者接口訪問它。因此,如果您想調用「client_pool :: implementation」方法而不是將其移至「client_pool」接口,並且其實現應該將工作委託給「client_pool :: implementation」類。在其他情況下,這看起來像設計錯誤。

+0

問題是disconnect()方法將成爲公共接口的一部分,但它確實不是。 write()方法是接口需要的唯一方法。 disconnect()的目的是擺脫死管 - 在我看來,一個實現細節。 –

+0

在你的體系結構中,「write_context :: next_chunk」完成了所有寫作工作並檢測到管道已經死亡。它似乎只需要管道處理,並可以與「client_pool」分開工作。您可以返回相應的返回值或在此方法中拋出異常,以指示管道已死,並通過斷開死管來處理client_pool :: write中的這種情況。通過這種重構可以消除類之間不必要的依賴關係:「write_context」類不需要「client_pool」來完成他的工作。 – vladv

+0

不,我不能。 next_chunk被異步調用。 –

1

品牌write_contextimplementation的好友。通過pimpl作爲ownerwrite_context