我正在尝试用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);
是私有的。我能做什么/我的替代方案是什么?
答案 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;类。在其他情况下,这看起来像设计错误。