Hi Sebastian,

first of all thank you for taking the time to read through my code.


You probably don't want to inherit everything from your generic class. It makes the code less easy to read, and mostly kills off the type safety you should be leveraging.

Pardon me, but what is type safety exactly?
In my opinion it makes the code a lot easier because you essentially have those operations that are exactly the same all in one place: socket creation, binding, etc. Moreover since the result of the operation does not depend on the operation itself but rather on the parameter passed, I can abstract the operation and leverage this abstraction by having all classes use inline methods to act on their private members. Otherwise I'd have a lot of code repetition just to initialize every possible kind of zmq entity: bad. The main problem I found is managing the context and the associated spawned threads on which you have no control whatsoever.
That's why I put context creation outside of the classes.
But left the ability to use a class by itself: if it doesn't get an already existing context, it creates its own.




For example, why is it possible to call ::socketconn() on a Publisher object?

This one makes sense, but I don't know how to avoid this.
A possible workaround is to define two type of generic classes: one for the /binders/ and one for the /connecters/. Other ways to use the available syntax in order to mask some methods are unknown to me in C++ (C# or .NET have something like that if I'm not mistaken).



Second red flag, you use friend classes all over the place. I'm not sure why every subclass of generic makes Context a friend, but again, this kind of violates encapsulation (not really, but close, anyway). When your design makes you declare friends for all subclasses, you should know something's not quite right.

For example, does Context really need to access Publisher's internals?

You are absolutely right!
This was because of my noobness: at first I thought that you had to declare the friendship inside the class that would access the other class's members. Obviously it's wrong: it's the befriended class that has to grant access to the other class. So while at first I filled my code with friends, they are totally useless and wrong.
Thank you for pointing that out to me.



I gave this exercise [1] a shot a few months ago, and have been using (a slightly modified version of) the mentioned API for some time now. It works, but it's not optimal, and it definitely is something I'll be looking at again in the not-too-distant future. In my example, ZMQRequestSocket and ZMQResponseSocket both provide connect() and bind(). It doesn't make any sense, but in my urgency to get on with things, I just chalked it off on the huge list of todos. The main difference between your approach and mine is that I've used virtual multiple inheritance, which is also one of the things I'm not too happy with.

You are far above my comfortable level of understanding of C++ (tongue in cheek). Unfortunately I know what it can do, much less I know how to do it: you see I'm a native C programmer, so my C++ knowledge is fairly basic, I have some experience with OOP on Windows, but other than that I miss a thorough C++ course.
So for example I don't know what a virtual inheritance is at the moment.
Reading about it now it seems a clever solution: only include one copy of the members when doing multiple inheritance. Why don't you like it?



PS: I do realise that the main question in this thread (create interfaces to send messages in a pseudo-abstract manner) isn't answered by my response. I'm not quite sure what you're hoping to achieve, as I have not completely understood the question, but you may have some look checking out the Visitor pattern, which provides you with the ability to bind specific logic to different objects, without actually implementing the logic in said object.

Well what I have posted as code doesn't do anything towards the goal save for the abstraction of classes. I noticed in your example that you make use of Google's protobuf: just today a collegue of mine sent a mail pointing to this project which my save us from reinventing the wheel each and every time. I still have to investigate, but it may well be the solution for abstracting message handling.


Pseudocode:

struct Message {
    std::string content;

    virtual void accept(Visitor const &) { };
};

struct HelloMessage : public Message {
    HelloMessage() : content("Hello!") { };
    void accept(Visitor const & visitor) { visitor.visit(*this); };
};

struct ByeMessage : public Message {
    ByeMessage() : content("Bye!") { };
    void accept(Visitor const & visitor) { visitor.visit(*this); };
};

class Visitor {
    public:
    virtual void visit(Message const & message) { };
    virtual void visit(HelloMessage const & message) = 0;
    virtual void visit(ByeMessage const & message) = 0;
};

class ZMQVisitor : public Visitor {
    private:
    // Let's assume that the zmq_socket and stuff is initliased
    zmq::context_t blah;
    zmq::socket_t sock;
    public:
    void visit(HelloMessage const & message) {
        // Do whatever needed to send a message, this is pseudocode
        zmq_send(this->sock, message.content);
    };
    void visit(ByeMessage const & message) {
        // Send the message, then terminate the connection
        zmq_send(this->sock, message.content);
        zmq_close(this->sock):
    };
};


Assuming the code above, you could do something along the lines of:

ZMQVisitor sender(/* provide values to init */);
HelloMessage foo;
Message bar;
ByeMessage shutdown;

// sends the message
foo.accept(sender);
// doesn't do anything
bar.accept(sender);
// Shuts the connection down
shutdown.accept(sender);


I think I don't understand the purpose: it seems a lot convoluted without achieving much of an improvement. I mean: we have 5 classes in addition to the ZMQ required ones, and yet you have to manually add members for each new message you may want to implement?
Unless I totally misunderstood, if I added a message class
struct HowDoYouDoMessage : public message

I'd then also have to add
void ZMQVisitor::visit(HowDoYouDoMessage const & message)

right?
This is exactly what I'm trying to avoid.
My objective is to have the base classes in place, able to handle anything you can throw at them (given it uses a predefined syntax).
A simple example is my Publisher::PubMessage method
    void Publisher::PubMessage (int count, ...)
    {
        va_list argptr;

        va_start( argptr, count );

        for( ; count > 1; count-- ) {
            //char *mystr = va_arg(argptr, char*);
            s_sendmore (this->_zmq_socket_ptr, va_arg(argptr, char*));
        }
        s_send(this->_zmq_socket_ptr, va_arg(argptr, char*));

        va_end( argptr );
    }

It's not complicated at all (I will complicate it overtime) yet I can pass it any number of arbitrary parameters (as long as they are strings) and it works. This allows for example for having a Publisher sending multiple kinds of messages with correct filterable headers with just one class.

As I said, I'm not sure I understood your initial question, so please don't take it the wrong way if I'm completely off-topic.


No problem, I know my limitations that's why I volunteered my work here in the public arena. I know lots of more powerful things can be done (ROS message generation and handling for example, or even protobuf itself, or mavlink to keep it simple), yet since there is actually none ready for ZMQ and I need one, I'm willing to at least put a few base stones in place.

Claudio
_______________________________________________
zeromq-dev mailing list
[email protected]
http://lists.zeromq.org/mailman/listinfo/zeromq-dev

Reply via email to