From 162fb4f75bd041dbc775c6082eb8ddcafc47f68c Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 20 Nov 2015 12:23:02 +0000 Subject: [PATCH 01/15] Add detach operation to CallChainOfFunctionPointersWithContext, Add operator== to functionpointerWithContext class --- ble/CallChainOfFunctionPointersWithContext.h | 28 +++++++++ ble/FunctionPointerWithContext.h | 60 ++++++++++++++++++-- 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 3d5ff7d..59946b8 100644 --- a/ble/CallChainOfFunctionPointersWithContext.h +++ b/ble/CallChainOfFunctionPointersWithContext.h @@ -97,6 +97,34 @@ public: return common_add(new FunctionPointerWithContext(tptr, mptr)); } + /** + * Detach a function pointer from a callchain + * + * @oaram toDetach FunctionPointerWithContext to detach from this callchain + * + * @return true if a function pointer has been detached and false otherwise + */ + void detach(const FunctionPointerWithContext& toDetach) { + pFunctionPointerWithContext_t current = chainHead; + pFunctionPointerWithContext_t previous = NULL; + + while (current) { + if(*current == toDetach) { + if(previous == NULL) { + chainHead = current->getNext(); + } else { + previous->chainAsNext(current->getNext()); + } + delete current; + return true; + } + + previous = current; + current = current->getNext(); + } + return false; + } + /** Clear the call chain (remove all functions in the chain). */ void clear(void) { diff --git a/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index 5fb6e29..79d5594 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -26,6 +26,7 @@ template class FunctionPointerWithContext { public: typedef FunctionPointerWithContext *pFunctionPointerWithContext_t; + typedef const FunctionPointerWithContext *cpFunctionPointerWithContext_t; typedef void (*pvoidfcontext_t)(ContextType context); /** Create a FunctionPointerWithContext, attaching a static function. @@ -33,7 +34,7 @@ public: * @param function The void static function to attach (default is none). */ FunctionPointerWithContext(void (*function)(ContextType context) = NULL) : - _function(NULL), _caller(NULL), _next(NULL) { + _memberFunctionAndPointer(), _caller(NULL), _next(NULL) { attach(function); } @@ -48,6 +49,17 @@ public: attach(object, member); } + FunctionPointerWithContext(const FunctionPointerWithContext& that) : + _memberFunctionAndPointer(that._memberFunctionAndPointer), _caller(that._caller), _next(NULL) { + } + + FunctionPointerWithContext& operator=(const FunctionPointerWithContext& that) { + _memberFunctionAndPointer = that._memberFunctionAndPointer; + _caller = that._caller; + _next = NULL; + return *this; + } + /** Attach a static function. * * @param function The void static function to attach (default is none). @@ -73,6 +85,16 @@ public: * FunctionPointers their callbacks are invoked as well. * @Note: All chained callbacks stack up, so hopefully there won't be too * many FunctionPointers in a chain. */ + void call(ContextType context) const { + _caller(this, context); + + /* Propagate the call to next in the chain. */ + if (_next) { + _next->call(context); + } + } + + /** Same as above, workaround for mbed os FunctionPointer implementation. */ void call(ContextType context) { _caller(this, context); @@ -101,9 +123,18 @@ public: return (pvoidfcontext_t)_function; } + friend bool operator==(const FunctionPointerWithContext& lhs, const FunctionPointerWithContext& rhs) { + return rhs._caller == lhs._caller && + memcmp( + &rhs._memberFunctionAndPointer, + &lhs._memberFunctionAndPointer, + sizeof(rhs._memberFunctionAndPointer) + ) == 0; + } + private: template - static void membercaller(pFunctionPointerWithContext_t self, ContextType context) { + static void membercaller(cpFunctionPointerWithContext_t self, ContextType context) { if (self->_memberFunctionAndPointer._object) { T *o = static_cast(self->_memberFunctionAndPointer._object); void (T::*m)(ContextType); @@ -112,7 +143,7 @@ private: } } - static void functioncaller(pFunctionPointerWithContext_t self, ContextType context) { + static void functioncaller(cpFunctionPointerWithContext_t self, ContextType context) { if (self->_function) { self->_function(context); } @@ -141,10 +172,10 @@ private: * object this pointer and pointer to member - * _memberFunctionAndPointer._object will be NULL if none attached */ - MemberFunctionAndPtr _memberFunctionAndPointer; + mutable MemberFunctionAndPtr _memberFunctionAndPointer; }; - void (*_caller)(FunctionPointerWithContext*, ContextType); + void (*_caller)(const FunctionPointerWithContext*, ContextType); pFunctionPointerWithContext_t _next; /**< Optional link to make a chain out of functionPointers. This * allows chaining function pointers without requiring @@ -152,4 +183,23 @@ private: * 'CallChain' as an alternative. */ }; +/** + * @brief Create a new FunctionPointerWithContext which bind an instance and a + * a member function together. + * @details This little helper is a just here to eliminate the need to write the + * FunctionPointerWithContext type each time you want to create one by kicking + * automatic type deduction of function templates. With this function, it is easy + * to write only one entry point for functions which expect a FunctionPointer + * in parameters. + * + * @param object to bound with member function + * @param member The member function called + * @return a new FunctionPointerWithContext + */ +template +FunctionPointerWithContext makeFunctionPointer(T *object, void (T::*member)(ContextType context)) +{ + return FunctionPointerWithContext(object, member); +} + #endif // ifndef MBED_FUNCTIONPOINTER_WITH_CONTEXT_H From dfc03ce0e68b8fdf3aa2d9b1846de1371e04aeaa Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 20 Nov 2015 12:56:39 +0000 Subject: [PATCH 02/15] Convert Gap::timeout callback to a callback chain. Add 'boolean' conversion and function operator syntax to CallChainOfFunctionPointerWithContext --- ble/CallChainOfFunctionPointersWithContext.h | 31 ++++++++++++++++++++ ble/FunctionPointerWithContext.h | 7 +++++ ble/Gap.h | 19 ++++++++---- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 59946b8..213414a 100644 --- a/ble/CallChainOfFunctionPointersWithContext.h +++ b/ble/CallChainOfFunctionPointersWithContext.h @@ -97,6 +97,14 @@ public: return common_add(new FunctionPointerWithContext(tptr, mptr)); } + /** Add a function at the front of the chain. + * + * @param func The FunctionPointerWithContext to add. + */ + void add(const FunctionPointerWithContext& func) { + common_add(new FunctionPointerWithContext(func)); + } + /** * Detach a function pointer from a callchain * @@ -153,6 +161,29 @@ public: } } + /** + * @brief same as above but const + */ + void call(ContextType context) const { + if (chainHead) { + chainHead->call(context); + } + } + + /** + * @brief same as above but with function call operator + */ + void operator()(ContextType context) const { + call(context); + } + + typedef void (CallChainOfFunctionPointersWithContext::*bool_type)() const; + void True() const {} + + operator bool_type() const { + return chainHead == NULL ? 0 : &CallChainOfFunctionPointersWithContext::True; + } + private: pFunctionPointerWithContext_t common_add(pFunctionPointerWithContext_t pf) { if (chainHead == NULL) { diff --git a/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index 79d5594..c2dbca0 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -94,6 +94,13 @@ public: } } + /** + * @brief Same as above + */ + void operator()(ContextType context) const { + call(context); + } + /** Same as above, workaround for mbed os FunctionPointer implementation. */ void call(ContextType context) { _caller(this, context); diff --git a/ble/Gap.h b/ble/Gap.h index c87c6e3..5f2e3ce 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -140,8 +140,9 @@ public: return (durationInMillis * 1000) / UNIT_1_25_MS; } + typedef FunctionPointerWithContext TimeoutEventCallback_t; + typedef CallChainOfFunctionPointersWithContext TimeoutEventCallbackChain_t; - typedef void (*TimeoutEventCallback_t)(TimeoutSource_t source); typedef void (*ConnectionEventCallback_t)(const ConnectionCallbackParams_t *params); typedef void (*DisconnectionEventCallback_t)(const DisconnectionCallbackParams_t *params); typedef FunctionPointerWithContext RadioNotificationEventCallback_t; @@ -893,7 +894,13 @@ public: * Set up a callback for timeout events. Refer to TimeoutSource_t for * possible event types. */ - void onTimeout(TimeoutEventCallback_t callback) {timeoutCallback = callback;} + void onTimeout(TimeoutEventCallback_t callback) { + timeoutCallbackChain.add(callback); + } + + TimeoutEventCallbackChain_t& onTimeout() { + return timeoutCallbackChain; + } /** * Append to a chain of callbacks to be invoked upon GAP connection. @@ -956,7 +963,7 @@ protected: _scanResponse(), state(), scanningActive(false), - timeoutCallback(NULL), + timeoutCallbackChain(), radioNotificationCallback(), onAdvertisementReport(), connectionCallChain(), @@ -1002,8 +1009,8 @@ public: } void processTimeoutEvent(TimeoutSource_t source) { - if (timeoutCallback) { - timeoutCallback(source); + if (timeoutCallbackChain) { + timeoutCallbackChain(source); } } @@ -1017,7 +1024,7 @@ protected: bool scanningActive; protected: - TimeoutEventCallback_t timeoutCallback; + TimeoutEventCallbackChain_t timeoutCallbackChain; RadioNotificationEventCallback_t radioNotificationCallback; AdvertisementReportCallback_t onAdvertisementReport; CallChainOfFunctionPointersWithContext connectionCallChain; From b9cde8590caf9776d74cce6c84f695742ea74594 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 20 Nov 2015 13:04:26 +0000 Subject: [PATCH 03/15] Improve management of onConnection callback chain --- ble/Gap.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ble/Gap.h b/ble/Gap.h index 5f2e3ce..4d2c1f8 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -143,7 +143,9 @@ public: typedef FunctionPointerWithContext TimeoutEventCallback_t; typedef CallChainOfFunctionPointersWithContext TimeoutEventCallbackChain_t; - typedef void (*ConnectionEventCallback_t)(const ConnectionCallbackParams_t *params); + typedef FunctionPointerWithContext ConnectionEventCallback_t; + typedef CallChainOfFunctionPointersWithContext ConnectionEventCallbackChain_t; + typedef void (*DisconnectionEventCallback_t)(const DisconnectionCallbackParams_t *params); typedef FunctionPointerWithContext RadioNotificationEventCallback_t; @@ -910,6 +912,10 @@ public: template void onConnection(T *tptr, void (T::*mptr)(const ConnectionCallbackParams_t*)) {connectionCallChain.add(tptr, mptr);} + ConnectionEventCallbackChain_t& onconnection() { + return connectionCallChain; + } + /** * Append to a chain of callbacks to be invoked upon GAP disconnection. */ @@ -1027,7 +1033,7 @@ protected: TimeoutEventCallbackChain_t timeoutCallbackChain; RadioNotificationEventCallback_t radioNotificationCallback; AdvertisementReportCallback_t onAdvertisementReport; - CallChainOfFunctionPointersWithContext connectionCallChain; + ConnectionEventCallbackChain_t connectionCallChain; CallChainOfFunctionPointersWithContext disconnectionCallChain; private: From 0383194a59c20b75d30dd84324ecd1f5d7923677 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 20 Nov 2015 13:09:24 +0000 Subject: [PATCH 04/15] Add DisconnectionEventCAllbackChain accessor --- ble/Gap.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ble/Gap.h b/ble/Gap.h index 4d2c1f8..c941bae 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -146,7 +146,9 @@ public: typedef FunctionPointerWithContext ConnectionEventCallback_t; typedef CallChainOfFunctionPointersWithContext ConnectionEventCallbackChain_t; - typedef void (*DisconnectionEventCallback_t)(const DisconnectionCallbackParams_t *params); + typedef FunctionPointerWithContext DisconnectionEventCallback_t; + typedef CallChainOfFunctionPointersWithContext DisconnectionEventCallbackChain_t; + typedef FunctionPointerWithContext RadioNotificationEventCallback_t; /* @@ -924,6 +926,10 @@ public: template void onDisconnection(T *tptr, void (T::*mptr)(const DisconnectionCallbackParams_t*)) {disconnectionCallChain.add(tptr, mptr);} + DisconnectionEventCallbackChain_t& onDisconnection() { + return disconnectionCallChain; + } + /** * Set the application callback for radio-notification events. * @@ -1034,7 +1040,7 @@ protected: RadioNotificationEventCallback_t radioNotificationCallback; AdvertisementReportCallback_t onAdvertisementReport; ConnectionEventCallbackChain_t connectionCallChain; - CallChainOfFunctionPointersWithContext disconnectionCallChain; + DisconnectionEventCallbackChain_t disconnectionCallChain; private: /* Disallow copy and assignment. */ From 5908e3600da66ef26f55916ec6385e02e47021b0 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Fri, 20 Nov 2015 13:17:00 +0000 Subject: [PATCH 05/15] Alignement and comment --- ble/Gap.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ble/Gap.h b/ble/Gap.h index c941bae..c3b318a 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -959,11 +959,14 @@ public: */ void onRadioNotification(void (*callback)(bool param)) { radioNotificationCallback.attach(callback); + // why does it start radio notification ? It is not even indicated in the + // doc that it start the listening process initRadioNotification(); } template void onRadioNotification(T *tptr, void (T::*mptr)(bool)) { radioNotificationCallback.attach(tptr, mptr); + // why does it start radio notification ? initRadioNotification(); } @@ -1036,10 +1039,10 @@ protected: bool scanningActive; protected: - TimeoutEventCallbackChain_t timeoutCallbackChain; - RadioNotificationEventCallback_t radioNotificationCallback; - AdvertisementReportCallback_t onAdvertisementReport; - ConnectionEventCallbackChain_t connectionCallChain; + TimeoutEventCallbackChain_t timeoutCallbackChain; + RadioNotificationEventCallback_t radioNotificationCallback; + AdvertisementReportCallback_t onAdvertisementReport; + ConnectionEventCallbackChain_t connectionCallChain; DisconnectionEventCallbackChain_t disconnectionCallChain; private: From 57e69cb0d02e35c87449266c286e9445df0ce3e0 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 20:03:03 +0000 Subject: [PATCH 06/15] Add safe bool conversion to ninic function pointer semantic --- ble/FunctionPointerWithContext.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index c2dbca0..f1edac6 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -111,6 +111,19 @@ public: } } + typedef void (FunctionPointerWithContext::*bool_type)() const; + + /** + * implementation of safe bool operator + */ + operator bool_type() const { + if(_function || _memberFunctionAndPointer._object) { + return &FunctionPointerWithContext::trueValue; + } + + return 0; + } + /** * Set up an external FunctionPointer as a next in the chain of related * callbacks. Invoking call() on the head FunctionPointer will invoke all @@ -156,6 +169,12 @@ private: } } + /** + * @brief True value used in conversion to bool, this function is useless + * beside this usage + */ + void trueValue() const {} + struct MemberFunctionAndPtr { /* * Forward declaration of a class and a member function to this class. From acf4467c44addbb19f682d231fd202dd62ebd8d9 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 20:04:08 +0000 Subject: [PATCH 07/15] Replace callbacks with function pointer into FunctionPointer object --- ble/ServiceDiscovery.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ble/ServiceDiscovery.h b/ble/ServiceDiscovery.h index 937b4a3..595bc20 100644 --- a/ble/ServiceDiscovery.h +++ b/ble/ServiceDiscovery.h @@ -38,7 +38,7 @@ public: * framework. The application can safely make a persistent shallow-copy of * this object to work with the service beyond the callback. */ - typedef void (*ServiceCallback_t)(const DiscoveredService *); + typedef FunctionPointerWithContext ServiceCallback_t; /** * Callback type for when a matching characteristic is found during service- @@ -48,12 +48,12 @@ public: * framework. The application can safely make a persistent shallow-copy of * this object to work with the characteristic beyond the callback. */ - typedef void (*CharacteristicCallback_t)(const DiscoveredCharacteristic *); + typedef FunctionPointerWithContext CharacteristicCallback_t; /** * Callback type for when serviceDiscovery terminates. */ - typedef void (*TerminationCallback_t)(Gap::Handle_t connectionHandle); + typedef FunctionPointerWithContext TerminationCallback_t; public: /** From 57ad5334861c4f5d8420e886ec8d01a030640801 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 20:05:20 +0000 Subject: [PATCH 08/15] Add typedef for FunctionPointerWithContext and CallChains. Provide access to Callchains --- ble/GattServer.h | 58 +++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/ble/GattServer.h b/ble/GattServer.h index 57e8649..a71f63b 100644 --- a/ble/GattServer.h +++ b/ble/GattServer.h @@ -26,9 +26,18 @@ class GattServer { public: + /* Event callback handlers. */ - typedef void (*EventCallback_t)(GattAttribute::Handle_t attributeHandle); - typedef void (*ServerEventCallback_t)(void); /**< Independent of any particular attribute. */ + typedef FunctionPointerWithContext DataSentCallback_t; + typedef CallChainOfFunctionPointersWithContext DataSentCallbackChain_t; + + typedef FunctionPointerWithContext DataWrittenCallback_t; + typedef CallChainOfFunctionPointersWithContext DataWrittenCallbackChain_t; + + typedef FunctionPointerWithContext DataReadCallback_t; + typedef CallChainOfFunctionPointersWithContext DataReadCallbackChain_t; + + typedef FunctionPointerWithContext EventCallback_t; protected: GattServer() : @@ -238,12 +247,19 @@ public: * @Note: It is also possible to set up a callback into a member function of * some object. */ - void onDataSent(void (*callback)(unsigned count)) {dataSentCallChain.add(callback);} + void onDataSent(const DataSentCallback_t& callback) {dataSentCallChain.add(callback);} template void onDataSent(T *objPtr, void (T::*memberPtr)(unsigned count)) { dataSentCallChain.add(objPtr, memberPtr); } + /** + * @brief get the callback chain called when the event DATA_EVENT is triggered. + */ + DataSentCallbackChain_t& onDataSent() { + return dataSentCallChain; + } + /** * Set up a callback for when an attribute has its value updated by or at the * connected peer. For a peripheral, this callback is triggered when the local @@ -259,12 +275,16 @@ public: * @Note: It is also possible to set up a callback into a member function of * some object. */ - void onDataWritten(void (*callback)(const GattWriteCallbackParams *eventDataP)) {dataWrittenCallChain.add(callback);} + void onDataWritten(const DataWrittenCallback_t& callback) {dataWrittenCallChain.add(callback);} template void onDataWritten(T *objPtr, void (T::*memberPtr)(const GattWriteCallbackParams *context)) { dataWrittenCallChain.add(objPtr, memberPtr); } + DataWrittenCallbackChain_t& onDataWritten() { + return dataWrittenCallChain; + } + /** * Setup a callback to be invoked on the peripheral when an attribute is * being read by a remote client. @@ -284,7 +304,7 @@ public: * @return BLE_ERROR_NOT_IMPLEMENTED if this functionality isn't available; * else BLE_ERROR_NONE. */ - ble_error_t onDataRead(void (*callback)(const GattReadCallbackParams *eventDataP)) { + ble_error_t onDataRead(const DataReadCallback_t& callback) { if (!isOnDataReadAvailable()) { return BLE_ERROR_NOT_IMPLEMENTED; } @@ -302,6 +322,10 @@ public: return BLE_ERROR_NONE; } + DataReadCallbackChain_t& onDataRead() { + return dataReadCallChain; + } + /** * Set up a callback for when notifications or indications are enabled for a * characteristic on the local GATT server. @@ -323,15 +347,11 @@ public: /* Entry points for the underlying stack to report events back to the user. */ protected: void handleDataWrittenEvent(const GattWriteCallbackParams *params) { - if (dataWrittenCallChain.hasCallbacksAttached()) { - dataWrittenCallChain.call(params); - } + dataWrittenCallChain.call(params); } void handleDataReadEvent(const GattReadCallbackParams *params) { - if (dataReadCallChain.hasCallbacksAttached()) { - dataReadCallChain.call(params); - } + dataReadCallChain.call(params); } void handleEvent(GattServerEvents::gattEvent_e type, GattAttribute::Handle_t attributeHandle) { @@ -357,9 +377,7 @@ protected: } void handleDataSentEvent(unsigned count) { - if (dataSentCallChain.hasCallbacksAttached()) { - dataSentCallChain.call(count); - } + dataSentCallChain.call(count); } protected: @@ -367,12 +385,12 @@ protected: uint8_t characteristicCount; private: - CallChainOfFunctionPointersWithContext dataSentCallChain; - CallChainOfFunctionPointersWithContext dataWrittenCallChain; - CallChainOfFunctionPointersWithContext dataReadCallChain; - EventCallback_t updatesEnabledCallback; - EventCallback_t updatesDisabledCallback; - EventCallback_t confirmationReceivedCallback; + DataSentCallbackChain_t dataSentCallChain; + DataWrittenCallbackChain_t dataWrittenCallChain; + DataReadCallbackChain_t dataReadCallChain; + EventCallback_t updatesEnabledCallback; + EventCallback_t updatesDisabledCallback; + EventCallback_t confirmationReceivedCallback; private: /* Disallow copy and assignment. */ From 24377826364019f8189b3712215b2aeeed30636d Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 21:14:12 +0000 Subject: [PATCH 09/15] Replace simple callbacks by callchains --- ble/GattClient.h | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/ble/GattClient.h b/ble/GattClient.h index b814ede..343b5e9 100644 --- a/ble/GattClient.h +++ b/ble/GattClient.h @@ -23,18 +23,23 @@ #include "GattCallbackParamTypes.h" +#include "CallChainOfFunctionPointersWithContext.h" + class GattClient { public: - typedef void (*ReadCallback_t)(const GattReadCallbackParams *params); + typedef FunctionPointerWithContext ReadCallback_t; + typedef CallChainOfFunctionPointersWithContext ReadCallbackChain_t; enum WriteOp_t { GATT_OP_WRITE_REQ = 0x01, /**< Write request. */ GATT_OP_WRITE_CMD = 0x02, /**< Write command. */ }; - typedef void (*WriteCallback_t)(const GattWriteCallbackParams *params); + typedef FunctionPointerWithContext WriteCallback_t; + typedef CallChainOfFunctionPointersWithContext WriteCallbackChain_t; - typedef void (*HVXCallback_t)(const GattHVXCallbackParams *params); + typedef FunctionPointerWithContext HVXCallback_t; + typedef CallChainOfFunctionPointersWithContext HVXCallbackChain_t; /* * The following functions are meant to be overridden in the platform-specific sub-class. @@ -244,7 +249,11 @@ public: * Set up a callback for read response events. */ void onDataRead(ReadCallback_t callback) { - onDataReadCallback = callback; + onDataReadCallbackChain.add(callback); + } + + ReadCallbackChain_t& onDataRead() { + return onDataReadCallbackChain; } /** @@ -252,7 +261,11 @@ public: * @Note: Write commands (issued using writeWoResponse) don't generate a response. */ void onDataWritten(WriteCallback_t callback) { - onDataWriteCallback = callback; + onDataWriteCallbackChain.add(callback); + } + + WriteCallbackChain_t& onDataWritten() { + return onDataWriteCallbackChain; } /** @@ -281,7 +294,11 @@ public: * GATT server. */ void onHVX(HVXCallback_t callback) { - onHVXCallback = callback; + onHVXCallbackChain.add(callback); + } + + HVXCallbackChain_t& onHVX() { + return onHVXCallbackChain; } protected: @@ -292,27 +309,23 @@ protected: /* Entry points for the underlying stack to report events back to the user. */ public: void processReadResponse(const GattReadCallbackParams *params) { - if (onDataReadCallback) { - onDataReadCallback(params); - } + onDataReadCallbackChain(params); } void processWriteResponse(const GattWriteCallbackParams *params) { - if (onDataWriteCallback) { - onDataWriteCallback(params); - } + onDataWriteCallbackChain(params); } void processHVXEvent(const GattHVXCallbackParams *params) { - if (onHVXCallback) { - onHVXCallback(params); + if (onHVXCallbackChain) { + onHVXCallbackChain(params); } } protected: - ReadCallback_t onDataReadCallback; - WriteCallback_t onDataWriteCallback; - HVXCallback_t onHVXCallback; + ReadCallbackChain_t onDataReadCallbackChain; + WriteCallbackChain_t onDataWriteCallbackChain; + HVXCallbackChain_t onHVXCallbackChain; private: /* Disallow copy and assignment. */ From 693a563af765b4311bce5944f032608c82daf1bf Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 21:14:51 +0000 Subject: [PATCH 10/15] Fix invalid return value --- ble/CallChainOfFunctionPointersWithContext.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 213414a..40b0910 100644 --- a/ble/CallChainOfFunctionPointersWithContext.h +++ b/ble/CallChainOfFunctionPointersWithContext.h @@ -124,13 +124,12 @@ public: previous->chainAsNext(current->getNext()); } delete current; - return true; + return; } previous = current; current = current->getNext(); } - return false; } /** Clear the call chain (remove all functions in the chain). From ebb982f7dc2d1599649d6f89b9a75fd0e98fe58e Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 21:15:06 +0000 Subject: [PATCH 11/15] Add read end to end operation --- ble/DiscoveredCharacteristic.h | 2 ++ source/DiscoveredCharacteristic.cpp | 41 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/ble/DiscoveredCharacteristic.h b/ble/DiscoveredCharacteristic.h index 1ff67b8..62f7c7a 100644 --- a/ble/DiscoveredCharacteristic.h +++ b/ble/DiscoveredCharacteristic.h @@ -83,6 +83,8 @@ public: */ ble_error_t read(uint16_t offset = 0) const; + ble_error_t read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const; + /** * Perform a write without response procedure. * diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index 5d0133d..b090dd0 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -31,6 +31,47 @@ DiscoveredCharacteristic::read(uint16_t offset) const return gattc->read(connHandle, valueHandle, offset); } + +struct OneShotReadCallback { + static void launch(GattClient* client, const GattClient::ReadCallback_t& cb) { + OneShotReadCallback* oneShot = new OneShotReadCallback(client, cb); + oneShot->attach(); + // delete will be made when this callback is called + } + +private: + OneShotReadCallback(GattClient* client, const GattClient::ReadCallback_t& cb) : + _client(client), + _callback(cb) { } + + void attach() { + _client->onDataRead(makeFunctionPointer(this, &OneShotReadCallback::call)); + } + + void call(const GattReadCallbackParams* params) { + _callback(params); + _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); + delete this; + } + + GattClient* _client; + GattClient::ReadCallback_t _callback; +}; + +ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const { + ble_error_t error = read(offset); + if(error) { + return error; + } + + OneShotReadCallback::launch(gattc, onRead); + + return error; +} + + + + ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value) const { From 00cabcefea4e4f322f7393c9c175a89dacf52e81 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Sun, 22 Nov 2015 21:22:59 +0000 Subject: [PATCH 12/15] Verify ReadResponse identity --- source/DiscoveredCharacteristic.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index b090dd0..5d7e777 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -33,28 +33,37 @@ DiscoveredCharacteristic::read(uint16_t offset) const struct OneShotReadCallback { - static void launch(GattClient* client, const GattClient::ReadCallback_t& cb) { - OneShotReadCallback* oneShot = new OneShotReadCallback(client, cb); + static void launch(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle,const GattClient::ReadCallback_t& cb) { + OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); oneShot->attach(); // delete will be made when this callback is called } private: - OneShotReadCallback(GattClient* client, const GattClient::ReadCallback_t& cb) : + OneShotReadCallback(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) : _client(client), + _connHandle(connHandle), + _handle(handle), _callback(cb) { } void attach() { _client->onDataRead(makeFunctionPointer(this, &OneShotReadCallback::call)); } - void call(const GattReadCallbackParams* params) { - _callback(params); - _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); - delete this; + void call(const GattReadCallbackParams* params) { + // verifiy that it is the right characteristic on the right connection + if(params->connHandle == _connHandle && params->handle == _handle) { + _callback(params); + _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); + delete this; + } } GattClient* _client; + Gap::Handle_t _connHandle; + GattAttribute::Handle_t _handle; GattClient::ReadCallback_t _callback; }; @@ -64,7 +73,7 @@ ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::Re return error; } - OneShotReadCallback::launch(gattc, onRead); + OneShotReadCallback::launch(gattc, connHandle, valueHandle, onRead); return error; } From 381345fe65b821f83266441de5bfed2fb2772ace Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Mon, 23 Nov 2015 10:54:25 +0000 Subject: [PATCH 13/15] Allow user of GattClient to call write on a discovered characteristic with the continuation function --- ble/DiscoveredCharacteristic.h | 5 +++ source/DiscoveredCharacteristic.cpp | 56 +++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/ble/DiscoveredCharacteristic.h b/ble/DiscoveredCharacteristic.h index 62f7c7a..583a8de 100644 --- a/ble/DiscoveredCharacteristic.h +++ b/ble/DiscoveredCharacteristic.h @@ -137,6 +137,11 @@ public: */ ble_error_t write(uint16_t length, const uint8_t *value) const; + /** + * Same as above but register the callback wich will be called once the data has been written + */ + ble_error_t write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& onRead) const; + void setupLongUUID(UUID::LongUUIDBytes_t longUUID) { uuid.setupLong(longUUID); } diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index 5d7e777..b037c13 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -31,7 +31,6 @@ DiscoveredCharacteristic::read(uint16_t offset) const return gattc->read(connHandle, valueHandle, offset); } - struct OneShotReadCallback { static void launch(GattClient* client, Gap::Handle_t connHandle, GattAttribute::Handle_t handle,const GattClient::ReadCallback_t& cb) { @@ -45,7 +44,7 @@ private: GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) : _client(client), _connHandle(connHandle), - _handle(handle), + _handle(handle), _callback(cb) { } void attach() { @@ -54,7 +53,7 @@ private: void call(const GattReadCallbackParams* params) { // verifiy that it is the right characteristic on the right connection - if(params->connHandle == _connHandle && params->handle == _handle) { + if (params->connHandle == _connHandle && params->handle == _handle) { _callback(params); _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); delete this; @@ -69,7 +68,7 @@ private: ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const { ble_error_t error = read(offset); - if(error) { + if (error) { return error; } @@ -78,9 +77,6 @@ ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::Re return error; } - - - ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value) const { @@ -109,6 +105,52 @@ DiscoveredCharacteristic::writeWoResponse(uint16_t length, const uint8_t *value) return gattc->write(GattClient::GATT_OP_WRITE_CMD, connHandle, valueHandle, length, value); } +struct OneShotWriteCallback { + static void launch(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle,const GattClient::WriteCallback_t& cb) { + OneShotWriteCallback* oneShot = new OneShotWriteCallback(client, connHandle, handle, cb); + oneShot->attach(); + // delete will be made when this callback is called + } + +private: + OneShotWriteCallback(GattClient* client, Gap::Handle_t connHandle, + GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) : + _client(client), + _connHandle(connHandle), + _handle(handle), + _callback(cb) { } + + void attach() { + _client->onDataWritten(makeFunctionPointer(this, &OneShotWriteCallback::call)); + } + + void call(const GattWriteCallbackParams* params) { + // verifiy that it is the right characteristic on the right connection + if (params->connHandle == _connHandle && params->handle == _handle) { + _callback(params); + _client->onDataWritten().detach(makeFunctionPointer(this, &OneShotWriteCallback::call)); + delete this; + } + } + + GattClient* _client; + Gap::Handle_t _connHandle; + GattAttribute::Handle_t _handle; + GattClient::WriteCallback_t _callback; +}; + +ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& onRead) const { + ble_error_t error = write(length, value); + if (error) { + return error; + } + + OneShotWriteCallback::launch(gattc, connHandle, valueHandle, onRead); + + return error; +} + ble_error_t DiscoveredCharacteristic::discoverDescriptors(DescriptorCallback_t callback, const UUID &matchingUUID) const { From 38e27c4a0aa6f444f7a447adf50d7f649eaea620 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Mon, 23 Nov 2015 11:27:08 +0000 Subject: [PATCH 14/15] FunctionPointer call is not propagated through FunctionPointer call but it is a mechanism internal to CallChain object. This change allow to safelly remove a callback in a callchain while calling it. It also clean responsabilities and reduce coupling. --- ble/CallChainOfFunctionPointersWithContext.h | 26 +++++++++++++++----- ble/FunctionPointerWithContext.h | 5 ---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 40b0910..761c651 100644 --- a/ble/CallChainOfFunctionPointersWithContext.h +++ b/ble/CallChainOfFunctionPointersWithContext.h @@ -118,9 +118,15 @@ public: while (current) { if(*current == toDetach) { - if(previous == NULL) { + if(previous == NULL) { + if(currentCalled == current) { + currentCalled = NULL; + } chainHead = current->getNext(); } else { + if(currentCalled == current) { + currentCalled = previous; + } previous->chainAsNext(current->getNext()); } delete current; @@ -155,17 +161,23 @@ public: * chained FunctionPointers. */ void call(ContextType context) { - if (chainHead) { - chainHead->call(context); - } + ((const CallChainOfFunctionPointersWithContext*) this)->call(context); } /** * @brief same as above but const */ void call(ContextType context) const { - if (chainHead) { - chainHead->call(context); + currentCalled = chainHead; + + while(currentCalled) { + currentCalled->call(context); + // if this was the head and the call removed the head + if(currentCalled == NULL) { + currentCalled = chainHead; + } else { + currentCalled = currentCalled->getNext(); + } } } @@ -197,6 +209,8 @@ private: private: pFunctionPointerWithContext_t chainHead; + mutable pFunctionPointerWithContext_t currentCalled; + /* Disallow copy constructor and assignment operators. */ private: diff --git a/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index f1edac6..6e60a44 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -104,11 +104,6 @@ public: /** Same as above, workaround for mbed os FunctionPointer implementation. */ void call(ContextType context) { _caller(this, context); - - /* Propagate the call to next in the chain. */ - if (_next) { - _next->call(context); - } } typedef void (FunctionPointerWithContext::*bool_type)() const; From 184d29c35bfe8db868f9ff3f0e3832d7af8da089 Mon Sep 17 00:00:00 2001 From: Vincent Coubard Date: Wed, 25 Nov 2015 11:27:33 +0000 Subject: [PATCH 15/15] Various enhancement: Add SafeBool class which allow to easily declare a safe bool operator in c++03. CallChainOfFunctionPointerswithContext: - unify syntax of add - detach function now return true if a function has been detached and false otherwise - Explanations about function call operator - use safe bool idiom - explanations about iterator and why it is mutable FunctionPointerWithContext: - fix call propagation - use safe bool idiom Gap: - add documentation - onRadioNotification does mot call initRadioNotification anymore GattClient: - documentation GattServer: - documentation --- ble/CallChainOfFunctionPointersWithContext.h | 43 ++++--- ble/FunctionPointerWithContext.h | 24 +--- ble/Gap.h | 26 ++++- ble/GattClient.h | 26 ++++- ble/GattServer.h | 16 +++ ble/SafeBool.h | 114 +++++++++++++++++++ source/DiscoveredCharacteristic.cpp | 4 +- 7 files changed, 213 insertions(+), 40 deletions(-) create mode 100644 ble/SafeBool.h diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 761c651..fcda1c5 100644 --- a/ble/CallChainOfFunctionPointersWithContext.h +++ b/ble/CallChainOfFunctionPointersWithContext.h @@ -18,6 +18,7 @@ #include #include "FunctionPointerWithContext.h" +#include "SafeBool.h" /** Group one or more functions in an instance of a CallChainOfFunctionPointersWithContext, then call them in @@ -56,7 +57,7 @@ */ template -class CallChainOfFunctionPointersWithContext { +class CallChainOfFunctionPointersWithContext : public SafeBool > { public: typedef FunctionPointerWithContext *pFunctionPointerWithContext_t; @@ -101,8 +102,8 @@ public: * * @param func The FunctionPointerWithContext to add. */ - void add(const FunctionPointerWithContext& func) { - common_add(new FunctionPointerWithContext(func)); + pFunctionPointerWithContext_t add(const FunctionPointerWithContext& func) { + return common_add(new FunctionPointerWithContext(func)); } /** @@ -112,7 +113,7 @@ public: * * @return true if a function pointer has been detached and false otherwise */ - void detach(const FunctionPointerWithContext& toDetach) { + bool detach(const FunctionPointerWithContext& toDetach) { pFunctionPointerWithContext_t current = chainHead; pFunctionPointerWithContext_t previous = NULL; @@ -130,12 +131,14 @@ public: previous->chainAsNext(current->getNext()); } delete current; - return; + return true; } previous = current; current = current->getNext(); } + + return false; } /** Clear the call chain (remove all functions in the chain). @@ -156,9 +159,6 @@ public: } /** Call all the functions in the chain in sequence - * @Note: The stack frames of all the callbacks within the chained - * FunctionPointers will stack up. Hopefully there won't be too many - * chained FunctionPointers. */ void call(ContextType context) { ((const CallChainOfFunctionPointersWithContext*) this)->call(context); @@ -183,16 +183,30 @@ public: /** * @brief same as above but with function call operator + * \code + * + * void first(bool); + * void second(bool); + * + * CallChainOfFunctionPointerWithContext foo; + * + * foo.attach(first); + * foo.attach(second); + * + * // call the callchain like a function + * foo(true); + * + * \endcode */ void operator()(ContextType context) const { call(context); } - typedef void (CallChainOfFunctionPointersWithContext::*bool_type)() const; - void True() const {} - - operator bool_type() const { - return chainHead == NULL ? 0 : &CallChainOfFunctionPointersWithContext::True; + /** + * @brief bool conversion operation + */ + bool toBool() const { + return chainHead != NULL; } private: @@ -209,6 +223,9 @@ private: private: pFunctionPointerWithContext_t chainHead; + // iterator during a function call, this has to be mutable because the call function is const. + // Note: mutable is the correct behaviour here, the iterator never leak outside the object. + // So the object can still be seen as logically const even if it change its internal state mutable pFunctionPointerWithContext_t currentCalled; diff --git a/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index 6e60a44..02b07a6 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -18,12 +18,13 @@ #define MBED_FUNCTIONPOINTER_WITH_CONTEXT_H #include +#include "SafeBool.h" /** A class for storing and calling a pointer to a static or member void function * that takes a context. */ template -class FunctionPointerWithContext { +class FunctionPointerWithContext : public SafeBool > { public: typedef FunctionPointerWithContext *pFunctionPointerWithContext_t; typedef const FunctionPointerWithContext *cpFunctionPointerWithContext_t; @@ -87,11 +88,6 @@ public: * many FunctionPointers in a chain. */ void call(ContextType context) const { _caller(this, context); - - /* Propagate the call to next in the chain. */ - if (_next) { - _next->call(context); - } } /** @@ -103,7 +99,7 @@ public: /** Same as above, workaround for mbed os FunctionPointer implementation. */ void call(ContextType context) { - _caller(this, context); + ((const FunctionPointerWithContext*) this)->call(context); } typedef void (FunctionPointerWithContext::*bool_type)() const; @@ -111,12 +107,8 @@ public: /** * implementation of safe bool operator */ - operator bool_type() const { - if(_function || _memberFunctionAndPointer._object) { - return &FunctionPointerWithContext::trueValue; - } - - return 0; + bool toBool() const { + return (_function || _memberFunctionAndPointer._object); } /** @@ -164,12 +156,6 @@ private: } } - /** - * @brief True value used in conversion to bool, this function is useless - * beside this usage - */ - void trueValue() const {} - struct MemberFunctionAndPtr { /* * Forward declaration of a class and a member function to this class. diff --git a/ble/Gap.h b/ble/Gap.h index c3b318a..7eefcd9 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -897,35 +897,56 @@ public: /** * Set up a callback for timeout events. Refer to TimeoutSource_t for * possible event types. + * @note It is possible to unregister callbacks using onTimeout().detach(callback) */ void onTimeout(TimeoutEventCallback_t callback) { timeoutCallbackChain.add(callback); } + /** + * @brief provide access to the callchain of timeout event callbacks + * It is possible to register callbacks using onTimeout().add(callback); + * It is possible to unregister callbacks using onTimeout().detach(callback) + * @return The timeout event callbacks chain + */ TimeoutEventCallbackChain_t& onTimeout() { return timeoutCallbackChain; } /** * Append to a chain of callbacks to be invoked upon GAP connection. + * @note It is possible to unregister callbacks using onConnection().detach(callback) */ void onConnection(ConnectionEventCallback_t callback) {connectionCallChain.add(callback);} template void onConnection(T *tptr, void (T::*mptr)(const ConnectionCallbackParams_t*)) {connectionCallChain.add(tptr, mptr);} + /** + * @brief provide access to the callchain of connection event callbacks + * It is possible to register callbacks using onConnection().add(callback); + * It is possible to unregister callbacks using onConnection().detach(callback) + * @return The connection event callbacks chain + */ ConnectionEventCallbackChain_t& onconnection() { return connectionCallChain; } /** * Append to a chain of callbacks to be invoked upon GAP disconnection. + * @note It is possible to unregister callbacks using onDisconnection().detach(callback) */ void onDisconnection(DisconnectionEventCallback_t callback) {disconnectionCallChain.add(callback);} template void onDisconnection(T *tptr, void (T::*mptr)(const DisconnectionCallbackParams_t*)) {disconnectionCallChain.add(tptr, mptr);} + /** + * @brief provide access to the callchain of disconnection event callbacks + * It is possible to register callbacks using onDisconnection().add(callback); + * It is possible to unregister callbacks using onDisconnection().detach(callback) + * @return The disconnection event callbacks chain + */ DisconnectionEventCallbackChain_t& onDisconnection() { return disconnectionCallChain; } @@ -959,15 +980,10 @@ public: */ void onRadioNotification(void (*callback)(bool param)) { radioNotificationCallback.attach(callback); - // why does it start radio notification ? It is not even indicated in the - // doc that it start the listening process - initRadioNotification(); } template void onRadioNotification(T *tptr, void (T::*mptr)(bool)) { radioNotificationCallback.attach(tptr, mptr); - // why does it start radio notification ? - initRadioNotification(); } protected: diff --git a/ble/GattClient.h b/ble/GattClient.h index 343b5e9..a4109d3 100644 --- a/ble/GattClient.h +++ b/ble/GattClient.h @@ -246,24 +246,40 @@ public: /* Event callback handlers. */ public: /** - * Set up a callback for read response events. + * Set up a callback for read response events. + * It is possible to remove registered callbacks using + * onDataRead().detach(callbackToRemove) */ void onDataRead(ReadCallback_t callback) { onDataReadCallbackChain.add(callback); } + /** + * @brief provide access to the callchain of read callbacks + * It is possible to register callbacks using onDataRead().add(callback); + * It is possible to unregister callbacks using onDataRead().detach(callback) + * @return The read callbacks chain + */ ReadCallbackChain_t& onDataRead() { return onDataReadCallbackChain; } /** * Set up a callback for write response events. + * It is possible to remove registered callbacks using + * onDataWritten().detach(callbackToRemove). * @Note: Write commands (issued using writeWoResponse) don't generate a response. */ void onDataWritten(WriteCallback_t callback) { onDataWriteCallbackChain.add(callback); } + /** + * @brief provide access to the callchain of data written callbacks + * It is possible to register callbacks using onDataWritten().add(callback); + * It is possible to unregister callbacks using onDataWritten().detach(callback) + * @return The data written callbacks chain + */ WriteCallbackChain_t& onDataWritten() { return onDataWriteCallbackChain; } @@ -292,11 +308,19 @@ public: * Set up a callback for when the GATT client receives an update event * corresponding to a change in the value of a characteristic on the remote * GATT server. + * It is possible to remove registered callbacks using onHVX().detach(callbackToRemove). */ void onHVX(HVXCallback_t callback) { onHVXCallbackChain.add(callback); } + + /** + * @brief provide access to the callchain of HVX callbacks + * It is possible to register callbacks using onHVX().add(callback); + * It is possible to unregister callbacks using onHVX().detach(callback) + * @return The HVX callbacks chain + */ HVXCallbackChain_t& onHVX() { return onHVXCallbackChain; } diff --git a/ble/GattServer.h b/ble/GattServer.h index a71f63b..6a6324b 100644 --- a/ble/GattServer.h +++ b/ble/GattServer.h @@ -274,6 +274,8 @@ public: * * @Note: It is also possible to set up a callback into a member function of * some object. + * + * @Note It is possible to unregister a callback using onDataWritten().detach(callback) */ void onDataWritten(const DataWrittenCallback_t& callback) {dataWrittenCallChain.add(callback);} template @@ -281,6 +283,12 @@ public: dataWrittenCallChain.add(objPtr, memberPtr); } + /** + * @brief provide access to the callchain of data written event callbacks + * It is possible to register callbacks using onDataWritten().add(callback); + * It is possible to unregister callbacks using onDataWritten().detach(callback) + * @return The data written event callbacks chain + */ DataWrittenCallbackChain_t& onDataWritten() { return dataWrittenCallChain; } @@ -301,6 +309,8 @@ public: * @Note: It is also possible to set up a callback into a member function of * some object. * + * @Note It is possible to unregister a callback using onDataRead().detach(callback) + * * @return BLE_ERROR_NOT_IMPLEMENTED if this functionality isn't available; * else BLE_ERROR_NONE. */ @@ -322,6 +332,12 @@ public: return BLE_ERROR_NONE; } + /** + * @brief provide access to the callchain of data read event callbacks + * It is possible to register callbacks using onDataRead().add(callback); + * It is possible to unregister callbacks using onDataRead().detach(callback) + * @return The data read event callbacks chain + */ DataReadCallbackChain_t& onDataRead() { return dataReadCallChain; } diff --git a/ble/SafeBool.h b/ble/SafeBool.h new file mode 100644 index 0000000..c498508 --- /dev/null +++ b/ble/SafeBool.h @@ -0,0 +1,114 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BLE_API_SAFE_BOOL_H_ +#define BLE_API_SAFE_BOOL_H_ + +//safe bool idiom, see : http://www.artima.com/cppsource/safebool.html + +namespace SafeBool_ { +/** + * @brief Base class for all intances of SafeBool, + * This base class reduce instantiation of trueTag function + */ +class base { + template + friend class SafeBool; + +protected: + //the bool type is a pointer to method which can be used in boolean context + typedef void (base::*BoolType_t)() const; + + // non implemented call, use to disallow conversion between unrelated types + void invalidTag() const; + + // member function which indicate true value + void trueTag() const {} +}; + + +} + +/** + * @brief template class SafeBool use CRTP to made boolean conversion easy and correct. + * Derived class should implement the function bool toBool() const to make this work. Inheritance + * should be public. + * + * @tparam T Type of the derived class + * + * \code + * + * class A : public SafeBool { + * public: + * + * // boolean conversion + * bool toBool() { + * + * } + * }; + * + * class B : public SafeBool { + * public: + * + * // boolean conversion + * bool toBool() const { + * + * } + * }; + * + * A a; + * B b; + * + * // will compile + * if(a) { + * + * } + * + * // compilation error + * if(a == b) { + * + * } + * + * + * \endcode + */ +template +class SafeBool : public SafeBool_::base { +public: + /** + * bool operator implementation, derived class has to provide bool toBool() const function. + */ + operator BoolType_t() const { + return (static_cast(this))->toBool() + ? &SafeBool::trueTag : 0; + } +}; + +//Avoid conversion to bool between different classes +template +void operator==(const SafeBool& lhs,const SafeBool& rhs) { + lhs.invalidTag(); +// return false; +} + +//Avoid conversion to bool between different classes +template +void operator!=(const SafeBool& lhs,const SafeBool& rhs) { + lhs.invalidTag(); +// return false; +} + +#endif /* BLE_API_SAFE_BOOL_H_ */ diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index b037c13..91119e0 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -33,7 +33,7 @@ DiscoveredCharacteristic::read(uint16_t offset) const struct OneShotReadCallback { static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle,const GattClient::ReadCallback_t& cb) { + GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) { OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); oneShot->attach(); // delete will be made when this callback is called @@ -107,7 +107,7 @@ DiscoveredCharacteristic::writeWoResponse(uint16_t length, const uint8_t *value) struct OneShotWriteCallback { static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle,const GattClient::WriteCallback_t& cb) { + GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) { OneShotWriteCallback* oneShot = new OneShotWriteCallback(client, connHandle, handle, cb); oneShot->attach(); // delete will be made when this callback is called