c ++ lambda自传异常

时间:2013-05-20 01:26:57

标签: c++ exception lambda boost-asio

当我运行此代码时,我得到std :: bad_functon_call异常。 我无法弄清楚这个例外的原因是什么。 它由receiveCallback中的async_receive引发。 在被调用之前,receiveCallback是否从内存中清除了?

//callback on connection accepted
    std::function<void(const boost::system::error_code& error, tcp::socket* socketPtr)> acceptCallback =
        [this, onMessageReceivedCallback, acceptCallback](const boost::system::error_code& error, tcp::socket* socketPtr)
    {
        cout<<"accept: "<<error.message()<<endl;

        const int bufferSize = 100;
        char* message = new char[bufferSize];

        //callback on message received
        std::function<void(const boost::system::error_code& error,std::size_t bytes_transferred)> receiveCallback =
            [message, bufferSize, socketPtr, onMessageReceivedCallback, receiveCallback](const boost::system::error_code& error,std::size_t bytes_transferred)
        {
            onMessageReceivedCallback(message, bytes_transferred);

            socketPtr->async_receive(
            boost::asio::buffer(message, bufferSize),
            receiveCallback);
        };

        socketPtr->async_receive(
            boost::asio::buffer(message, bufferSize),
            receiveCallback);

        //create socket for the next connection
        socketPtr = new tcp::socket(io_service_);
        //continue accepting connections
        acceptor_.async_accept(*socketPtr, std::bind(acceptCallback, std::placeholders::_1, socketPtr));

1 个答案:

答案 0 :(得分:2)

您的代码是未定义的行为:当lambda按值捕获receiveCallback时,receiveCallback尚未初始化,因此lambda获取的副本是垃圾(使用GCC 4.7我甚至得到段错误而不是{ {1}})。顺便说一下std::bad_function_call表现出同样的问题。

一个明显的解决方案是通过引用而不是按值来捕获acceptCallback,但是它会产生与对象生命周期相关的问题(receiveCallback对象必须在整个持续时间内保持活动状态I / O操作,但只要退出receiveCallback lambda就会被破坏。

这是一个鸡和蛋的情况:由于生命周期问题,通过引用捕获将无法工作,您需要通过值捕获来解决该问题,但是按值捕获会生成未初始化对象的副本,因此您需要通过引用捕获来解决那。 MEH。

请注意,accept以其他方式不正确:您也会按值捕获receiveCallback,这意味着每当调用lambda时,您最终都不会使用{{1}填充的缓冲区,但是使用了实例化lambda时生成的缓冲区的副本(换句话说,再次使用未初始化的垃圾)。


现在已经进行了诊断,如何修复并编写工作代码?

在我看来,完全放弃了lambda。写一个将保留message缓冲区和boost::asio并且具有messageonMessageReceivedCallback成员函数的类。

根据accept的确切API,您可能需要在receive个对象中“包装”您的成员函数,以获得boost::asio可以使用的回调(std::function是您的朋友在这里,不要忘记asio生成的std::mem_fn对象的第一个参数到你的类的实例。)