tags:

views:

41

answers:

1

I wrote a small tcp client using boost::asio, providing the following function:

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);
}

My idea is to offer the user of my TcpClient class asynchronous operations, without to worry about thread handling, io_services and so on.

Now the code calling the above function from my unittest class looks like this:

CTestTcpClient header

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

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);
    }
}

Now here is the problem: the while loop will never exit, because the is_completed flag is set in a copy of the m_readHandler. If I set a breakpoint in operator() of my handler, I can check and compare the this pointer. It seems boost::asio is copying my handler, call the operator() there, and return. My original handler is never touched at all.

The boost documentation says that copies will be made of the handler as required. So that seems to be ok, but what can I do to achieve the desired behaviour?

Thanks for any help

+1  A: 

The typical idiom I've used for completion handlers is boost::bind a member function using boost::shared_from_this to ensure the object in question does not go out of scope. This is prevalent in nearly all of the Boost.Asio examples as well.

If you don't want to change your design you could try boost::ref, for example:

client.AsyncRead2(receiveBuffer, TESTDATA_SIZE, boost::ref(m_readHandler) );
Sam Miller
nabulke