5

I have an event driven application. I want to keep the event handler (EventHandler class capable of many/all events) a common implementation - while allowing the EventSource be changeable (specifically - at compile time).

To couple the EventHandler with the EventSource, I will have to store an instance of handler within the EventSource. I tried to store handlers of various forms:

  • pointer to an interface of EventHandler (that has public handler methods defined in concrete EventHandler's
  • instance of std::function - this provided greatest flexibility

However, in both cases, the latency in calling the target method/lambda was quite high (on my test setup about 250ns) - and to worse, was inconsistent. May be due to virtual table and/or heap allocation and/or type erasure ???

In order to reduce this latency, I want to make use of templates.

The best I could come up with is:

template <typename EventHandler>
class EventSource1
{
    EventHandler*   mHandler;
public:
    typedef EventHandler    EventHandlerType;

    void AssignHandler (EventHandler* handler)
    {
        this->mHandler = handler;
    }

    void EventuallyDoCallback (int arbArg)
    {
        this->mHandler->CallbackFunction (arbArg);
    }
};

template <EventSourceType>
class EventSourceTraits
{
    typedef EventSourceType::EventHandlerType   EventHandlerType;
    static void AssignHandler (EventSourceType& source, EventHandlerType* handler)
    {
        source.AssignHandler(handler);
    }
};

class EventHandler
{
public:
    void CallbackFunction (int arg)
    {
        std::cout << "My callback called\n";
    }
};


int main ()
{
    EventSource1<EventHandler> source;                  /// as one can notice, EventSource's need not to know the event handler objects.
    EventHandler handler;

    EventSourceTraits<EventSource1>::AssignHandler (source, &handler);
}

This method impose a restriction that all my EventSource's to be a template classes.

Question is: Is this best way to achieve consistent and low latency to callback? Can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?

skypjack
  • 49,335
  • 19
  • 95
  • 187
rat6
  • 81
  • 9
  • 1
    Have you tried out what the latency is if - just for diagnostic purpose - you call one well known event handler from one well known event source through a statically bound function? Just to find out if the latency is really induced by the call and no other effects apply (like, for example, objects to be instantiated, dispatcher code to evaluate, threads that lock each other out, or what ever)? – Stephan Lechner Feb 07 '17 at 07:03
  • 3
    Try and measure a plain unadorned pointer to a regular non-member function first. Can't outperform that. If you find its performance acceptable, you have a fighting chance. A virtual call should have essentially the same performance as a function pointer though. Have you done any real profiling? – n. m. could be an AI Feb 07 '17 at 07:05
  • @StephanLechner, a well known member function of a well known object takes <30ns consistently. A virtual call takes approximately 60ns but stays consistent. Either of this is acceptable (because they are consistent). I get similar behavior with above stated code with template. p.s. I have not done profiling of any kind. – rat6 Feb 07 '17 at 07:41
  • 1
    Lately i realize, my test method had a problem. The thread I used to compute the latency was doing a File I/O before every iteration. I am not sure how/why it should affect, but when the File IO was replaced with dummy data in memory, the results were consistent even with std::function. – rat6 Feb 07 '17 at 07:53
  • Now that my problem of inconsistent latency seemed to have been resolved, I am wondering if the above stated code can be made better? I am rather interested in avoiding interfaces & class hierarchies. – rat6 Feb 07 '17 at 07:55
  • @rat6 If you want to improve working code you should better ask this question at [SE Code Review](http://codereview.stackexchange.com/). – πάντα ῥεῖ Feb 07 '17 at 09:16
  • 1
    Beyond the review, the code suffers from design. I am curious to learn this way of using templates. – rat6 Feb 07 '17 at 09:25

1 Answers1

2

Is this best way to achieve consistent and low latency to callback?

As suggested in the comments to the question, I'd rather try and measure to know if that's really a problem and what's the best alternative for you.
There doesn't exist the best way, it mostly depends on the actual problem.

can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?

Maybe the following can be a good point from which to start to achieve that:

#include <iostream>

class EventSource1
{
    using invoke_t = void(*)(void *C, int value);

    template<typename T, void(T::*M)(int)>
    static void proto(void *C, int value) {
        (static_cast<T*>(C)->*M)(value);
    }

    invoke_t invoke;
    void *handler;

public:
    template<typename T, void(T::*M)(int) = &T::CallbackFunction>
    void AssignHandler (T* ref)
    {
        invoke = &proto<T, M>;
        handler = ref;
    }

    void EventuallyDoCallback (int arg)
    {
        invoke(handler, arg);
    }
};

class EventHandler
{
public:
    void CallbackFunction (int arg)
    {
        std::cout << "My callback called: " << arg << std::endl;
    }
};

int main ()
{
    EventSource1 source;
    EventHandler handler;

    source.AssignHandler(&handler);
    source.EventuallyDoCallback(42);
}

See it on wandbox.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • 1
    For the low latency program I am writing, i was expecting a solution comparable with the latency of virtual function. Your solution with templates works great for me. Thank You ! – rat6 Feb 07 '17 at 10:37
  • @skypjack this should perform much better than std::function – svoltron Oct 25 '18 at 08:32
  • @svoltron Well, it's not a drop-in replacement for a `std::function`, it has its pros and cons. Btw I've extensively used something similar in a project of mine, [`EnTT`](https://github.com/skypjack/entt) (see class `delegate`) and it worked quite well all the times I've used it in a production environment. – skypjack Oct 25 '18 at 08:35
  • @skypjack that project of yours looks really interesting indeed, I'll look more into it. I'm into performance, I've not measured it, but at first glance, it should behave better than std::function. How about the cons you were speaking about? – svoltron Oct 25 '18 at 08:44
  • 1
    @svoltron ie it hasn't the flexibility of an `std::function`, you cannot attach it a data member, you cannot attach a _compatible function_ and you have to adhere strictly to the signature, and so on. Btw in a high performance environment these are things of which you don't care much and you're usually willing to sacrifice features for performance, so go for it. ;-) – skypjack Oct 25 '18 at 08:47
  • 1
    @svoltron Thank you, you're welcome. Feel free to contact me if you want to discuss it further. – skypjack Oct 25 '18 at 08:54