I'm trying to implement a simple TCP server using ASIO. The main difference here is that I'm using std::unique_ptr
to hold the buffers instead of raw pointers and I'm moving them inside the completion handler in order to extend its lifetime.
Here is the code and it works just fine (I guess)
void do_read() {
auto data = std::make_unique<uint8_t[]>(1500);
auto undesired_var = boost::asio::buffer(data.get(), 1500);
socket_.async_read_some(
undesired_var,
[self = shared_from_this(), data = std::move(data)](auto ec, auto len) mutable {
if (!ec) {
std::cout << "Received " << len << " bytes :)" << std::endl;
} else {
std::cout << "Read error: " << ec.message() << std::endl;
}
}
);
}
Running the code above, I get the following output:
/tcp_echo/cmake-build-debug/bin/tcp_server 6666
Received 9 bytes :)
Note that I created a variable called undesired_var
in order to hold the boost::asio::mutable_buffer
data.
When I try to remove those variables by creating them directly in the socket_.async_read_some
call:
void do_read() {
auto data = std::make_unique<uint8_t[]>(1500);
socket_.async_read_some(
boost::asio::buffer(data.get(), 1500),
[self = shared_from_this(), data = std::move(data)](auto ec, auto len) mutable {
if (!ec) {
std::cout << "Received " << len << " bytes :)" << std::endl;
} else {
std::cout << "Read error: " << ec.message() << std::endl;
}
}
);
}
I got the following output
tcp_echo/cmake-build-debug/bin/tcp_server 6666
Read error: Bad address
It seems that in the second case my std::unique_ptr
is getting destroyed prematurely, but I can figure out why. Did I miss something?
Yeah it's the order in which argument expressions are evaluated.
The std::move
can happen before you do .get()
.
Besides, there seems to be a large problem here:
auto data = std::make_unique<uint8_t>(1500);
That dynamically allocates an unsigned char (uint8_t
) which is initialized from the integer value 1500
. The allocation has size 1 (1 byte).
auto undesired_var = boost::asio::buffer(data.get(), 1500);
You're using it as if it is 1500 bytes. Oops. You probably meant:
auto data = std::make_unique<uint8_t[]>(1500);
The type of data
is now std::unique_ptr<uint8_t[], std::default_delete<uint8_t[]>>
which is an entirely different template specialization