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.