diff --git a/src/NimBLEAttValue.h b/src/NimBLEAttValue.h index ba92b080..bddf020e 100644 --- a/src/NimBLEAttValue.h +++ b/src/NimBLEAttValue.h @@ -73,6 +73,14 @@ template struct Has_c_str_length().c_str())), decltype(void(std::declval().length()))> : std::true_type {}; +/* Used to determine if the type passed to a template has a value_type member (std::vector, std::array, std::string, etc.). */ +template +struct Has_value_type : std::false_type {}; + +template +struct Has_value_type + : std::true_type {}; + /** * @brief A specialized container class to hold BLE attribute values. * @details This class is designed to be more memory efficient than using\n @@ -274,13 +282,32 @@ class NimBLEAttValue { /** * @brief Template to set value to the value of val. * @param [in] v The value to set. - * @details Only used if the has a `data()` and `size()` method. + * @details Only used if the has a `data()` and `size()` method with `value_type`. + * Correctly calculates byte size for containers with multi-byte element types. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && Has_value_type::value, bool>::type +# endif + setValue(const T& v) { + return setValue( + reinterpret_cast(v.data()), + v.size() * sizeof(typename T::value_type) + ); + } + + /** + * @brief Template to set value to the value of val. + * @param [in] v The value to set. + * @details Only used if the has a `data()` and `size()` method without `value_type`. */ template # ifdef _DOXYGEN_ bool # else - typename std::enable_if::value, bool>::type + typename std::enable_if::value && !Has_value_type::value, bool>::type # endif setValue(const T& v) { return setValue(reinterpret_cast(v.data()), v.size()); @@ -295,7 +322,11 @@ class NimBLEAttValue { template typename std::enable_if::value, bool>::type setValue(const T& s) { if constexpr (Has_data_size::value) { - return setValue(reinterpret_cast(s.data()), s.size()); + if constexpr (Has_value_type::value) { + return setValue(reinterpret_cast(s.data()), s.size() * sizeof(typename T::value_type)); + } else { + return setValue(reinterpret_cast(s.data()), s.size()); + } } else if constexpr (Has_c_str_length::value) { return setValue(reinterpret_cast(s.c_str()), s.length()); } else { diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 789d21d7..58017907 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -112,15 +112,35 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { } /** - * @brief Template to send a notification with a value from a class that has a data() and size() method. + * @brief Template to send a notification with a value from a class that has a data() and size() method with value_type. * @param [in] v The value to send. * @param [in] connHandle Optional, a connection handle to send the notification to. + * @details Correctly calculates byte size for containers with multi-byte element types. */ template # ifdef _DOXYGEN_ bool # else - typename std::enable_if::value, bool>::type + typename std::enable_if::value && Has_value_type::value, bool>::type +# endif + notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return notify( + reinterpret_cast(v.data()), + v.size() * sizeof(typename T::value_type), + connHandle + ); + } + + /** + * @brief Template to send a notification with a value from a class that has a data() and size() method without value_type. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_value_type::value, bool>::type # endif notify(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { return notify(reinterpret_cast(v.data()), v.size(), connHandle); @@ -160,7 +180,27 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { } /** - * @brief Template to send a indication with a value from a class that has a data() and size() method. + * @brief Template to send a indication with a value from a class that has a data() and size() method with value_type. + * @param [in] v The value to send. + * @param [in] connHandle Optional, a connection handle to send the notification to. + * @details Correctly calculates byte size for containers with multi-byte element types. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && Has_value_type::value, bool>::type +# endif + indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { + return indicate( + reinterpret_cast(v.data()), + v.size() * sizeof(typename T::value_type), + connHandle + ); + } + + /** + * @brief Template to send a indication with a value from a class that has a data() and size() method without value_type. * @param [in] v The value to send. * @param [in] connHandle Optional, a connection handle to send the notification to. */ @@ -168,7 +208,7 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { # ifdef _DOXYGEN_ bool # else - typename std::enable_if::value, bool>::type + typename std::enable_if::value && !Has_value_type::value, bool>::type # endif indicate(const T& v, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { return indicate(reinterpret_cast(v.data()), v.size(), connHandle); @@ -190,7 +230,11 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { typename std::enable_if::value && !std::is_array::value, bool>::type notify( const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { if constexpr (Has_data_size::value) { - return notify(reinterpret_cast(value.data()), value.size(), connHandle); + if constexpr (Has_value_type::value) { + return notify(reinterpret_cast(value.data()), value.size() * sizeof(typename T::value_type), connHandle); + } else { + return notify(reinterpret_cast(value.data()), value.size(), connHandle); + } } else if constexpr (Has_c_str_length::value) { return notify(reinterpret_cast(value.c_str()), value.length(), connHandle); } else { @@ -212,7 +256,11 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { typename std::enable_if::value && !std::is_array::value, bool>::type indicate( const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { if constexpr (Has_data_size::value) { - return indicate(reinterpret_cast(value.data()), value.size(), connHandle); + if constexpr (Has_value_type::value) { + return indicate(reinterpret_cast(value.data()), value.size() * sizeof(typename T::value_type), connHandle); + } else { + return indicate(reinterpret_cast(value.data()), value.size(), connHandle); + } } else if constexpr (Has_c_str_length::value) { return indicate(reinterpret_cast(value.c_str()), value.length(), connHandle); } else { diff --git a/src/NimBLERemoteValueAttribute.h b/src/NimBLERemoteValueAttribute.h index 7a92479a..bc76319b 100644 --- a/src/NimBLERemoteValueAttribute.h +++ b/src/NimBLERemoteValueAttribute.h @@ -109,13 +109,34 @@ class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAtt * @brief Template to set the remote characteristic value to val. * @param [in] v The value to write. * @param [in] response True == request write response. - * @details Only used if the has a `data()` and `size()` method. + * @details Only used if the has a `data()` and `size()` method with `value_type`. + * Correctly calculates byte size for containers with multi-byte element types. */ template # ifdef _DOXYGEN_ bool # else - typename std::enable_if::value, bool>::type + typename std::enable_if::value && Has_value_type::value, bool>::type +# endif + writeValue(const T& v, bool response = false) const { + return writeValue( + reinterpret_cast(v.data()), + v.size() * sizeof(typename T::value_type), + response + ); + } + + /** + * @brief Template to set the remote characteristic value to val. + * @param [in] v The value to write. + * @param [in] response True == request write response. + * @details Only used if the has a `data()` and `size()` method without `value_type`. + */ + template +# ifdef _DOXYGEN_ + bool +# else + typename std::enable_if::value && !Has_value_type::value, bool>::type # endif writeValue(const T& v, bool response = false) const { return writeValue(reinterpret_cast(v.data()), v.size(), response); @@ -131,7 +152,11 @@ class NimBLERemoteValueAttribute : public NimBLEValueAttribute, public NimBLEAtt template typename std::enable_if::value, bool>::type writeValue(const T& v, bool response = false) const { if constexpr (Has_data_size::value) { - return writeValue(reinterpret_cast(v.data()), v.size(), response); + if constexpr (Has_value_type::value) { + return writeValue(reinterpret_cast(v.data()), v.size() * sizeof(typename T::value_type), response); + } else { + return writeValue(reinterpret_cast(v.data()), v.size(), response); + } } else if constexpr (Has_c_str_length::value) { return writeValue(reinterpret_cast(v.c_str()), v.length(), response); } else {