Skip to content
This repository has been archived by the owner on Aug 19, 2021. It is now read-only.

Add the Event class for fine-grain control of event dispatch #15

Merged
merged 3 commits into from
Sep 23, 2016

Conversation

geky
Copy link
Contributor

@geky geky commented Aug 24, 2016

For a more fine-grain control of event dispatch, the Event class can be manually instantiated and configured. An Event represents an event as a C++ style function object and can be directly passed
to other APIs that expect a callback.

From the updated readme:

// Creates an event bound to the specified event queue
EventQueue queue;
Event<void()> event(&queue, doit);

// The event can be manually configured for special timing requirements
// specified in milliseconds
event.delay(10);
event.period(10000);

// Posted events are dispatched in the constext of the queue's
// dispatch function
queue.dispatch();

// Events can also pass arguments to the underlying callback when both
// initially constructed and posted.
Event<void(int, int)> event(&queue, printf, "recieved %d and %d\n");

// Events can be posted multiple times and enqueue gracefully until
// the dispatch function is called.
event.post(1, 2);
event.post(3, 4);
event.post(5, 6);

queue.dispatch();

cc @pan- thoughts?

@pan-
Copy link
Member

pan- commented Aug 24, 2016

Maybe I misunderstand something here but I see two different concepts in the same class:

  • EventBuilder: Construct an event in several steps, the post member function commit it to its event queue. It act like a builder and can be reused to post different events.
  • A C++ function like object which wrap a callback and post this callback into the event queue when called. I don't think this is necessary to have this feature present into the Event class.. callback_func(event) and other variants might be enough (I know a workaround for the return type has to be found....).

Otherwise it is an interesting enhancement.

@geky
Copy link
Contributor Author

geky commented Aug 30, 2016

Yep, I was hoping to kill two birds with one stone.

The goal is to encourage users to move as much code out of interrupt context as possible. Unfortunately, default behaviour is interrupt context, which makes it easy to suddenly find yourself in an interrupt. But we can encourage better behaviour through syntactic sugar.

Currently (with this patch), this is the easiest method to move events out of interrupt context:

Event<> event(queue, func);
ticker.attach(&event, &Event<>::call, 10);

However, this forces the use of method pointers, which can be intimidating to new users. With the operator() overloaded, I would hope to see some syntactic sugar similar to the following emerge in the mbed codebase, allowing deferring from interrupts in a generic manner:

Event<> event(queue, func);
ticker.attach(event, 10);

If we want to avoid this syntax for now, I can split the function-like operations out into a different pr, which can stagnate until such overloads are added to mbed-os.

If we go this route, it would be beneficial to add a post_void sort of member function that works around the return type as you mention. Allowing this as a temporary pattern:

Event<> event(queue, func);
ticker.attach(&event, &Event<>::post_void, 10);

@pan-
Copy link
Member

pan- commented Aug 30, 2016

What about:

operator Callback<void(A0,...)>() { 
  return Callback<void(A0,...)>(this, &Event<A0,...>::post_void);
}

It would still be possible to write:

Event<> event(queue, func);
ticker.attach(event, 10);

but not:

Event<> event(queue, func);
event();

What do you think ?

@geky
Copy link
Contributor Author

geky commented Aug 30, 2016

Hmm, that is a good idea.

My only concern is a running concern I've had with the clarity of ownership. It would be very easy for even an experienced user to take your example and expect the following to work:

EventQueue queue;

void setup() {
    Event<> event(&queue, func);
    ticker.attach(event, 10);
}

int main() {
    setup();
    queue.dispatch();
}

Looking into related concepts, it looks like the operator& is actually overloadable.

As an alternative, what are you thoughts on instead adding a private EventPtr sort of proxy object that is returned from operator& and supports implicit casts to Callback and Event*? The above could be rewritten as follows, making the memory ownership a bit more clear:

EventQueue queue;

void setup() {
    Event<> event(&queue, func);
    ticker.attach(&event, 10);
}

int main() {
    setup();
    queue.dispatch();
}

@pan-
Copy link
Member

pan- commented Aug 30, 2016

My only concern is a running concern I've had with the clarity of ownership. It would be very easy for even an experienced user to take your example and expect the following to work.

100% agreed overloading operator& is much better in that case.

@geky
Copy link
Contributor Author

geky commented Sep 23, 2016

@pan-, I've removed the operator() and related functions, with the intention of considering compatibility with the Callback class in a different pr.

Any concerns with the updated pr?

@geky geky force-pushed the event-class branch 4 times, most recently from 4da36ae to 5f937f1 Compare September 23, 2016 10:52
For a more fine-grain control of event dispatch, the Event class
can be manually instantiated and configured.

More information and examples can be found in the updated readme
and documentation in Event.h
Before:
Event<> e = Event<>(&queue, func);

After:
Event<> e = queue.event(func);

Unfortunately, this technique only works for full bindings since
the event class does not recognize the actual parameter types for
functions. As such, the queue.event function can only return "Event<>".
Before:
Event<A, B, C>

After:
Event<void(A, B, C)>
@geky geky merged commit 1845529 into master Sep 23, 2016
@geky geky deleted the event-class branch September 24, 2016 04:29
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants