[zeromq-dev] [Midly OT] Seeking advice on generic message definition for abstract classes

Claudio Carbone erupter at libero.it
Mon Dec 17 21:07:04 CET 2012


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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.zeromq.org/pipermail/zeromq-dev/attachments/20121217/1bc70c3f/attachment.htm>


More information about the zeromq-dev mailing list