diff --git a/ble/CallChainOfFunctionPointersWithContext.h b/ble/CallChainOfFunctionPointersWithContext.h index 3d5ff7d..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; @@ -97,6 +98,49 @@ public: return common_add(new FunctionPointerWithContext(tptr, mptr)); } + /** Add a function at the front of the chain. + * + * @param func The FunctionPointerWithContext to add. + */ + pFunctionPointerWithContext_t add(const FunctionPointerWithContext& func) { + return common_add(new FunctionPointerWithContext(func)); + } + + /** + * 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 + */ + bool detach(const FunctionPointerWithContext& toDetach) { + pFunctionPointerWithContext_t current = chainHead; + pFunctionPointerWithContext_t previous = NULL; + + while (current) { + if(*current == toDetach) { + if(previous == NULL) { + if(currentCalled == current) { + currentCalled = NULL; + } + chainHead = current->getNext(); + } else { + if(currentCalled == current) { + currentCalled = previous; + } + 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) { @@ -115,16 +159,56 @@ 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) { - if (chainHead) { - chainHead->call(context); + ((const CallChainOfFunctionPointersWithContext*) this)->call(context); + } + + /** + * @brief same as above but const + */ + void call(ContextType context) const { + 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(); + } } } + /** + * @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); + } + + /** + * @brief bool conversion operation + */ + bool toBool() const { + return chainHead != NULL; + } + private: pFunctionPointerWithContext_t common_add(pFunctionPointerWithContext_t pf) { if (chainHead == NULL) { @@ -139,6 +223,11 @@ 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; + /* Disallow copy constructor and assignment operators. */ private: diff --git a/ble/DiscoveredCharacteristic.h b/ble/DiscoveredCharacteristic.h index 1ff67b8..583a8de 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. * @@ -135,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/ble/FunctionPointerWithContext.h b/ble/FunctionPointerWithContext.h index 5fb6e29..02b07a6 100644 --- a/ble/FunctionPointerWithContext.h +++ b/ble/FunctionPointerWithContext.h @@ -18,14 +18,16 @@ #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; typedef void (*pvoidfcontext_t)(ContextType context); /** Create a FunctionPointerWithContext, attaching a static function. @@ -33,7 +35,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 +50,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,13 +86,29 @@ 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) { + void call(ContextType context) const { _caller(this, context); + } - /* Propagate the call to next in the chain. */ - if (_next) { - _next->call(context); - } + /** + * @brief Same as above + */ + void operator()(ContextType context) const { + call(context); + } + + /** Same as above, workaround for mbed os FunctionPointer implementation. */ + void call(ContextType context) { + ((const FunctionPointerWithContext*) this)->call(context); + } + + typedef void (FunctionPointerWithContext::*bool_type)() const; + + /** + * implementation of safe bool operator + */ + bool toBool() const { + return (_function || _memberFunctionAndPointer._object); } /** @@ -101,9 +130,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 +150,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 +179,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 +190,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 diff --git a/ble/Gap.h b/ble/Gap.h index c87c6e3..7eefcd9 100644 --- a/ble/Gap.h +++ b/ble/Gap.h @@ -140,10 +140,15 @@ public: return (durationInMillis * 1000) / UNIT_1_25_MS; } + typedef FunctionPointerWithContext TimeoutEventCallback_t; + typedef CallChainOfFunctionPointersWithContext TimeoutEventCallbackChain_t; + + typedef FunctionPointerWithContext ConnectionEventCallback_t; + typedef CallChainOfFunctionPointersWithContext ConnectionEventCallbackChain_t; + + typedef FunctionPointerWithContext DisconnectionEventCallback_t; + typedef CallChainOfFunctionPointersWithContext DisconnectionEventCallbackChain_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; /* @@ -892,25 +897,60 @@ 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) {timeoutCallback = 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; + } + /** * Set the application callback for radio-notification events. * @@ -940,12 +980,10 @@ public: */ void onRadioNotification(void (*callback)(bool param)) { radioNotificationCallback.attach(callback); - initRadioNotification(); } template void onRadioNotification(T *tptr, void (T::*mptr)(bool)) { radioNotificationCallback.attach(tptr, mptr); - initRadioNotification(); } protected: @@ -956,7 +994,7 @@ protected: _scanResponse(), state(), scanningActive(false), - timeoutCallback(NULL), + timeoutCallbackChain(), radioNotificationCallback(), onAdvertisementReport(), connectionCallChain(), @@ -1002,8 +1040,8 @@ public: } void processTimeoutEvent(TimeoutSource_t source) { - if (timeoutCallback) { - timeoutCallback(source); + if (timeoutCallbackChain) { + timeoutCallbackChain(source); } } @@ -1017,11 +1055,11 @@ protected: bool scanningActive; protected: - TimeoutEventCallback_t timeoutCallback; - RadioNotificationEventCallback_t radioNotificationCallback; - AdvertisementReportCallback_t onAdvertisementReport; - CallChainOfFunctionPointersWithContext connectionCallChain; - CallChainOfFunctionPointersWithContext disconnectionCallChain; + TimeoutEventCallbackChain_t timeoutCallbackChain; + RadioNotificationEventCallback_t radioNotificationCallback; + AdvertisementReportCallback_t onAdvertisementReport; + ConnectionEventCallbackChain_t connectionCallChain; + DisconnectionEventCallbackChain_t disconnectionCallChain; private: /* Disallow copy and assignment. */ diff --git a/ble/GattClient.h b/ble/GattClient.h index b814ede..a4109d3 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. @@ -241,18 +246,42 @@ 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) { - onDataReadCallback = 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) { - onDataWriteCallback = 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; } /** @@ -279,9 +308,21 @@ 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) { - onHVXCallback = 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; } protected: @@ -292,27 +333,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. */ diff --git a/ble/GattServer.h b/ble/GattServer.h index 57e8649..6a6324b 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 @@ -258,13 +274,25 @@ 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(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); } + /** + * @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; + } + /** * Setup a callback to be invoked on the peripheral when an attribute is * being read by a remote client. @@ -281,10 +309,12 @@ 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. */ - 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 +332,16 @@ 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; + } + /** * Set up a callback for when notifications or indications are enabled for a * characteristic on the local GATT server. @@ -323,15 +363,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 +393,7 @@ protected: } void handleDataSentEvent(unsigned count) { - if (dataSentCallChain.hasCallbacksAttached()) { - dataSentCallChain.call(count); - } + dataSentCallChain.call(count); } protected: @@ -367,12 +401,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. */ 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/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: /** diff --git a/source/DiscoveredCharacteristic.cpp b/source/DiscoveredCharacteristic.cpp index 5d0133d..91119e0 100644 --- a/source/DiscoveredCharacteristic.cpp +++ b/source/DiscoveredCharacteristic.cpp @@ -31,6 +31,52 @@ 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) { + OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); + oneShot->attach(); + // delete will be made when this callback is called + } + +private: + 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) { + // 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; +}; + +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, connHandle, valueHandle, onRead); + + return error; +} + ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value) const { @@ -59,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 {