Hi Adam, I suspect you could write a wrapper class around ArrayPtr<const ArrayPtr<const word>> that implements ConstBufferSequence while adding minimal overhead. The class would have one data member of type ArrayPtr<...> and would implement the necessary methods to make it a ConstBufferSequence. You might need to similarly wrap each segment, if asio::buffer() would otherwise create a copy.
BTW, careful about `asio::buffer(a.begin(), a.size())` -- I think you need to multiply the size by `sizeof(capnp::word)`, since asio::buffer seems to be byte-based. -Kenton On Sat, Feb 11, 2017 at 4:03 AM, <[email protected]> wrote: > Hi, > > I'm trying to send Cap'n Proto messages over boost::asio streams, > specifically ZeroMQ sockets. > > I'm currently using capnp::messageToFlatArray(…) combined with > boost::asio::buffer(…) like so: > > auto data = capnp::messageToFlatArray(message); > socket.async_send(boost::asio::buffer(data.begin(), data.size()), [](const > boost::system::error_code& ec, std::size_t bytes_transferred){…}); > > While this works, the call to messageToFlatArray creates a copy of the > message contents. > > I found the following Stack Overflow reply from Kenton that briefly > outlined the idea of using MessageBuilder::getSegmentsForOutput() > combined with ZeroMQ multipart messages to avoid the overhead of a copy: > > Send the message as a ZeroMQ multi-part message, with the parts being the >> message's segments. capnp::MessageBuilder::getSegmentsForOutput() >> returns an array of arrays pointing into the raw message segments. >> capnp::SegmentArrayMessageReader >> takes such an array of arrays as input. If you can send the array of arrays >> as a multipart message, then you can skip using capnp/serialize.h at all, >> since its only purpose is to combine the segments into a single message >> with a segment table. In your case, ZeroMQ would be in charge of >> remembering where each segment starts and ends. >> > > -- Kenton Varda - http://stackoverflow.com/a/32042234 > > All of asio's stream write functions take a ConstBufferSequence that > allows the caller to pass multiple buffers to be written in a single call. > For the ZeroMQ-asio binding (zmqp), passing multiple buffers to a write > call sends a multipart message. > > The result of MessageBuilder::getSegmentsForOutput() isn't > directly convertible to a ConstBufferSequence as the outer ArrayPtr > doesn't provide ::value_type or ::const_iterator and the inner ArrayPtr's > aren't convertible to asio::const_buffers. > > I've come up with the following code to do the conversion: > > auto raw_segments = message.getSegmentsForOutput(); > > std::vector<asio::const_buffer> segments(raw_segments.size()); > std::transform(raw_segments.begin(), raw_segments.end(), segments.begin(), > [](const kj::ArrayPtr<const capnp::word> a) { > return asio::buffer(a.begin(), a.size()); > }); > > socket.async_send(segments, [](const boost::system::error_code& ec, > std::size_t > bytes_transferred){}); > > While this works, I do ask: > Is there an easier (or more "correct") way to convert the output of > MessageBuilder::getSegmentsForOutput() to a ConstBufferSequence? > Is there a more efficient method? Is it possible to forgo the conversion > of the outer array to a vector and the inner arrays to buffers? A > ConstBufferSequence is basically a "array of arrays" anyway. > > > Regards, > Adam > > -- > You received this message because you are subscribed to the Google Groups > "Cap'n Proto" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > Visit this group at https://groups.google.com/group/capnproto. > -- You received this message because you are subscribed to the Google Groups "Cap'n Proto" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. Visit this group at https://groups.google.com/group/capnproto.
