声明后添加好友类

时间:2011-11-13 16:07:23

标签: c++ oop encapsulation

我正在尝试用C ++编写命名管道服务器。我有一个名为client_pool的类,它包含管道实例的容器,以及一个公共成员函数write,它将数据异步发送到所有连接的客户端。

问题是,客户有意外断开的倾向。发生这种情况时,对WriteFileEx的调用将失败并显示ERROR_NO_DATA。当发生这种情况时,我想转到client_pool类并告诉它关闭客户端句柄并将其从容器中删除。但是,由于WriteFileEx非常难以使用,我在匿名命名空间中创建了一个名为write_context的辅助类。

所以最终结果是,我想在client_pool中调用一个私有方法,该方法在clients.h中声明,来自类write_context,它在{{1}中声明}}。类似的东西(省略细节/错误处理):

clients.h

clients.cpp

clients.cpp

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

显然,行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);是私有的。我能做什么/我的替代方案是什么?

4 个答案:

答案 0 :(得分:2)

直接在client_pool :: write方法中直接访问pimpl-&gt; connected和write_context有点与pimpl习语的观点相反。否则你可能会遇到一个案例,直到你遇到这样的问题。

我只想创建一个implementation :: write方法,您可以通过参数和指向client_pool的指针进行传递。

答案 1 :(得分:1)

我认为如果您使用命名的命名空间而不是匿名命名空间,则可以将此行放在类定义中:

friend void namespace_name::next_chunk()

或者将所有额外的东西放在匿名命名空间中作为类中的静态函数。因为静态方法和结构不会更改ABI,所以可以通过预处理器技巧将其隐藏在所有其他实例中。

或者那是残酷和可怕的:

#define class struct
#define private public
#define protected public

答案 2 :(得分:1)

write_context成为implementation的朋友。将pimpl作为owner的{​​{1}}传递。

答案 3 :(得分:0)

抱歉,这不是使用PIMPL习惯用法的最佳方式。

PIMPL隐藏了它所有者的实施细节,只能通过其所有者界面访问。所以,如果你想调用&#34; client_pool :: implementation&#34;方法应该转移到&#34; client_pool&#34;接口及其实现应该将工作委托给&#34; client_pool :: implementation&#34;类。在其他情况下,这看起来像设计错误。