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
This commit is contained in:
Vincent Coubard 2015-11-25 11:27:33 +00:00
parent 38e27c4a0a
commit 184d29c35b
7 changed files with 213 additions and 40 deletions

View File

@ -18,6 +18,7 @@
#include <string.h>
#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 <typename ContextType>
class CallChainOfFunctionPointersWithContext {
class CallChainOfFunctionPointersWithContext : public SafeBool<CallChainOfFunctionPointersWithContext<ContextType> > {
public:
typedef FunctionPointerWithContext<ContextType> *pFunctionPointerWithContext_t;
@ -101,8 +102,8 @@ public:
*
* @param func The FunctionPointerWithContext to add.
*/
void add(const FunctionPointerWithContext<ContextType>& func) {
common_add(new FunctionPointerWithContext<ContextType>(func));
pFunctionPointerWithContext_t add(const FunctionPointerWithContext<ContextType>& func) {
return common_add(new FunctionPointerWithContext<ContextType>(func));
}
/**
@ -112,7 +113,7 @@ public:
*
* @return true if a function pointer has been detached and false otherwise
*/
void detach(const FunctionPointerWithContext<ContextType>& toDetach) {
bool detach(const FunctionPointerWithContext<ContextType>& 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<bool> 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;

View File

@ -18,12 +18,13 @@
#define MBED_FUNCTIONPOINTER_WITH_CONTEXT_H
#include <string.h>
#include "SafeBool.h"
/** A class for storing and calling a pointer to a static or member void function
* that takes a context.
*/
template <typename ContextType>
class FunctionPointerWithContext {
class FunctionPointerWithContext : public SafeBool<FunctionPointerWithContext<ContextType> > {
public:
typedef FunctionPointerWithContext<ContextType> *pFunctionPointerWithContext_t;
typedef const FunctionPointerWithContext<ContextType> *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.

View File

@ -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<typename T>
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<typename T>
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 <typename T>
void onRadioNotification(T *tptr, void (T::*mptr)(bool)) {
radioNotificationCallback.attach(tptr, mptr);
// why does it start radio notification ?
initRadioNotification();
}
protected:

View File

@ -247,23 +247,39 @@ public:
public:
/**
* 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;
}

View File

@ -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 <typename T>
@ -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;
}

114
ble/SafeBool.h Normal file
View File

@ -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<typename>
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<A> {
* public:
*
* // boolean conversion
* bool toBool() {
*
* }
* };
*
* class B : public SafeBool<B> {
* public:
*
* // boolean conversion
* bool toBool() const {
*
* }
* };
*
* A a;
* B b;
*
* // will compile
* if(a) {
*
* }
*
* // compilation error
* if(a == b) {
*
* }
*
*
* \endcode
*/
template <typename T>
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<const T*>(this))->toBool()
? &SafeBool<T>::trueTag : 0;
}
};
//Avoid conversion to bool between different classes
template <typename T, typename U>
void operator==(const SafeBool<T>& lhs,const SafeBool<U>& rhs) {
lhs.invalidTag();
// return false;
}
//Avoid conversion to bool between different classes
template <typename T,typename U>
void operator!=(const SafeBool<T>& lhs,const SafeBool<U>& rhs) {
lhs.invalidTag();
// return false;
}
#endif /* BLE_API_SAFE_BOOL_H_ */

View File

@ -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