Pages

s_dump() for ZeroMQ version 3

Have been the ØMQ version 2 utility functions defined in zhelpers.h ported to version 3? If so, this post is not so useful as it could have been, but I guess it still has some interesting bit to show.

What I am doing here is rewriting the s_dump() function for ZeroMQ 3.1 on C++11, using some stuff like casting, manipulators, "auto" keyword in its new skin, lambda functions, a couple of STL algorithms. All in all a piece of fun code, I'd say.

What the s_dump() function does is dumping to standard output console a message pending on the specified socket. Here is my rewrite:
void dumpMessage(void* skCheck) // 1
{
    std::cout << "----------------------------------------" << std::endl;
    do {
        zmq_msg_t message;
        zmq_msg_init(&message);
        zmq_recvmsg(skCheck, &message, 0); // 2
        unsigned char* data = static_cast<unsigned char*>(zmq_msg_data(&message)); // 3
        int size = zmq_msg_size(&message);
        auto isBin = [](unsigned char uc){ return (uc < 32 || uc > 127) ? true : false; }; // 4
        bool isText = (std::find_if(data, data + size, isBin) == data + size) ? true : false; // 5
        std::cout << (isText ? "TXT" : "BIN") << " [" << std::setw(3) << std::setfill('0') << size << "] ";
        std::for_each(data, data + size, [isText](unsigned char c) // 6
        {
            if(!isText)
                std::cout << std::setw(2) << std::hex << static_cast<int>(c); // 7
            else
                std::cout << c;
        });
        std::cout << std::endl;
        zmq_msg_close(&message);
    }
    while(moreFrames(skCheck)); // 8
}
1. Just a matter of taste, I reckon this name is more descriptive. The input parameter is a socket, assumed to be "good", it is a very trusting piece of code.
2. The recv() function version 2 has been renamed recvmsg in version 3.
3. A message could be in text or binary format, so better using bytes (unsigned char for C/C++) as underlying type.
4. This line could have been included in the next one, but I suspected it was better to split them and make the code clearer. Here I define a lambda function, not bothering to specify its actual class type, falling back instead to the "auto" keyword, leaving to the compiler the burden of correctly identify it. It should be almost immediate its purpose, it gets in input a byte, checks if it is in the range of plain Latin characters and return true if it is considered binary.
5. Check if the message could be considered textual or should be interpreted as binary. It calls the STL algorithm find_if() to get the first binary byte in the data. That function returns "end" if the search fails, and in our case that is represented by the pointer to the array plus its size.
6. Another lambda function, here called as predicate for the STL algorithm for_each(), here we are using also the capture clause, since we want to use the boolean isText in this closure.
7. In case of binary we print each element as a number (that's way we cast it int, otherwise it would be interpreted as a character), in hexadecimal format, reserving two positions to each one. Being a number, the filling character is zero. In case of text a blank would have been the default choice.
8. Check this was a non-final part of a multi-part message. If so, we iterate till all the message frames are processed.
Here is the function to check if there are more frames for the current message:
bool moreFrames(void* skCheck)
{
    int more; // 1
    size_t more_size = sizeof(int);
    zmq_getsockopt(skCheck, ZMQ_RCVMORE, &more, &more_size);
    return more != 0; // 2
}
1. This is a difference between ZeroMQ version 2 and 3. Before it was a fixed sized variable (64 bit), now it is a plain int.
2. Returns true if the RCVMORE option in the specified socket is set.

No comments:

Post a Comment