2011-01-05 5 views
2

네트워크 클라이언트와 서버 모두에서 작업을 관리하기위한 코드가 있습니다. 그러나 여기 저기에 클라이언트 또는 서버에 의해 독점적으로 호출되는 몇 가지 함수가 있으며 실수로 서버에서 클라이언트 함수를 호출하는 경우 (또는 그 반대로) 중요한 버그 소스입니다.C++의 파생 함수에 코드를 자동으로 추가하는 방법

이러한 종류의 프로그래밍 오류를 줄이기 위해 함수에 태그를 지정하여 오용 된 경우 일괄 처리를 발생 시키려고합니다. 내 현재 솔루션은 클라이언트 또는 서버가 구성원에게 액세스해서는 안되는 경우 각각의 함수를 시작할 때 간단한 매크로로 어설 션을 호출합니다. 그러나 이것은 클래스의 파생 인스턴스가 여러 개있을 때 문제가 발생합니다. 모든 자식 클래스에서 클라이언트 또는 서버 측 구현에 태그를 지정해야합니다.

기본 클래스의 가상 멤버의 서명에 태그를 추가하면 태그를 한 번만 넣고 반복적으로 잊어 버려서 오류가 발생하지 않게 할 수 있습니다. 나는 기본 클래스 구현에 체크를 넣은 다음 base :: functionName과 같은 것으로 참조했지만, 모든 구현에 함수 호출을 수동으로 추가해야하는만큼 동일한 문제가 발생합니다. 이상적으로, 기본 생성자처럼 자동으로 호출되는 부모 버전의 함수를 가질 수 있습니다.

누구나 C++에서 이와 같은 것을 달성하는 방법을 알고 있습니까? 고려해야 할 다른 방법이 있습니까?

감사합니다. 다음 Base::doit()의 구현은 그것이 적절한 환경에서 호출되는 것 있는지 확인하기 위해 검사를 할 수

class Base { 
public: 
    void doit(const Something &); 
protected: 
    virtual void real_doit(const Something &); 
}; 

class Derived: public Base { 
protected: 
    virtual void real_doit(const Something &); 
}; 

및 :

+4

서버/클라이언트에 대해 다른 코드 및/또는 클래스가 있어야하는 것처럼 들립니다. – Falmarri

+2

일반 코드를 기본 클래스로 제외하고 서버 클래스와 클라이언트 클래스를 파생시키는 것이 왜 좋을까요? – Puppy

답변

5

또 다른 방법은 발신자가 실제로 전화가 아닌 다른 메소드를 오버라이드 (override) 할 수 있습니다 가상 real_doit() 기능을 호출하십시오. 파생 클래스는 보호 된 가상 함수보다 우선하며 두 클래스의 사용자는 보호 된 함수를 호출 할 수 없습니다.

Base::doit() 함수는 이 아니고 가상이므로 실수로 잘못된 클래스를 재정의 할 수 없습니다. (사람들은 시도 할 수는 있지만, 전화가 걸리지 않으면 곧 알게되기를 바랍니다.)

+1

+1 : 이것은 비 가상 인터페이스 관용구 *라고하며 사전 조건과 사후 조건을 한 번만 시행 할 수 있습니다. –

+0

감사. 이것은 내 목적을위한 가장 깨끗하고 파괴적인 접근 방식 인 것처럼 보입니다. 또한 이디엄의 이름을 게시 한 Matthieu에게 감사드립니다. 전에 본 적이 있지만 그것을 사용하는 표준적인 접근 방식을 깨닫지 못했습니다. – Ian

3

당신이 제안한 것은 엄청나게 복잡합니다. 간단한 해결책은

class CommonStuff { 
    // all common code that anybody can safely call 
}; 

class ServerBase : public CommonStuff { 
    // only what the server is allowed to call; can safely be overwritten 
}; 

class ClientBase : public CommonStuff { 
    // only what the client is allowed to call; can safely be overwritten 
}; 

컴파일 시간 강제 조치 런타임 집행의 어떤 종류보다 훨씬 더 나은 것 같은데.

+0

+1 : NVI가 알고있는 좋은 관용구이기 때문에 나는 그레그 응답을지지했다. 그러나 나는 클래스가 결코 호출되어서는 안되는 방법을 가지고있는 상황에 어떻게 대처할 수 있는지 이해하지 못한다. 그것은 코드 냄새 다. 지금까지 내가 걱정으로. –

1

수업을 다시 디자인하지 않고도 묻고있는 것을 할 수있는 언어에는 (내가 알고있는) 방법이 없습니다. 가장 간단한 해결책은 서버 기능을 선언하지 않는 Client 인터페이스 (순수 가상) 클래스와 클라이언트 기능을 선언하지 않는 인터페이스 클래스 인 Server을 가지며 통합 코드가 두 인터페이스에서 (공개적으로) 상속되도록하는 것입니다. 그런 다음 클라이언트 프로그램에서 Client 인터페이스에 선언되지 않은 메소드에 대한 액세스를 허용하지 않는 Client 인터페이스에 대한 참조 (또는 포인터)를 사용하십시오. 서버에서 Server 인터페이스를 사용하십시오.

이렇게하면 파생 클래스를 Server 또는 Client으로 사용할 수도 있습니다.

1

나는이 라이브러리를 세 개의 라이브러리로 분할하는 것을 고려할 것입니다 : 대부분이있는 기본 라이브러리, 서버 전용 라이브러리 및 클라이언트 전용 라이브러리. 클라이언트가 서버 라이브러리를 사용하지 않는 한 당신은 훌륭합니다. 당신은 몇 가지 추가 클래스를 추가 끝낼 수 있습니다 (클래스 Processor 각 서브 클래스가 기본하지 않는 하나 개의 추가 기능이 BaseProcessor, ClientProcessorServerProcessor로 분할 수 있습니다.)

를 문제가 해결되지 않을 경우, 수 당신은 서버/클라이언트 체크를 클래스 생성자에 넣고 거기에 어서션을 호출하면됩니까? (서버 전용 또는 클라이언트 전용이 메서드가 아니라 클래스에 세분화 된 경우에만 작동합니다.)

그래도 작동하지 않으면 라이브러리의 다른 버전을 실제로 컴파일하는 것이 바람직합니까 , 그것은 서버 또는 클라이언트 빌드 여부에 따라? #ifdef SERVERBUILD#ifdef CLIENTBUILD으로 메서드와 선언을 둘러싸고 둘 다 정의되지 않았는지 확인 (#if defined(SERVERBUILD) && defined(CLIENTBUILD), #error Can't define both!)해야합니다.

0

나는 Greg Hewgill의 답변에 투표했지만, 당신이 요청한 것과 같은 "aspect"를 추가하는 방법에 대해 생각해 봤습니다.

class Base { 
protected: 
    class Aspect { 
    public: 
     Aspect(int x) { 
      std::cout << "aspect" << std::endl; 
     } 
    }; 
public: 
    virtual void doit(const Something &arg, const Aspect hook = 0) 
    { 
     std::cout << "doit(" << arg << ")" << std::endl; 
    } 
}; 

발신자 그냥 base.doit(arg)Aspect 이후에 기본 인자라고 할 수 있습니다 : 여기 (클래스 Base 및 방법 doit)를 자신의 명명 규칙을 사용했다. 해당 생성자는 doit 전에 실행되며 해당 소멸자 (그림은 표시되지 않음)가 실행됩니다. 안타깝게도 기본 인수 인 hook = this을 만드는 첫 번째 아이디어는 허용되지 않습니다.

어린이는 동일한 서명으로 doit을 무시하고 동일한 효과를 얻을 수 있습니다.

+0

나는 당신이 무엇을 얻고 있는지 보았습니다. 따라서 클라이언트가 호출하려고하면 강사에 어설 션이있는 "ServerOnly"애스펙트 클래스가 있어야합니다. – Ian

+0

나는이 접근 방식을 정말 좋아한다. "#define SERVER_ONLY_RESTRICTION const RestrictServerOnly _hook = 0"과 같은 매크로를 정의하면 기본 인수가 적용되어 "virtual void doit (const Something & arg, SERVER_ONLY_RESTRICTION); " 한 가지주의해야 할 점은 aspect를 private 대신에 보호해야하거나 자식 클래스가 기본 함수에서 파생 될 수 없다는 것입니다. – Ian

+0

저는 Aspect 클래스가 기본 클래스의 멤버에 액세스 할 수 있어야 할 때 전역 범위의 정보에만 액세스 할 수 있다는 것을 알았습니다. 당신이 이미 말했듯이, "hook = this"가 기본 인자로 허용되지 않기 때문에 중첩 된 클래스에 접근하는 방법을 생각할 수 없습니다. – Ian

관련 문제