2

I am learning boost::asio, and encountered a problem. Basically, asio::buffer_cast() works strange on my machine. Any help would be greatly appreciated.

I have sample code as follows,

#include <iostream>
#include <vector>
#include <sstream>
#include <iomanip>
#include <boost/asio.hpp>
int main ()
{  
std::ostringstream type_stream;
type_stream << std::setw(4) << 100;
std::cout<<"type_stream:"<<type_stream.str()<<std::endl;

std::ostringstream head_stream;
head_stream << std::setw(10) << 92;
std::cout<<"head_stream:"<<head_stream.str()<<std::endl;

std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(type_stream.str()));
buffers.push_back(boost::asio::buffer(head_stream.str()));

auto test = buffers[0];
const unsigned char* p1 = boost::asio::buffer_cast<const unsigned char*>(test);
std::cout<<"type_stream again:"<<std::string(reinterpret_cast<const char*>(p1))<<std::endl;

auto test2 = buffers[1];
const unsigned char* p2 = boost::asio::buffer_cast<const unsigned char*>(test2);
std::cout<<"head_stream again:"<<std::string(reinterpret_cast<const char*>(p2))<<std::endl;

return 0;
}

If you run this code here: https://wandbox.org/permlink/4VkxJ4TFgjHzrath, it works fine. The output is

type_stream: 100
head_stream:        92
type_stream again: 100
head_stream again:        92

But when put the code in a function and run on my machine, I got output as

type_stream: 100
head_stream:        92
type_stream again:        92
head_stream again:        92

Am I doing anything wrong in the code? It seems on my machine the second buffer covered the first one. I have gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0, and the newest boost::asio.

sehe
  • 374,641
  • 47
  • 450
  • 633
J.Yang
  • 49
  • 7

2 Answers2

1

Yes, there's Undefined Behaviour.

The problem is that

 buffers.push_back(boost::asio::buffer(type_stream.str()));

the type_stream.str() returns a temporary std::string and therefore the buffer isn't valid after the push.

Fix:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>

std::string format_buf(int v, int width) {
    std::ostringstream oss;
    oss << std::setw(width) << v;
    return oss.str();
}

int main() {
    std::string type_str = format_buf(100, 4);
    std::string head_str = format_buf(92, 10);

    std::cout << "type_stream first: " << std::quoted(type_str) << std::endl;
    std::cout << "head_stream first: " << std::quoted(head_str) << std::endl;

    std::vector<boost::asio::const_buffer> buffers;
    buffers.push_back(boost::asio::buffer(type_str));
    buffers.push_back(boost::asio::buffer(head_str));

    auto test = buffers[0];
    const unsigned char *p1 = boost::asio::buffer_cast<const unsigned char *>(test);
    std::cout << "type_stream again:" << std::quoted(reinterpret_cast<const char *>(p1)) << std::endl;

    auto test2 = buffers[1];
    const unsigned char *p2 = boost::asio::buffer_cast<const unsigned char *>(test2);
    std::cout << "head_stream again:" << std::quoted(reinterpret_cast<const char *>(p2)) << std::endl;
}

Prints

type_stream first: " 100"
head_stream first: "        92"
type_stream again:" 100"
head_stream again:"        92"

BONUS

Since you're effectively trying to do fixed-width formatting (?) why not make it simpler and more robust:

Live On Coliru

char type[4] = {};
char head[10] = {};

bio::stream<bio::array_sink> tstream(type);
tstream << 100;

bio::stream<bio::array_sink> hstream(head);
hstream << 92;

It's more robust because with your code the fields may be too wide (e.g. with type containing 12345)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • thank you! How stupid I am :) I don't know much about array_sink, it seems concatenate type and head together. – J.Yang Nov 01 '18 at 14:52
  • It will if you use the same buffer. I'll make the difference between `tstream` and `hstream` a bit more obvious in my "BONUS" snippet – sehe Nov 01 '18 at 14:58
0

With help from @sehe, the following code works. But I got new worries. Is it okay to create char type[4] = {}; char head[10] = {}; char type2[2] = {}; inside prepareBuffers() function? As the buffer was created by buffers.push_back(boost::asio::buffer(type));, I suspect type will be destroyed when goes out of prepareBuffers() function. But the in the main() the program still can access the content of buffer. Is there any copy happens?

#include <boost/asio.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>

namespace bio = boost::iostreams;

void prepareBuffers(std::vector<boost::asio::const_buffer> & buffers){
char type[4] = {};
char head[10] = {};

bio::stream<bio::array_sink> tstream(type);
bio::stream<bio::array_sink> hstream(head);

tstream << 555555;
hstream << 923;

std::cout << "type_stream first: " << std::quoted(type) << std::endl;
std::cout << "head_stream first: " << std::quoted(head) << std::endl;

buffers.push_back(boost::asio::buffer(type));
buffers.push_back(boost::asio::buffer(head)); 

auto test = buffers[0];
const unsigned char* p1 = boost::asio::buffer_cast<const unsigned char*>(test);
std::cout<<"in function type_stream again:"<<std::string(reinterpret_cast<const char*>(p1))<<std::endl;

test = buffers[1];
const unsigned char* p2 = boost::asio::buffer_cast<const unsigned char*>(test);
std::cout<<"in function head_stream again:"<<std::string(reinterpret_cast<const char*>(p2))<<std::endl;
}

int main() {
std::vector<boost::asio::const_buffer> buffers2;
prepareBuffers(buffers2);

auto test21 = buffers2[0];
const unsigned char* p1 = boost::asio::buffer_cast<const unsigned char*>(test21);
std::cout<<"type_stream again:"<<std::string(reinterpret_cast<const char*>(p1))<<std::endl;

auto test22 = buffers2[1];
const unsigned char* p2 = boost::asio::buffer_cast<const unsigned char*>(test22);
std::cout<<"type_stream again:"<<std::string(reinterpret_cast<const char*>(p2))<<std::endl;

return 0;
}
J.Yang
  • 49
  • 7