Henry
Henry

Reputation: 113

CPPZMQ - Publish and subscribe with standard vector

From the documentation at: https://brettviren.github.io/cppzmq-tour/index.html#intro, it seems that it is possible with CPPZMQ to send and receive a standard vector by using messages or buffers. However, I have not been able to use the vector from the subscriber, I get an error when trying to access it:

Segmentation error (core dumped)

when I run the following code:

Publisher:

    #include <vector>
    #include <iostream>
    #include <zmq.hpp>
    #include <thread>
    using namespace std;
    using namespace zmq;

    int main()
    {
        vector<float> v(2, 0.0);
        context_t ctx;
        socket_t pub(ctx, ZMQ_PUB);
        const std::string addr = "tcp://127.0.0.1:5678";
        pub.bind(addr);

        while (true)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            v = {0.1, 0.2};
            message_t msg(v);
            auto res = pub.send(msg, send_flags::none);
            cout << "message sent" << endl;
        }
    }

Subscriber:

    #include <vector>
    #include <iostream>
    #include <zmq.hpp>
    using namespace std;
    using namespace zmq;

    int main()
    {
        context_t ctx;
        socket_t sub(ctx, socket_type::sub);
        const std::string addr = "tcp://127.0.0.1:5678";
        sub.set(zmq::sockopt::subscribe, "");
        sub.connect(addr);
        message_t msg;
        const vector<float>* iptr = msg.data<vector<float>>();

        while (true)
        {
            if (sub.recv(msg, zmq::recv_flags::none))
            {
                cout << "msg received" << endl;
                iptr = msg.data<vector<float>>();
                cout << "iptr: " << iptr << endl;
                cout << "element 0: " << (*iptr)[0] << "endl";
            }
        }
    }

My question is:

How do I retrieve the vector in the publisher ? More generally, with a vector of constant length and type, I would need an efficient way to send and receive such vector, for example avoiding copy and avoiding reallocation and destruction at every message. What is the recommended way to do that ?

Upvotes: 0

Views: 1028

Answers (1)

pptaszni
pptaszni

Reputation: 8228

Constructing zmq::message_t directly from STL vector is ok, because iterator based constructor will be called.

std::vector<float> v({0.1, 0.2});
message_t msg(v);

It will internally copy the content of the vector to the underlying zmq_msg_data casted to float*:

std::copy(first, last, data<value_t>());  // value_t == float in this case

However, templated version of message_t::data method

template<typename T> T *data() ZMQ_NOTHROW { return static_cast<T *>(data()); }

is just a static cast from void* to your T*. So if you invoke it like that: msg.data<vector<float>>(); you try to cast raw buffer to complex std::vector class, which is incorrect. Instead, you can copy received raw data to newly created float vector:

std::vector<float> vec;
vec.resize(msg.size()/sizeof(float));  // note that msg size must be divisible by float size
std::memcpy(vec.data(), msg.data(), msg.size());

Alternatively you can get already casted data float* rawData = msg.data<float>(); and use

std::copy(rawData, rawData+msg.size()/sizeof(float), std::back_inserter(vec));

Upvotes: 1

Related Questions