Removed implicit conversion in comparison operators (issue #998)

This commit is contained in:
Benoit Blanchon
2019-05-23 21:54:42 +02:00
parent 4eb8074358
commit 630107ae8a
14 changed files with 398 additions and 148 deletions

View File

@ -7,154 +7,216 @@
#include "../Variant/VariantRef.hpp"
namespace ARDUINOJSON_NAMESPACE {
template <typename T, typename Enable = void>
struct Comparer;
template <typename T>
struct is_simple_value {
static const bool value = is_integral<T>::value ||
is_floating_point<T>::value ||
is_same<T, bool>::value;
struct Comparer<T, typename enable_if<IsString<T>::value>::type> {
T rhs;
int result;
explicit Comparer(T value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float) {}
void visitString(const char *lhs) {
result = -adaptString(rhs).compare(lhs);
}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {
result = adaptString(rhs).compare(NULL);
}
};
template <typename T>
typename enable_if<is_signed<T>::value, int>::type sign(const T &value) {
return value < 0 ? -1 : value > 0 ? 1 : 0;
}
template <typename T>
typename enable_if<is_unsigned<T>::value, int>::type sign(const T &value) {
return value > 0 ? 1 : 0;
}
template <typename T>
struct Comparer<T, typename enable_if<is_integral<T>::value ||
is_floating_point<T>::value>::type> {
T rhs;
int result;
explicit Comparer(T value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float lhs) {
result = sign(lhs - static_cast<Float>(rhs));
}
void visitString(const char *) {}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt lhs) {
result = -sign(static_cast<T>(lhs) + rhs);
}
void visitPositiveInteger(UInt lhs) {
result = static_cast<T>(lhs) < rhs ? -1 : static_cast<T>(lhs) > rhs ? 1 : 0;
}
void visitBoolean(bool) {}
void visitNull() {}
};
template <>
struct Comparer<bool, void> {
bool rhs;
int result;
explicit Comparer(bool value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float) {}
void visitString(const char *) {}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool lhs) {
result = static_cast<int>(lhs - rhs);
}
void visitNull() {}
};
template <typename TVariant>
class VariantComparisons {
private:
template <typename T>
static int compare(TVariant lhs, const T &rhs) {
Comparer<T> comparer(rhs);
lhs.accept(comparer);
return comparer.result;
}
public:
// const char* == TVariant
// value == TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator==(
T *lhs, TVariant rhs) {
return adaptString(lhs).equals(rhs.template as<const char *>());
friend bool operator==(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == 0;
}
template <typename T>
friend bool operator==(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) == 0;
}
// std::string == TVariant
// TVariant == value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator==(
const T &lhs, TVariant rhs) {
return adaptString(lhs).equals(rhs.template as<const char *>());
friend bool operator==(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == 0;
}
template <typename T>
friend bool operator==(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) == 0;
}
// TVariant == const char*
// value != TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator==(
TVariant lhs, T *rhs) {
return adaptString(rhs).equals(lhs.template as<const char *>());
friend bool operator!=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) != 0;
}
template <typename T>
friend bool operator!=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) != 0;
}
// TVariant == std::string
// TVariant != value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator==(
TVariant lhs, const T &rhs) {
return adaptString(rhs).equals(lhs.template as<const char *>());
friend bool operator!=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) != 0;
}
template <typename T>
friend bool operator!=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) != 0;
}
// bool/int/float == TVariant
// value < TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator==(
const T &lhs, TVariant rhs) {
return lhs == rhs.template as<T>();
friend bool operator<(T *lhs, TVariant rhs) {
return compare(rhs, lhs) > 0;
}
template <typename T>
friend bool operator<(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) > 0;
}
// TVariant == bool/int/float
// TVariant < value
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator==(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() == rhs;
friend bool operator<(TVariant lhs, T *rhs) {
return compare(lhs, rhs) < 0;
}
template <typename T>
friend bool operator<(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) < 0;
}
// const char* != TVariant
// value <= TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator!=(
T *lhs, TVariant rhs) {
return !adaptString(lhs).equals(rhs.template as<const char *>());
friend bool operator<=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) >= 0;
}
template <typename T>
friend bool operator<=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) >= 0;
}
// std::string != TVariant
// TVariant <= value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator!=(
const T &lhs, TVariant rhs) {
return !adaptString(lhs).equals(rhs.template as<const char *>());
friend bool operator<=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) <= 0;
}
template <typename T>
friend bool operator<=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) <= 0;
}
// TVariant != const char*
// value > TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator!=(
TVariant lhs, T *rhs) {
return !adaptString(rhs).equals(lhs.template as<const char *>());
friend bool operator>(T *lhs, TVariant rhs) {
return compare(rhs, lhs) < 0;
}
template <typename T>
friend bool operator>(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) < 0;
}
// TVariant != std::string
// TVariant > value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return !adaptString(rhs).equals(lhs.template as<const char *>());
friend bool operator>(TVariant lhs, T *rhs) {
return compare(lhs, rhs) > 0;
}
template <typename T>
friend bool operator>(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) > 0;
}
// bool/int/float != TVariant
// value >= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=(
const T &lhs, TVariant rhs) {
return lhs != rhs.template as<T>();
friend bool operator>=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) <= 0;
}
template <typename T>
friend bool operator>=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) <= 0;
}
// TVariant != bool/int/float
// TVariant >= value
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() != rhs;
friend bool operator>=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) >= 0;
}
// bool/int/float < TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<(
const T &lhs, TVariant rhs) {
return lhs < rhs.template as<T>();
}
// TVariant < bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() < rhs;
}
// bool/int/float <= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=(
const T &lhs, TVariant rhs) {
return lhs <= rhs.template as<T>();
}
// TVariant <= bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() <= rhs;
}
// bool/int/float > TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>(
const T &lhs, TVariant rhs) {
return lhs > rhs.template as<T>();
}
// TVariant > bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() > rhs;
}
// bool/int/float >= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=(
const T &lhs, TVariant rhs) {
return lhs >= rhs.template as<T>();
}
// TVariant >= bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() >= rhs;
friend bool operator>=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) >= 0;
}
};

View File

@ -0,0 +1,22 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#pragma once
namespace ARDUINOJSON_NAMESPACE {
inline int8_t safe_strcmp(const char* a, const char* b) {
if (a == b) return 0;
if (!a) return -1;
if (!b) return 1;
return static_cast<int8_t>(strcmp(a, b));
}
inline int8_t safe_strncmp(const char* a, const char* b, size_t n) {
if (a == b) return 0;
if (!a) return -1;
if (!b) return 1;
return static_cast<int8_t>(strncmp(a, b, n));
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -5,6 +5,7 @@
#pragma once
#include <WString.h>
#include "../Polyfills/safe_strcmp.hpp"
namespace ARDUINOJSON_NAMESPACE {
@ -25,11 +26,14 @@ class ArduinoStringAdapter {
return !_str->c_str();
}
bool equals(const char* expected) const {
int8_t compare(const char* other) const {
// Arduino's String::c_str() can return NULL
const char* actual = _str->c_str();
if (!actual || !expected) return actual == expected;
return 0 == strcmp(actual, expected);
const char* me = _str->c_str();
return safe_strcmp(me, other);
}
bool equals(const char* expected) const {
return compare(expected) == 0;
}
const char* data() const {

View File

@ -6,6 +6,7 @@
#include <stddef.h> // size_t
#include <string.h> // strcmp
#include "../Polyfills/safe_strcmp.hpp"
namespace ARDUINOJSON_NAMESPACE {
@ -13,10 +14,12 @@ class ConstRamStringAdapter {
public:
ConstRamStringAdapter(const char* str = 0) : _str(str) {}
int8_t compare(const char* other) const {
return safe_strcmp(_str, other);
}
bool equals(const char* expected) const {
const char* actual = _str;
if (!actual || !expected) return actual == expected;
return strcmp(actual, expected) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -10,10 +10,15 @@ class FlashStringAdapter {
public:
FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {}
int8_t compare(const char* other) const {
if (!other && !_str) return 0;
if (!_str) return -1;
if (!other) return 1;
return -strcmp_P(other, reinterpret_cast<const char*>(_str));
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp_P(expected, actual) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -11,10 +11,15 @@ class SizedFlashStringAdapter {
SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz)
: _str(str), _size(sz) {}
int8_t compare(const char* other) const {
if (!other && !_str) return 0;
if (!_str) return -1;
if (!other) return 1;
return -strncmp_P(other, reinterpret_cast<const char*>(_str), _size);
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strncmp_P(expected, actual, _size) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -12,10 +12,12 @@ class SizedRamStringAdapter {
public:
SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {}
int8_t compare(const char* other) const {
return safe_strncmp(_str, other, _size) == 0;
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp(actual, expected) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -23,6 +23,11 @@ class StlStringAdapter {
return false;
}
int8_t compare(const char* other) const {
if (!other) return 1;
return static_cast<int8_t>(_str->compare(other));
}
bool equals(const char* expected) const {
if (!expected) return false;
return *_str == expected;