2010-08-12 3 views
1

j'ai écrit un petit client tcp utilisant boost :: asio, assurant la fonction suivante:empêchant coup de pouce de faire une copie d'un mon gestionnaire de rappel

typedef boost::function<void(const bs::error_code& errCode, size_t bytesTransferred)> ReadHandler; 

void CTcpClient::AsyncRead2(std::vector<char>& data, size_t length, ReadHandler readCompletedCallback) 
{ 
    async_read(m_tcpData->socket, ba::buffer(data, length), readCompletedCallback); 
} 

Mon idée est d'offrir à l'utilisateur de ma classe TcpClient asynchrone opérations, sans se soucier de la gestion des threads, io_services et ainsi de suite.

Maintenant, le code appelant la fonction ci-dessus de ma classe unittest ressemble à ceci:

tête CTestTcpClient

class CTestTcpClient : public ::testing::Test 
{ 
public: 
    CTestTcpClient(void); 
    virtual ~CTestTcpClient(void); 

    struct ReadCompletedHandler 
    { 
     ReadCompletedHandler() : m_isCompleted(false){}; 

     void operator()(const boost::system::error_code& errCode, size_t bytesTransferred) 
     { 
      m_isCompleted = true; // my breakpoint here, checking this pointer 
     }; 

     bool isCompleted(void) 
     { 
      return m_isCompleted; 
     } 

    private: 
     bool m_isCompleted; 
    }; 

    ReadCompletedHandler m_readHandler; 
}; 

CTestTcpClient source de

TEST_F(CTestTcpClient, testAsynchronousRead_Success) 
{ 
    CTcpClient client(testService); 
    // Skipped code to setup echo server, connecting and sending of data 
    // Receive echo 
    vector<char> receiveBuffer(TESTDATA_SIZE); 

    client.AsyncRead2(receiveBuffer, TESTDATA_SIZE, m_readHandler); 

    while (!m_readHandler.isCompleted()) 
    { 
     client.Wait(200); 
    } 
} 

est le problème maintenant ici : la boucle while ne sortira jamais, car le flag is_completed est définir dans un copier du m_readHandler. Si je définis un point d'arrêt dans operator() de mon gestionnaire, je peux vérifier et comparer le pointeur this. Il semble boost :: asio copie mon gestionnaire, appelle l'opérateur() et revient. Mon gestionnaire d'origine n'est jamais touché du tout.

La documentation d'amplification indique que des copies seront faites du gestionnaire si nécessaire. Cela semble donc bien, mais que puis-je faire pour atteindre le comportement souhaité?

Merci pour toute aide

+0

+1 pour une question bien formatée. –

Répondre

1

L'idiome typique que je l'ai utilisé pour les gestionnaires d'achèvement est boost::bind une fonction de membre à l'aide boost::shared_from_this pour assurer l'objet en question ne va pas hors de portée. Ceci est répandu dans presque tous les Boost.Asio examples ainsi.

Si vous ne voulez pas changer votre conception, vous pouvez essayer boost::ref, par exemple:

client.AsyncRead2(receiveBuffer, TESTDATA_SIZE, boost::ref(m_readHandler)); 
+0

J'ai essayé d'utiliser boost :: ref comme vous l'avez suggéré, et cela fonctionne parfaitement :-) Parfois, la solution est si facile, si vous le savez mais .... Sam, merci pour votre temps et aide – nabulke

Questions connexes