Skip to content

Commit

Permalink
Fixed enums serialized as booleans (fixes #1197)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Feb 26, 2020
1 parent 0214c9b commit 2996503
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@ HEAD
* Changed the array subscript operator to automatically add missing elements
* Fixed "deprecated-copy" warning on GCC 9 (fixes #1184)
* Fixed `MemberProxy::set(char[])` not duplicating the string (issue #1191)
* Fixed enums serialized as booleans (issue #1197)

v6.14.1 (2020-01-27)
-------
Expand Down
11 changes: 11 additions & 0 deletions extras/tests/JsonVariant/set.cpp
Expand Up @@ -5,6 +5,8 @@
#include <ArduinoJson.h>
#include <catch.hpp>

enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };

TEST_CASE("JsonVariant and strings") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
Expand Down Expand Up @@ -91,6 +93,15 @@ TEST_CASE("JsonVariant and strings") {

REQUIRE(variant == "hello");
}

SECTION("stores an enum as an integer") {
ErrorCode code = ERROR_10;

variant.set(code);

REQUIRE(variant.is<int>() == true);
REQUIRE(variant.as<int>() == 10);
}
}

TEST_CASE("JsonVariant with not enough memory") {
Expand Down
27 changes: 27 additions & 0 deletions extras/tests/Misc/TypeTraits.cpp
Expand Up @@ -7,6 +7,9 @@

using namespace ARDUINOJSON_NAMESPACE;

class EmptyClass {};
enum EmptyEnum {};

TEST_CASE("Polyfills/type_traits") {
SECTION("is_base_of") {
REQUIRE_FALSE(
Expand Down Expand Up @@ -48,6 +51,30 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_unsigned<double>::value == false);
}

SECTION("is_convertible") {
CHECK((is_convertible<short, int>::value == true));
CHECK((is_convertible<int, int>::value == true));
CHECK((is_convertible<EmptyEnum, int>::value == true));
CHECK((is_convertible<int*, int>::value == false));
CHECK((is_convertible<EmptyClass, int>::value == false));
}

SECTION("is_class") {
CHECK((is_class<int>::value == false));
CHECK((is_class<EmptyEnum>::value == false));
CHECK((is_class<int*>::value == false));
CHECK((is_class<EmptyClass>::value == true));
}

SECTION("is_enum") {
CHECK(is_enum<int>::value == false);
CHECK(is_enum<EmptyEnum>::value == true);
CHECK(is_enum<int*>::value == false);
CHECK(is_enum<EmptyClass>::value == false);
CHECK(is_enum<bool>::value == false);
CHECK(is_enum<double>::value == false);
}

SECTION("IsVisitable") {
CHECK(IsVisitable<DeserializationError>::value == false);
CHECK(IsVisitable<JsonPair>::value == false);
Expand Down
3 changes: 3 additions & 0 deletions src/ArduinoJson/Polyfills/type_traits.hpp
Expand Up @@ -9,7 +9,10 @@
#include "type_traits/integral_constant.hpp"
#include "type_traits/is_array.hpp"
#include "type_traits/is_base_of.hpp"
#include "type_traits/is_class.hpp"
#include "type_traits/is_const.hpp"
#include "type_traits/is_convertible.hpp"
#include "type_traits/is_enum.hpp"
#include "type_traits/is_floating_point.hpp"
#include "type_traits/is_integral.hpp"
#include "type_traits/is_same.hpp"
Expand Down
14 changes: 14 additions & 0 deletions src/ArduinoJson/Polyfills/type_traits/declval.hpp
@@ -0,0 +1,14 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License

#pragma once

#include <ArduinoJson/Namespace.hpp>

namespace ARDUINOJSON_NAMESPACE {

template <typename T>
T declval();

} // namespace ARDUINOJSON_NAMESPACE
26 changes: 26 additions & 0 deletions src/ArduinoJson/Polyfills/type_traits/is_class.hpp
@@ -0,0 +1,26 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License

#pragma once

#include "declval.hpp"

namespace ARDUINOJSON_NAMESPACE {

template <typename T>
struct is_class {
protected: // <- to avoid GCC's "all member functions in class are private"
typedef char Yes[1];
typedef char No[2];

template <typename U>
static Yes &probe(void (U::*)(void));
template <typename>
static No &probe(...);

public:
static const bool value = sizeof(probe<T>(0)) == sizeof(Yes);
};

} // namespace ARDUINOJSON_NAMESPACE
34 changes: 34 additions & 0 deletions src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp
@@ -0,0 +1,34 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License

#pragma once

#include "declval.hpp"

#ifdef _MSC_VER
#pragma warning(push)
// conversion from 'T' to 'To', possible loss of data
#pragma warning(disable : 4244)
#endif

namespace ARDUINOJSON_NAMESPACE {

template <typename From, typename To>
struct is_convertible {
protected: // <- to avoid GCC's "all member functions in class are private"
typedef char Yes[1];
typedef char No[2];

static Yes &probe(To);
static No &probe(...);

public:
static const bool value = sizeof(probe(declval<From>())) == sizeof(Yes);
};

} // namespace ARDUINOJSON_NAMESPACE

#ifdef _MSC_VER
#pragma warning(pop)
#endif
23 changes: 23 additions & 0 deletions src/ArduinoJson/Polyfills/type_traits/is_enum.hpp
@@ -0,0 +1,23 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License

#pragma once

#include "is_class.hpp"
#include "is_convertible.hpp"
#include "is_floating_point.hpp"
#include "is_integral.hpp"
#include "is_same.hpp"

namespace ARDUINOJSON_NAMESPACE {

template <typename T>
struct is_enum {
static const bool value = is_convertible<T, int>::value &&
!is_class<T>::value && !is_integral<T>::value &&
!is_floating_point<T>::value &&
!is_same<T, bool>::value;
};

} // namespace ARDUINOJSON_NAMESPACE
11 changes: 10 additions & 1 deletion src/ArduinoJson/Variant/VariantRef.hpp
Expand Up @@ -153,7 +153,9 @@ class VariantRef : public VariantRefBase<VariantData>,
}

// set(bool value)
FORCE_INLINE bool set(bool value) const {
template <typename T>
FORCE_INLINE bool set(
T value, typename enable_if<is_same<T, bool>::value>::type * = 0) const {
return variantSetBoolean(_data, value);
}

Expand Down Expand Up @@ -237,6 +239,13 @@ class VariantRef : public VariantRefBase<VariantData>,
typename enable_if<IsVisitable<TVariant>::value, bool>::type set(
const TVariant &value) const;

// set(enum value)
template <typename T>
FORCE_INLINE bool set(
T value, typename enable_if<is_enum<T>::value>::type * = 0) const {
return variantSetSignedInteger(_data, static_cast<Integer>(value));
}

// Get the variant as the specified type.
//
// std::string as<std::string>() const;
Expand Down

0 comments on commit 2996503

Please sign in to comment.