2013-05-20 23 views
2

当我运行这段代码时,出现std :: bad_functon_call异常。 我无法弄清楚这个例外是什么原因。 它由receiveCallback中的async_receive引发。 在被调用之前receiveCallback是否从内存中清除出来?C++ lambda自传异常

//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)); 
+0

虽然lambda的可读性和排序可以很好,但由于生命周期,它往往很困难。作为替代方案,可能值得考虑[协程](http://stackoverflow.com/a/13997290/1053968)。 –

回答

2

你的代码是未定义行为:当拉姆达捕获receiveCallback按值,receiveCallback尚未初始化,因此拉姆达得到副本是垃圾(使用GCC 4.7我甚至得到了段错误,而不是std::bad_function_call)。顺便提一句,acceptCallback表现出同样的问题。

一个明显的解决方案是通过引用而不是按值来捕获receiveCallback,但是随后它会造成与对象的生命周期有关的问题(在整个I/O操作期间对象必须保持活动状态,但只要您退出accept拉姆达就会被破坏)。

这是一个鸡和鸡蛋的情况:通过引用捕获将不会工作,因为生存问题,你需要捕获价值来解决这个问题,但通过值捕获使未复制的对象的副本,所以你需要通过引用捕获解决。咩。

注意receiveCallback是另一种方式不正确:您可以通过值捕捉message过,这意味着只要拉姆达叫你最终不会与boost::asio被填充的缓冲器,但是那是缓冲区的副本当lambda被实例化时(换句话说,再次未初始化的垃圾)。


既然已经做了诊断,如何解决这个问题并编写工作代码呢?

在我看来,完全放弃lambda。写一个类,它将保存你的message缓冲区和onMessageReceivedCallback,并且有acceptreceive成员函数。

取决于boost::asio您可能需要确切的API来“包装”的成员函数std::function对象来获取asio可以使用回调(std::mem_fn这里是你的朋友,不要忘记std::bind所产生的第一个参数std::function反对您的类的实例)。