Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE: Introduce ChainableEventHandler and subclasses #13734

Merged
merged 3 commits into from
Oct 19, 2020

Conversation

AGlass0fMilk
Copy link
Member

@AGlass0fMilk AGlass0fMilk commented Oct 7, 2020

Summary of changes

Requires #13727 to be merged first
Merged #13727 since this PR is closely tied to it.

Introduces a ChainableEventHandler base class that is essentially a singly-linked-list of EventHandlers along with two subclasses:

  • ChainableGattServerEventHandler that enables chaining together GattServer::EventHandlers
  • ChainableGapEventHandler that enables chaining together Gap::EventHandlerss

The ChainableGattServerEventHandler class allows an application to register separate event handlers (eg: for different services that need to handle GattServer events) and then set the global GattServer::setEventHandler to the instance of ChainableGattServerEventHandler with all registered GattServer::EventHandlers.

The ChainableGapEventHandler accomplishes that same as above for Gap:EventHandler implementations.

Common functionality has been split off into ChainableEventHandler.

See #13728 for discussion around this implementation.

Impact of changes

None

Migration actions required

None

Documentation

None

Pull request type

[] Patch update (Bug fix / Target update / Docs update / Test update / Refactor)
[x] Feature update (New feature / Functionality change / New API)
[] Major update (Breaking change E.g. Return code change / API behaviour change)

Test results

[] No Tests required for this change (E.g docs only update)
[x] Covered by existing mbed-os tests (Greentea or Unittest)
[] Tests / results supplied as part of this PR

Reviewers

@pan- @paul-szczepanek-arm The code compiles but my template-class-function-pointer kung fu isn't flawless. Please look over my implementation and point out anything that looks suspect.

I will test this with an example in the near future.


@ciarmcom
Copy link
Member

ciarmcom commented Oct 8, 2020

@AGlass0fMilk, thank you for your changes.
@paul-szczepanek-arm @pan- @ARMmbed/mbed-os-maintainers please review.

@paul-szczepanek-arm
Copy link
Member

That's very nice, we should add a mention in the gattserver class that this exists otherwise people might not even notice. It should be mentioned in the setEventHandler call.

@AGlass0fMilk AGlass0fMilk changed the title [BLE] Introduce ChainableGattServerEventHandler subclass [BLE] Introduce ChainableEventHandler and subclasses Oct 8, 2020
@AGlass0fMilk
Copy link
Member Author

AGlass0fMilk commented Oct 8, 2020

@pan-

I ran into issues compiling the ChainableGapEventHandler due to the cv-ref qualified-ness of those handlers... I introduced a variant of execute_on_all in d6af9e1 that works for now but I think we will need some of @kjbracey-arm 's magic from Callback to remove cv-qualifiers and handle any kind of functions added to event handlers in the future...

eg:

struct EventHandler {
/**
* Called when an advertising device receive a scan response.
*
* @param event Scan request event.
*
* @version: 5+.
*
* @see AdvertisingParameters::setScanRequestNotification().
*/
virtual void onScanRequestReceived(const ScanRequestEvent &event)
{
}
/**
* Called when advertising ends.
*
* Advertising ends when the process timeout or if it is stopped by the
* application or if the local device accepts a connection request.
*
* @param event Advertising end event.
*
* @see startAdvertising()
* @see stopAdvertising()
* @see onConnectionComplete()
*/
virtual void onAdvertisingEnd(const AdvertisingEndEvent &event)
{
}

@pan-
Copy link
Member

pan- commented Oct 8, 2020

@AGlass0fMilk Did you try to use forwarding references (&& in template context) ? It might be good to use std::forward to pass the arguments to the handler too.

@AGlass0fMilk
Copy link
Member Author

@pan- That change seems to work well! Thanks for the suggestion!

pan-
pan- previously requested changes Oct 9, 2020
Copy link
Member

@pan- pan- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great change, I left few comments for improvment.

@mergify mergify bot dismissed pan-’s stale review October 9, 2020 15:26

Pull request has been modified.

@@ -83,6 +83,7 @@ class ChainableGapEventHandler : public ble::Gap::EventHandler,
}

void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event) override {
execute_on_all(&ble::Gap::EventHandler::onDisconnectionComplete, event);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I forgot to comment for that one 😅 .

pan-
pan- previously approved these changes Oct 9, 2020
Copy link
Member

@pan- pan- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@AGlass0fMilk
Copy link
Member Author

@pan- Do you have any good resources where I can learn why your execute_on_all change was needed (and how it works), more about std::forward and related concepts? I need to improve my understanding of lvalues/rvalues/etc.

Any search terms I could use would be appreciated 😄

Currently looking at these:
https://www.artima.com/cppsource/rvalue.html
http://thbecker.net/articles/rvalue_references/section_02.html
https://accu.org/journals/overload/12/61/kilpelainen_227/

@mergify mergify bot dismissed pan-’s stale review October 9, 2020 16:00

Pull request has been modified.

@AGlass0fMilk
Copy link
Member Author

@pan- I decided to merge #13727 into this one since the additional GattServer::EventHandler functions were referenced. It started to get messy to maintain them separately.

Let me know if this is OK.

@pan-
Copy link
Member

pan- commented Oct 9, 2020

@AGlass0fMilk

Why it didn't work: A template function deduces the template types based on the parameter passed in input.

template<typename T>
void foo(T v);

foo('a'); // T is a char
foo(0); // T is an int

In our case, the arguments passed to the function execute_on_all doesn't always match the arguments specified in the handle and that's where the compiler get confused.

template<typename... Args>
void execute_on_all(void (*fn)(Args...), Args... args);

void foo(const int&);
execute_on_all(foo, 0); // Error, Args is deduced as const int& from the function and int from the second parameter.


// using forwarding references
template<typename... Args>
void execute_on_all(void (*fn)(Args...), Args&&... args); 

 void foo(const int&);
execute_on_all(foo, 0); // Error, Args is deduced as const int& from the function and int& from the second parameter.

Having two variadic arguments helps the compiler at recognizing the handler type and the parameter type. If they are not compatible with one another the compiler will complain at the call site.

template<typename... FnArgs, typename... Args>
void execute_on_all(void (*fn)(FnArgs...), Args&&... args); 

One of the most confusing thing about lvalue and rvalue reference to me was the usage of the && token that means different things depending on the context:

void foo(int&& a); // non template context: a is an lvalue reference 

template<typename T>
void foo(T&& a); // template context: a is a forwarding reference, it is an lvalue reference or rvalue reference depending on what is passed in the function.

I recommend reading the C++ proposal papers, they capture very well the reason why these constructs where introduced in the language by detailing the problem they solve:

@AGlass0fMilk
Copy link
Member Author

@pan- Thanks for the detailed explanation!

Let me know if this PR needs any more work.

pan-
pan- previously approved these changes Oct 12, 2020
Copy link
Member

@pan- pan- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mergify mergify bot added the needs: CI label Oct 12, 2020
@mergify mergify bot dismissed pan-’s stale review October 13, 2020 20:06

Pull request has been modified.

@AGlass0fMilk
Copy link
Member Author

@0xc0170 Glad to have you back 😄

I have rebased with master and cleaned up the history of this PR.

Let me know if it's good to go.

@0xc0170
Copy link
Contributor

0xc0170 commented Oct 14, 2020

@AGlass0fMilk the history looks good but feature should not be one liner in the commit message. There are details in this PR that could be also in the commits, please add them there.

…lasses.

Common functionality has been split off into a generic ChainableEventHandler for use by other EventHandler implementations. The ChainableEventHandler is essentially singly-linked list that propagates callbacks to all objects in the list.

The ChainableGattServerEventHandler enables chaining together GattServer::EventHandlers. An application can register separate event handlers (eg: for different services that need to handle GattServer events) and then set the global GattServer::setEventHandler to the instance of ChainableGattServerEventHandler with all registered GattServer::EventHandlers.
The ChainableGapEventHandler enables chaining together Gap::EventHandlers so separate parts of an application can be notified of Gap events as needed. The application can register multiple Gap::EventHandler objects to one ChainableGapEventHandler and then set the global Gap::EventHandler to the ChainableGapEventHandler. All registered EventHandlers will then be called when a Gap Event occurs.
Update parameters passed to onDataSent, onUpdatesEnabled/Disabled, and onConfirmationReceived callbacks.

Deprecate single-callback-registering functions for event handling in lieu of the new EventHandler-based API.

Introduce new GattServer::EventHandler callback functions to replace the deprecated versions.
@AGlass0fMilk
Copy link
Member Author

@0xc0170 I have updated the commit messages. Let me know if they are OK.

@0xc0170
Copy link
Contributor

0xc0170 commented Oct 16, 2020

CI started

@mbed-ci
Copy link

mbed-ci commented Oct 16, 2020

Jenkins CI Test : ❌ FAILED

Build Number: 2 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_build-ARM ✔️
jenkins-ci/mbed-os-ci_build-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_dynamic-memory-usage ✔️
jenkins-ci/mbed-os-ci_cloud-client-pytest
jenkins-ci/mbed-os-ci_greentea-test ✔️

@0xc0170
Copy link
Contributor

0xc0170 commented Oct 17, 2020

CI restarted

@mbed-ci
Copy link

mbed-ci commented Oct 17, 2020

Jenkins CI Test : ✔️ SUCCESS

Build Number: 3 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_build-ARM ✔️
jenkins-ci/mbed-os-ci_build-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_dynamic-memory-usage ✔️
jenkins-ci/mbed-os-ci_greentea-test ✔️
jenkins-ci/mbed-os-ci_cloud-client-pytest ✔️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants