2011-11-13 4 views
3

C++로 명명 된 파이프 서버를 작성하려고합니다. 파이프 인스턴스의 컨테이너를 포함하는 client_pool 클래스와 연결된 모든 클라이언트에 비동기 적으로 데이터를 보내는 단일 공용 멤버 함수 write이 있습니다.선언 후 친구 클래스 추가

문제는 클라이언트가 예기치 않게 연결이 끊기는 경향이 있다는 것입니다. 이 경우 WriteFileEx에 대한 호출이 ERROR_NO_DATA과 함께 실패합니다. 그런 일이 생기면 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(); 
    } 
} 

, 라인 owner->pimpl->disconnect(target);pimpl 때문에 컴파일되지 않습니다 비공개입니다. 나는 무엇을 할 수 있는가/나의 대안은 무엇인가?

friend void namespace_name::next_chunk() 

또는 정적 기능은 내부 익명 네임 스페이스에있는 모든 여분의 물건이 배치 : 당신은 익명의 네임 스페이스 대신 명명 된 네임 스페이스를 사용하는 경우

답변

2

직접 접근 pimpl-> 연결하고 client_pool :: 쓰기 방법은 pimpl 관용구의 점에 좀 반대되는 직접에 write_context. 이와 같은 문제가 발생할 때까지는 그렇지 않은 경우를 만들 수 있습니다.

나는 client_pool에 인수와 포인터를 전달할 수있는 implementation :: write 메소드를 만들 것이다.

1

난 당신이 클래스 정의 내부의 줄을 배치 할 수 있다고 생각 수업. 정적 메서드와 구조체는 ABI를 변경하지 않기 때문에 전 처리기 트릭을 통해 다른 모든 인스턴스에서 숨길 수 있습니다.

또는 잔인하고 끔찍한 거기 :

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

제럴드의 솔루션만큼이나 깨끗하지는 않지만 upvote에 충분히 적합합니다. –

0

죄송 합니다만 PIMPL 관용구를 사용하는 가장 좋은 방법은 아닙니다.

PIMPL은 소유자의 구현 정보를 숨기며 소유자 인터페이스를 통해서만 액세스해야합니다. 그래서, "client_pool :: implementation"메서드를 "client_pool"인터페이스로 옮겨야하고 그것의 구현이 "client_pool :: implementation"클래스에 작업을 위임해야하는 것보다 "client_pool :: implementation"메서드를 호출하고자한다면. 다른 경우에 이것은 디자인 버그처럼 보입니다.

+0

문제는 실제로는 아니지만 disconnect() 메소드가 공용 인터페이스에 포함된다는 것입니다. write() 메서드는 인터페이스에 필요한 유일한 메서드입니다. disconnect()의 목적은 죽은 파이프를 없애는 것입니다. 제 생각에는 구현 세부 사항입니다. –

+0

아키텍쳐에서 "write_context :: next_chunk"는 쓰는 동안 모든 작업을 수행하고 그 파이프가 죽었다는 것을 탐지합니다. 파이프 핸들 만 있으면되고 "client_pool"과는 별도로 작업 할 수 있습니다. 이 메소드에서 해당 반환 값을 반환하거나 예외를 throw하여 해당 파이프가 작동하지 않음을 알리고 해당 파이프를 연결 해제하여 client_pool :: write에서 이러한 상황을 처리 할 수 ​​있습니다. 이 재 모델링을 통해 클래스 간의 불필요한 종속성을 제거 할 수 있습니다. "write_context"클래스는 "client_pool"이 필요하지 않습니다. – vladv

+0

아니, 할 수 없다. next_chunk는 비동기 적으로 호출됩니다. –

1

write_contextimplementation의 친구로 지정하십시오. ownerwrite_context으로 pimpl으로 전달하십시오.