When creating a "Handler Registration" type architecture, how should I pass handlers?

304 views Asked by At

I am for learning purposes writing a UDP client/server protocol in c++, where the communication from the server component of my application to the rest of the application is achieved using handlers.

For example, I might register an Authorization component against the value 1, and an TextMessage component against value 2. The client then first sends a message or two to the Authorization component to login and then starts sending text messages to the TextMessage handler.

This is what I have now:

class NetworkHandler {
    virtual void handleMessage(const endpoint & endpoint, const buffer & message) = 0;
};

Any class that wish to register as a component simply subclass NetworkHandler and call

udpServer.addHandler(handler);

However, I'm having a few issues with deciding how I should pass the handlers, being a newbie and all.

Should I pass the handler by reference? This is easy and convenient, but the question of ownership comes into play - the calling function should probably not have to worry about keeping a reference around for no reason.

Should I copy the handler? This is also easy and convenient, but here there is a question of whether or not my handler is copyable or whether or not I need to keep a reference to my handler from outside the class.

What I am wondering is, what is the common best practice for this kind of situation? Sorry for wall of text.

3

There are 3 answers

0
Fred Foo On BEST ANSWER

When in doubt, share with a shared_ptr. That's self-documenting and almost always works, at the expense of some pointer indirection and one/two memory allocations.

0
James On

You have a few options.

  • Whoever allocates, deletes. Pass in a pointer/reference, return pointer/reference when detached.
  • Server ownership. Pass in pointer to allocated instance, server is responsible for deleting when detached.
  • Smart Pointer (such as a boost shared_ptr or otherwise). As long as someone has a reference, you need not worry about the memory management (for the most part).

I recently completed such a system and used a smart pointer for it, as it proved to be the easiest and required no extra documentation.

0
Maxim Egorushkin On

In other words, the question is whether the server component owns the application component or the other way around, is that right?

On way to think of it is as of a stack, with application logic layer being on top of the network transport layer. The network layer is a lower level mechanism that does not have knowledge of the application logic and could be used to build several applications with different application logic on top of it, so it would be inconvenient for it to create and own the application layer. Application layer on the other hand, knows which components it needs to implement the required logic. So, it can create and own the network layer.

If you want to separate layers cleanly, or if there is a mismatch between lifetimes of the application layer and the network layer, it makes sense to have a piece of code that creates both components (objects) and binds them together through the interfaces, and this piece of code can own both. Normally, that piece of code is main() function that reads the command line / config file, creates and configures required objects, binds them together and drives the event loop (select/epoll/...).

Going this way, you don't need to concern the callback interfaces with ownership issues and clutter them with smart pointers, just pass a pointer or a reference to the interfaces through callbacks. Just make sure you don't call the callbacks in constructors or destructors of the long lived objects, so that you can build and tear down the call graph in the most convenient order.