Very, very fast event library

Started by
53 comments, last by Iftah 16 years, 7 months ago
So, bonus question: What causes boost::signals to be *that* much slower?
Advertisement
Quote:Putting it in both shouldn't cause an ambiguity. ADL should always pickup the version in the events namespace unless std::swap is explicitly used, even if using namespace std, events or both is in scope.
Huh. Learn something new every day.
Quote:
Regarding the AutoTracked class: I'm not sure that private inheritance is a good idea here. Or, at least, if you use private inheritance, put in a using ConnectionSet::Clear. Or, (and this is really the ickiest of the ideas) add functionality for the AutoTracked class to detect that its been registered with the same Event<> multiple times.
I'm not sure what you mean. Not a good idea as opposed to private composition? I don't want to expose Clear because for anything beyond the simplest case users should be using Register instead of AutoRegister, and because it adds functions to the user's class. As for registering the same event multiple times, it seems like one of those things that users might actually want to do sometime.

Quote:Also, I'd probably move AutoRegister to become a member function of AutoTracked rather than AutoRegister. At time of call of AutoRegister, you need to know three things: The identity of the object, the identity of the member function and the identity of the event. With AutoRegister in the Event<> class, it's possible to mistakenly call AutoRegister with a non-AutoTracked object; with AutoRegister as part of the AutoTracked class it's not. Though I admit that you took the time out to make the conversion error understandable.

I don't like this idea, for a couple of reasons:
* It would split Register functionality across two classes, which seems non-obvious.
* It would clutter up the Observer's interface, probably even its public interface (since there's many situations where an outside class will want to connect the event). This, to me, is the more important of the two. Inheriting from AutoTracked should have no side effects.
Quote:Original post by Spoonbender
So, bonus question: What causes boost::signals to be *that* much slower?

It looks like there are a couple of biggies. First, despite the fact that boost::signals isn't threadsafe, it enters and leaves critical sections a couple of times. Secondly, there's this named-slots thingy which seems to result in overly complex data structures which take a long time to iterate over. And then there's a lot of nickel-and-diming from various housekeeping things it does. Finally, the individual calls have much higher overhead because of boost::bind's runtiminess. It's all functionality that I'm sure someone has a need for, but I don't.
Quote:Original post by Sneftel
I'm not sure what you mean. Not a good idea as opposed to private composition? I don't want to expose Clear because for anything beyond the simplest case users should be using Register instead of AutoRegister, and because it adds functions to the user's class. As for registering the same event multiple times, it seems like one of those things that users might actually want to do sometime.

That's the problem. A user may want to use AutoRegister with the same Event<> more than once. Here's what happens without Clear(): AutoRegister gets called, ConnectionSet now maintains two Connection to the same event, Event<> is still live, so both reference counts are positive. When Event<> is triggered, both delegates are invoked.
Well, right. Isn't that what the user should expect? What alternative effect would it have?
Personally, for something labeled auto, I'd expect that the new delegate would replace the old delegate.

Let's go to a dialog box example. Someone has a open/save dialog that gets used for both saving and loading. User has serializable object with Save and Load members. And first connects the objects Load member to the OnOk event for the dialog. Then later user goes to save and then serializable object then connects Save to the OnOk event.
Ah, I see what you mean.

It's tricky, though, because I can think of situations for which each of the alternative behaviors are correct. One solution would be to have the ConnectionSet detect the duplicated event (the "ickiest" solution you mentioned earlier) and assert out. By disallowing the act completely, the ambiguity is removed.

I don't think that it's necessary to be that draconian, though. The basic idea of AutoTrackable is that the lifetime of connections to an object is the same as that of the object itself. As long as users realize that, they'll at least consider whether re-registering an event does what they think it does.
I suppose it doesn't matter which you choose as long as when you document it, you're up front about the purpose of the class. Though if you can see situations going both ways, you may want to consider having both kinds, either through two separate classes or a template policy parameter. (Or three ways if you include the never double register as a possibility.) The thing is, the way it's doing things now can trivially be done by the user. The "detect if event already registered" version can be done in a couple different ways, but the one requiring the least memory allocation overhead seems to require friend access to the Event and Connections.
Separate classes or template policies (it buuurns us!) would be a reasonable approach. For detection I'm not overly concerned about friendship, but I can't think how I'd do it without significantly penalizing the common case.
Quote:The thing is, the way it's doing things now can trivially be done by the user.

With this system, the user is freed from having to think about Connections at all. There's no question of the Connection ending up in the wrong place, or of weird things happening to its reference count. The user merely needs to say "connect this to that" and housekeeping is invisible.
What I was saying that it's fairly easy for the user to write out:
class AutoTracked : private ConnectionSet {  public:    template<typename T, typename U>    void RegisterMe(T & e, U fn) {      Add(e.Register(this, fn));    }};

This requires no library support and is fairly trivial to see if it works correctly. But it's really a moot point if you decide to support multiple policies.

This topic is closed to new replies.

Advertisement