Peter Dimov wrote:
Jason Winnebeck wrote: There are no inherent cycles so far in the design. A typical Listener interface is
struct Listener { virtual void accept(Event const & event, Generator & sender) = 0; };
so that it can listen to several Generators at a time. The assumption is that Listener::accept cannot destroy 'sender'.
Hmm yes I do like that interface. For some reason when I designed my API over 2 years ago I didn't think to pass the generator into the event. I expected that the listener would hold a strong pointer to its generator anyway. Since I'm breaking backwards compatibility anyway for a redesign, one could argue now is the time to break it more, but I think breaking less is better than breaking more... I would be pretty warm to it, but I want to first hear how you would design the user code. I thought it was very convinent to set a listener and just go about ignoring it forever if you want. If you were to say to just simply create a list of listeners you never access or care about in some list simply to keep them alive, removing listeners when their connections died (you would also have to keep a list of all connections and scan them constantly to check for dead connections), then you have just ended up implementing a garbage collection scheme, and one would have to ask why use shared_ptr to begin with when you have to contain lists of every object in your program and scan them for unused objects. I wanted to move the API to use shared_ptr for that explicit reason so that the user doesn't have to maintain lists of every object they create checking for object death.
Logically, if a Generator does not own its Listeners, it should keep weak pointers. The advantage is that dead Listeners that haven't unregistered can be auto-unsubscribed by the Generator (unless you have real measurements that indicate a performance problem with weak_ptr::lock(), which would surprise me :-).)
It makes real sense when you say it that way. Although my generator classes only allow for a single listener (perhaps a bad idea), but I have thought about changing this, or providing a listener that has a list of listeners it distributes across (an adaptor of sorts). I could implement both the single listener/not passing generator reference scheme, and provide an adaptor class that allows for multiple listeners and passing in a reference to the event source, but as said before, adding complexity like that to the API is generally bad. I am curious about the overhead for weak_ptr's lock. Looking at the code you have to do the normal mutex lock then a try/catch and construct a new shared_ptr and so forth. It's the synchronization cost that I'm unsure about but I would think that the code might be a little expensive? I would say events are occuring constantly, but events occuring every 10-100ms is not constant by any means to a computer, and certainly not a "tight loop" subject to optimization. Thank you for your time, Jason