2017-05-08 1 views
2

멀티 캐스트 메시지를받는 시스템을 업데이트하고 데이터를 클래스로 파싱 한 다음 대기열을 통해 별도의 스레드에 기본 클래스 포인터를 전달합니다. 그런 다음 다른 스레드는 클래스에서 데이터 을 읽어 테이블에 저장합니다.정적 및 동적 캐스팅을 모두 피하십시오.

두 가지 종류의 메시지가 있습니다. 여기

class BaseMsg 
{ 
public: 
    BaseMsg(char tp) : msgType(tp) {} 
    virtual ~BaseMsg() = 0; 

    char msgType; 
} 

class MsgType1 : public BaseMsg 
{ 
public: 
    MsgType1(int a, int b) : BaseMsg('1'), val1(a), val2(b) {} 
    virtual ~MsgType1(); 

    int val1, val2; 
} 

class MsgType2 : public BaseMsg 
{ 
public: 
    MsgType2(double a, double b) : BaseMsg('2'), val1(a), val2(b) {} 
    virtual ~MsgType2(); 

    double val1, val2; 
} 

수신 코드 것 : 여기

void reciveMessage(char *datablob, MsgQueue *queue) 
{ 
    BaseMsg *msgReceived; 
    char type = datablob[0]; // message type is the first character in the data, 
           // the rest of the blob varies based on that type 
    if (type == '1') { 
     // code for parsing type 1 into 2 ints, i1, i2 
     msgReceived = new MsgType1(i1, i2); 
    } else { 
     // code for parsing type 2 into 2 doubles, d1, d2 
     msgReceived = new MsgType2(d1, d2); 
    } 
    queue->push(msgReceived); 
} 

는 별도의 스레드에서 실행되는 프로세스 코드의 다음은 클래스 디자인의 내가 아는

void processMessage(MsgQueue *queue) 
{ 
    BaseMsg *msgToProcess = queue->pop(); 

    if (msgToProcess->msgType == '1') { 
     MsgType1 *mt1 = static_cast<MsgType1 *>(msgToProcess); 
     // do stuff with the message type 1 data 
    } else { 
     MsgType2 *mt2 = static_cast<MsgType2 *>(msgToProcess); 
     // do stuff with the message type 2 data 
    } 
} 

해당 메시지 유형에 대한 검사 다운 캐스팅은 엉망이지만, 대기열을 통해 을 통신해야하는 제약이 주어지면 더 나은 솔루션을 찾을 수 없었습니다. 성능상의 이유로 수행하고 싶지 않은 인 dynamic_cast <을 사용해도 동일한 문제가 발생합니다. 여전히 을 알고 있어야합니다. msgToProcess를 변환 할 클래스의 유형.

캐스팅 체크를 없애려면 어떻게해야합니까? & 캐스팅? 나는 많은 경험이있다 with C & C++,하지만 OO 디자인은 그리 많지 않다. 그래서 내가 알지 못하는 방법이있을 수있다.

또한이 질문을 설명하기 위해 극단적으로 요약 된 예제입니다. 실제로 은 50 가지가 넘는 메시지 유형으로, 초당 수백만 개의 메시지를 수신 할 수 있으므로 성능이 중요합니다.

+1

"할 일"섹션에는 imp 할 수있는 방법이 있습니까? 기본 클래스에서 가상 함수 호출의 관점에서 참조하십시오. – RyanP

+0

'downcast'가 사용되는 장소의 수를 최소화 할 수는 있지만 피할 수는 없습니다. 덜 반복적으로 사용하는 방법에 대한 아이디어는 [다른 SO 포스트에 대한 내 답변] (http://stackoverflow.com/a/43038856/434551)을 참조하십시오. –

+0

RTTI (캐스팅)에 의존하기를 원하지 않는다면 차별적 인 유니온을 사용하고 싶을 수도 있지만 enum에 대한 switch 문이 필요합니다. 비슷한 작업을 수행하는 std :: variant도 있습니다. 나는 성능을 시도하지는 않았지만 어떤 종류의 간접 지정도 저장할 필요가 없으므로 캐시의 지역성으로부터 이점을 얻을 수 있기 때문에 실제로는 빠를 것이라고 가정합니다. – AlexG

답변

2

BaseMsg 클래스 내에서 예를 들어 doStuff()이라는 순수 가상 함수를 정의합니다.
각 파생 클래스는 해당 메서드를 구현합니다. 당신의 processMessage
, 당신은 단순히 다형성의 고유 한 속성을 사용하여 멤버 함수를 사용하도록 제안했다 다른 사람과 msgToProcess->doStuff()

+0

그 해결책을 생각했지만, 이제는 메시지가 포함 된 클래스가 "할 일"섹션에서 진행되는 모든 작업에 대해 알아야합니다. 이는 데이터 조작의 혼란입니다. 재 포장. 목표는 최소 2 개 파트를 최소한으로 묶는 것이 었습니다. 단지 "do stuff"코드가 두 세트의 구조에 대해 알도록하는 것이 었습니다. –

+0

doStuff (BaseProcessor *)가 더 나은 확장 성을 제공 할 수 있습니다.) – felix

+0

This. "다운 캐스팅 메시지 유형에 대한 검사가 엉성함을 알고 있지만 ..."당신은 다형성에 의존해야합니다. 그것이 바로 그 때문입니다. –

3

동의를 호출합니다.

하지만 메시지 클래스를 깨끗하게 유지해야한다는 것도 알고 있습니다. 나는 방문자 패턴 (즉 다형성 (polymorphism)에 의존)를 사용하는 것이이 경우에 좋은 생각이있을 수 있습니다 생각 :

class BaseMsg { 
public: 
    virtual void accept(MsgProcessor& p) = 0; 
}; 

class Msg1 : BaseMsg { 
public: 
    void accept(MsgProcessor& p) { p.process(*this); } 
}; 

class Msg2 : BaseMsg { 
public: 
    void accept(MsgProcessor& p) { p.process(*this); } 
}; 

class MsgProcessor { 
public: 
    void process(Msg1& m); 
    void process(Msg2& m); 
} 

좋은 점은 기능 중 하나는 의 모든에 대한 누락 된 경우이 컴파일되지 것입니다 파생 클래스.

편집 : 고정 물건 의견에서 지적

EDIT2 :

외부 코드베이스 :

class MsgVisitor; 
class BaseMsg { 
public: 
    virtual void accept(MsgVisitor&) = 0; 
}; 

class Msg1; 
class Msg2; 
class MsgVisitor { 
public: 
    virtual void visit(Msg1&) = 0; 
    virtual void visit(Msg2&) = 0; 
}; 

class Msg1 : public BaseMsg { 
public: 
    void accept(MsgVisitor& v) { v.visit(*this); } 
}; 

class Msg2 : public BaseMsg { 
public: 
    void accept(MsgVisitor& v) { v.visit(*this); } 
}; 

지역 코드베이스 :

class MsgProcessor : public MsgVisitor { 
public: 
    void visit(Msg1& m) { ... } // do stuff for Msg1 
    void visit(Msg2& m) { ... } 
}; 

void processMessage(MsgQueue *queue) 
{ 
    BaseMsg *msgToProcess = queue->pop(); 
    msgToProcess->accept(MsgProcessor()); 
    //delete msgToProcess; 
} 
이 코드베이스 외부 코드베이스에서 & 최소한의 변경
+0

위와 같이하면 논리적 계층 구조가 아닌 "할 일"영역에서 사용되는 클래스/구조가 대량으로 다시 작성됩니다. 불행하게도, 그것은 12 년 이상 된 다른 모든 코드이며, 다른 그룹이 관리합니다. 들어오는 데이터를 다시 작성하고이를 레거시 코드에 묶는 임무가 주어졌습니다. 이 코드를 다시 작성하는 것이 좋지만, 지금은 옵션이 아닙니다. :-( –

+0

@DanielJour 귀하의 의견은 질문에 대답하려고 시도조차하지 않습니다. 엄청나게 벗겨진 예제에서 관련없는 항목을 선택하십시오. –

+0

@FlyboyWilson 이는 의견이 아니며 대답이 아닌 이유입니다;) (제공 피드백 및 비평은이 사이트에서의 코멘트 사용 의도 중 하나입니다.) –

관련 문제