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

Sebastian Lauwers sebastian.lauwers at gmail.com
Mon Dec 17 19:13:10 CET 2012


Hi Claudio,

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.

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

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?

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.

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.

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);

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.

[1]:
https://github.com/firestarter/firestarter/blob/master/src/common/zmq/zmqsocket.hpp

-S.

On 17 December 2012 13:48, Claudio Carbone <erupter at libero.it> wrote:

>  I've done a bit of work.
>
> Here is an example main with the new class
>
> int main(int argc, char** argv) {
>     Saetta::Context mycontext;
>     Saetta::Publisher mypubber(mycontext); //=new Saetta::Server;
>     Saetta::Subscriber mysubber(mycontext); //=new Saetta::Client;
>     mysubber.SubscribeTopic("B");
>
>     for(int i=0;i<10;i++)
>     {
>         mypubber.PubMessage(3,"A","We don't want to see this","1");
>         mypubber.PubMessage(3,"B","We would like to see this","1");
>         //mypuber.worker();
>         usleep(100);
>         cout << mysubber.worker();
>         usleep(1500000);
>     }
>     mypubber.~Publisher();
>     mysubber.~Subscriber();
>     return (EXIT_SUCCESS);
> }
>
> I'm seeking help here: the constructors can work like it's depicted above,
> or providing them with an external context.
> The above mentioned piece of code does not work: when the Publisher
> constructor attempts to create the socket, ZMQ_SOCKET returns EFAULT.
> I checked with GDB and the context pointer is absolutely correct, so I
> can't see where is the problem.
>
> A version that actually works is this
>
> int main(int argc, char** argv) {
>     void *mycontext = zmq_init(1);
>     Saetta::Publisher mypubber(mycontext); //=new Saetta::Server;
>     Saetta::Subscriber mysubber(mycontext); //=new Saetta::Client;
>     mysubber.SubscribeTopic("B");
>
>     for(int i=0;i<10;i++)
>     {
>         mypubber.PubMessage(3,"A","We don't want to see this","1");
>         mypubber.PubMessage(3,"B","We would like to see this","1");
>         //mypuber.worker();
>         usleep(100);
>         cout << mysubber.worker();
>         usleep(1500000);
>     }
>     mypubber.~Publisher();
>     mysubber.~Subscriber();
>     return (EXIT_SUCCESS);
> }
>
> Attached you'll find the source of the class (currently testing only Pub
> and Sub).
> As you can see the variables passed are the same, yet when I just pass the
> pointer it works, when I pass the whole class it doesn't work.
> Hope someone can chime in.
>
> Claudio
>
>
>
> On 16/12/12 15:01, Claudio Carbone wrote:
>
> Hello all.
>
> Recently I've been moving from C to C++ and I'd really like to exploit
> the capabilities of this language.
> One of the scenario I've been thinking about is a ZMQ generic class able
> to do all the work.
>
> Generally speaking abstracting from the socket and context creation is
> easy: you just need to tell the constructor what kind of socket you
> want, and where you want it.
> What would be much more useful and powerful (and complex) would be to
> design a way to pass message syntax in an abstract form, so you can add
> as many messages you want to the class.
>
> That way a sender subclass would create as many sending hooks as message
> types, while a receiver class would create as many callbacks as message
> types.
> I think this would be a nice design, thus I'm asking here for advices
> and ideas about such an implementation.
>
> Thank you
> Claudio
> _______________________________________________
> zeromq-dev mailing listzeromq-dev at lists.zeromq.orghttp://lists.zeromq.org/mailman/listinfo/zeromq-dev
>
>
>
> _______________________________________________
> zeromq-dev mailing list
> zeromq-dev at lists.zeromq.org
> http://lists.zeromq.org/mailman/listinfo/zeromq-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.zeromq.org/pipermail/zeromq-dev/attachments/20121217/4efea591/attachment.htm>


More information about the zeromq-dev mailing list