2

このコードを実行すると、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));
4

1 に答える 1

2

あなたのコードは未定義の動作です:ラムダがreceiveCallback値でキャプチャするとき、receiveCallbackまだ初期化されていないため、ラムダが取得するコピーはガベージです(GCC 4.7では、の代わりにsegfaultも取得しますstd::bad_function_call)。acceptCallbackちなみに、同じ問題を示しています。

明らかな解決策はreceiveCallback、値ではなく参照によってキャプチャすることですが、オブジェクトの有効期間に関連する問題が発生します (receiveCallbackオブジェクトは、I/O 操作の全期間にわたって存続する必要がありますが、すぐに破棄されます)。acceptラムダを終了すると)。

これはニワトリが先か卵が先かという状況です。参照によるキャプチャは有効期間の問題のために機能しません。それを解決するには値によるキャプチャが必要ですが、値によるキャプチャでは初期化されていないオブジェクトのコピーが作成されるため、参照によるキャプチャが必要です。うーん。

receiveCallback別の方法で間違っていることに注意してください:message値でもキャプチャします。つまり、ラムダが呼び出されるたびに、 によって満たされたバッファboost::asioではなく、ラムダがインスタンス化されたときに作成されたバッファのコピーになります (つまり、初期化されていないガベージが再び発生します)。


診断が行われたので、それを修正して動作するコードを書く方法は?

私の意見では、ラムダを完全に削除してください。messageバッファ とを保持し、とメンバー関数onMessageReceivedCallbackを持つクラスを作成します。acceptreceive

正確な API によっては、メンバー関数をオブジェクトにboost::asio「ラップ」して、使用できるコールバックを取得する必要がある場合があります (ここでは友人です。結果のオブジェクトの最初の引数をクラスのインスタンスにすることを忘れないでください)。std::functionasiostd::mem_fnstd::bindstd::function

于 2013-05-20T05:59:48.540 に答える