2012-09-17 3 views
0

네트워크 (TCP-IP)를 기반으로하는 사용자 지정 IPC 시스템이 있습니다.수정 된 팩토리 메서드에서 boost :: shared_ptr

#include "boost/shared_ptr.hpp" 
#include <string> 

using namespace std; 

class TCommand { 
public: 
    typedef boost::shared_ptr<TCommand> Ptr; 

    TCommand() { 
     cout << " Creating TCommand..." << endl; 
    } 

    virtual ~TCommand() { 
     cout << " Destroying TCommand..." << endl; 
    } 

    static TCommand * factory(int classID); 

    virtual void parse(const char *data, int dataSize) = 0; 
    virtual void print() = 0; 
    virtual std::string getType() = 0; 

}; 


class TPingCommand : public TCommand { 
public: 
    static const int classID = 1; 
    int value; 

    TPingCommand() : TCommand() { 
     cout << " Creating TPingCommand..." << endl; 
    } 

    virtual ~TPingCommand() { 
     cout << " Destroying TPingCommand..." << endl; 
    } 

    virtual void parse(const char *data, int dataSize) { 
    if (dataSize < 4) throw 1; 

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; 
    } 

    virtual void print() { 
     cout << " TPingCommand:" << endl; 
     cout << " value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl; 
    } 

    virtual std::string getType() { 
     return "TPingCommand"; 
    } 
}; 

class TOtherCommand : public TCommand { 
public: 
    static const int classID = 2; 
    int value; 
    char value2; 
    short int value3; 

    TOtherCommand() : TCommand() { 
     cout << " Creating TOtherCommand..." << endl; 
    } 

    virtual ~TOtherCommand() { 
     cout << " Destroying TOtherCommand..." << endl; 
    } 

    virtual void parse(const char *data, int dataSize) { 
    if (dataSize < 7) throw 1; 

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; 
    this->value2 = data[4]; 
    this->value3 = data[5] << 8 | data[6]; 
    } 

    virtual void print() { 
     cout << " TOtherCommand:" << endl; 
     cout << " value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl; 
     cout << " value2 = " << dec << this->value2 << " (0x" << hex << (int)this->value2 << ")" << endl; 
     cout << " value3 = " << dec << this->value3 << " (0x" << hex << this->value3 << ")" << endl; 
    } 

    virtual std::string getType() { 
     return "TOtherCommand"; 
    } 
}; 


TCommand * TCommand::factory(int classID) { 
    cout << " Factory for classID = " << dec << classID << " (0x" << hex << classID << ")" << endl; 
    switch (classID) { 
    case TPingCommand::classID: return new TPingCommand(); break; 
    case TOtherCommand::classID: return new TOtherCommand(); break; 
    default: throw 1; 
    } 
    } 


TCommand::Ptr receiveFromNetwork(int test, TCommand::Ptr knownCommand) 
{ 
    // Receive command header from network. 
    // int classID is the command class internal ID. 
    // int dataSize is the command's body size in bytes. 
    // For instance: 
    // int classId = 2; 
    // int datasize = 7; 

    int classId = 1; 
    int dataSize = 4; 
    char data[10]; 

    if (test == 0) { 
     cout << " Using test data 0..." << endl; 
     classId = 1; 
     dataSize = 4; 
     data[0] = 0x01; data[1] = 0x02; data[2] = 0x03; data[3] = 0x04; 
    } else if (test == 1) { 
     cout << " Using test data 1..." << endl; 
     classId = 2; 
     dataSize = 7; 
     data[0] = 0x11; data[1] = 0x12; data[2] = 0x13; data[3] = 0x14; data[4] = 0x41; data[5] = 0x16; data[6] = 0x17; 
    } 

    TCommand::Ptr cmd; 
    if (knownCommand == 0) { 
     cout << " No command provided." << endl; 
     cmd.reset(TCommand::factory(classId)); 
     cout << " Command created from factory: " << cmd->getType() << endl; 
    } else { 
     cmd = knownCommand; 
     cout << " Command provided: " << cmd->getType() << endl; 
    } 

    cout << " Parsing data..." << endl; 
    cmd->parse(data, dataSize); 

    // The command was identified as TOtherCommand (classID = 2). 
    // The factory returned a TOtherCommand instance. 
    // The TOtherCommand's parse method will check the dataSize is suitable (7 bytes are necessary). 
    // The parse method will unserialize data to their fields. 
    // This way, the fields would be: 
    // data = 0x11121314; 
    // data2 = 0x42; // 'A' as char. 
    // data3 = 0x1213; 

    return cmd; 
} 

void caller() { 
    // Case 1 (ok): 
    // I know I'm going to receive a TPingCommand. 
    cout << "Test case 1:" << endl; 
    TCommand::Ptr known(new TPingCommand()); 
    TCommand::Ptr cmd1 = receiveFromNetwork(0, known); 
    cmd1->print(); 

    // Case 2 (problems): 
    cout << "Test case 2:" << endl; 
    TCommand::Ptr dummy; 
    TCommand::Ptr cmd2 = receiveFromNetwork(1, dummy); 
    cmd2->print(); 

    cout << "Teardown..." << endl; 
} 

int main() { 
    caller(); 
} 

receiveFromNetwork는 몇 가지 경우에, 그러나, 네트워크로부터 명령을 수신하는 데 사용되는 수정 팩토리 메소드의 일종이다, 내가 아는 :

코드 (및 설명 울부 짖는 소리를) 고려 어떤 유형의 명령을 선행 적으로 받아들이려고하는지, 그래서 나는 그것의 인스턴스를 생성하고 함수에 전달한다 (knownCommand처럼). 명령 클래스는 TCommand 클래스에서 파생됩니다. knownCommand는 함수에 의해 반환됩니다 (매개 변수로 전달 했으므로 필요하지는 않지만 다른 경우에는 유용합니다).

다른 모든 경우 네트워크에서 수신 한 처음 몇 바이트는 classID 명령을 설명하며이 함수 내에서 적절한 TCommand 인스턴스를 만드는 데 사용합니다. 그런 다음 명령이 네트워크 데이터에서 파싱되고 함수의 끝에서 반환됩니다. knownCommand는 단지 더미 TCommand 인스턴스입니다.

알려진 명령을 전달하면 잘 작동합니다. 더미 명령을 매개 변수로 전달하면 충돌이 발생합니다 (아는 한 두 번 무료).

knownCommand에 대한 TCommand 참조를 사용하려고 생각했지만 공유 포인터를 반환해야하고 동일한 원시 포인터가 두 개의 다른 공유 포인터 인스턴스에 의해 관리 될 수 있기 때문에 할 수 없습니다. 하나는 호출자 메소드이고 다른 하나는 receiveFromNetwork 메소드 내부).

누구나 그 문제를 해결하는 방법에 대한 좋은 아이디어가 있습니까?

여기에 문제가 시나리오의 부분 Valgrind의 출력입니다 :

==31859== Thread 2: 
==31859== Invalid read of size 4 
==31859== at 0x805D7B0: boost::detail::sp_counted_impl_p<TVideoGetSourceSizeCommand>::dispose() (checked_delete.hpp:34) 
==31859== by 0x407FF42: Server::receiveFromNetwork() (sp_counted_base_gcc_x86.hpp:145) 
==31859== by 0x40800AF: Server::serverThread(void*) (Server.cpp:107) 
==31859== by 0x434496D: start_thread (pthread_create.c:300) 
==31859== by 0x42B398D: clone (clone.S:130) 

정말 감사합니다.

+0

거짓 명령으로 무엇을 의미합니까? 그것은 get()을 지원합니까? –

+0

더미 명령은 널 포인터를 가지고있는 shared_ptr입니다 (허용되지는 않습니다). 호출자 함수에서 더미로 생성했습니다. 이것 좀 봐. 그것이 잘못되었다고 생각합니까? – Marcus

+0

디버거에서 실행 한 다음 발생하는 정확한 오류 메시지를 알려주시겠습니까? valgrind에서 실행 해 볼 수도 있습니다. – codeling

답변

0

발견.

코드의 호출 부분에서 shared_ptr이 잘못된 방식으로 "중복"되었습니다. 물론

boost::shared_ptr<DerivedType> ptr1(new DerivedType(...)); 
boost::shared_ptr<BaseType> ptr2(ptr1.get()); 

그것이 중복 무료와 프로그램이 다운 될 거라고 : 결과 실수는 다음과 같이이었다.

코드는 그다지 바보가 아니었지만, 결국 단순화 된 오류 시나리오였습니다. 비록 사소한 실수였습니다.

모든 도움 주셔서 감사합니다.

관련 문제