Replace ConverterNeedsWriteableRef with function_traits

This commit is contained in:
Benoit Blanchon
2024-05-22 09:25:46 +02:00
parent 60f9f7eff6
commit 9e0c56acc3
11 changed files with 102 additions and 85 deletions

View File

@ -140,15 +140,3 @@ TEST_CASE("Custom converter with specialization") {
REQUIRE(doc["value"]["imag"] == 3);
}
}
TEST_CASE("ConverterNeedsWriteableRef") {
using namespace ArduinoJson::detail;
CHECK(ConverterNeedsWriteableRef<int>::value == false);
CHECK(ConverterNeedsWriteableRef<float>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonVariant>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonVariantConst>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonObject>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonObjectConst>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonArray>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonArrayConst>::value == false);
}

View File

@ -17,3 +17,24 @@ TEST_CASE("JsonVariantConst::as<T>()") {
REQUIRE(var.as<const char*>() == std::string("hello"));
REQUIRE(var.as<std::string>() == std::string("hello"));
}
TEST_CASE("Invalid conversions") {
using namespace ArduinoJson::detail;
JsonVariantConst variant;
CHECK(is_same<decltype(variant.as<int>()), int>::value);
CHECK(is_same<decltype(variant.as<float>()), float>::value);
CHECK(is_same<decltype(variant.as<JsonVariantConst>()),
JsonVariantConst>::value);
CHECK(
is_same<decltype(variant.as<JsonObjectConst>()), JsonObjectConst>::value);
CHECK(is_same<decltype(variant.as<JsonArrayConst>()), JsonArrayConst>::value);
CHECK(is_same<decltype(variant.as<JsonVariant>()),
InvalidConversion<JsonVariantConst, JsonVariant>>::value);
CHECK(is_same<decltype(variant.as<JsonObject>()),
InvalidConversion<JsonVariantConst, JsonObject>>::value);
CHECK(is_same<decltype(variant.as<JsonArray>()),
InvalidConversion<JsonVariantConst, JsonArray>>::value);
}

View File

@ -58,5 +58,10 @@
// issue #1914
#define V7 7
// STM32, Mbed, Particle
#define A0 16
#define A1 17
#define A2 18
// catch.hpp mutes several warnings, this file also allows to detect them
#include "ArduinoJson.h"

View File

@ -6,6 +6,7 @@
#include "type_traits/conditional.hpp"
#include "type_traits/enable_if.hpp"
#include "type_traits/function_traits.hpp"
#include "type_traits/integral_constant.hpp"
#include "type_traits/is_array.hpp"
#include "type_traits/is_base_of.hpp"

View File

@ -0,0 +1,27 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename Sig>
struct function_traits;
template <typename ReturnType, typename Arg1>
struct function_traits<ReturnType (*)(Arg1)> {
using return_type = ReturnType;
using arg1_type = Arg1;
};
template <typename ReturnType, typename Arg1, typename Arg2>
struct function_traits<ReturnType (*)(Arg1, Arg2)> {
using return_type = ReturnType;
using arg1_type = Arg1;
using arg2_type = Arg2;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -20,7 +20,4 @@ template <typename T1, typename T2>
class InvalidConversion; // Error here? See https://arduinojson.org/v7/invalid-conversion/
// clang-format on
template <typename T>
struct ConverterNeedsWriteableRef;
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -305,19 +305,6 @@ inline bool canConvertFromJson(JsonVariantConst src, const std::string_view&) {
#endif
namespace detail {
template <typename T>
struct ConverterNeedsWriteableRef {
protected: // <- to avoid GCC's "all member functions in class are private"
static int probe(T (*f)(ArduinoJson::JsonVariant));
static char probe(T (*f)(ArduinoJson::JsonVariantConst));
public:
static const bool value =
sizeof(probe(Converter<T>::fromJson)) == sizeof(int);
};
} // namespace detail
template <>
struct Converter<JsonArrayConst> : private detail::VariantAttorney {
static void toJson(JsonArrayConst src, JsonVariant dst) {
@ -354,13 +341,6 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
return JsonArray(data != 0 ? data->asArray() : 0, resources);
}
static detail::InvalidConversion<JsonVariantConst, JsonArray> fromJson(
JsonVariantConst);
static bool checkJson(JsonVariantConst) {
return false;
}
static bool checkJson(JsonVariant src) {
auto data = getData(src);
return data && data->isArray();
@ -403,13 +383,6 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
return JsonObject(data != 0 ? data->asObject() : 0, resources);
}
static detail::InvalidConversion<JsonVariantConst, JsonObject> fromJson(
JsonVariantConst);
static bool checkJson(JsonVariantConst) {
return false;
}
static bool checkJson(JsonVariant src) {
auto data = getData(src);
return data && data->isObject();

View File

@ -53,17 +53,10 @@ struct Converter<JsonVariant> : private detail::VariantAttorney {
return src;
}
static detail::InvalidConversion<JsonVariantConst, JsonVariant> fromJson(
JsonVariantConst);
static bool checkJson(JsonVariant src) {
auto data = getData(src);
return !!data;
}
static bool checkJson(JsonVariantConst) {
return false;
}
};
template <>

View File

@ -27,6 +27,12 @@ class JsonVariantConst : public detail::VariantTag,
public detail::VariantOperators<JsonVariantConst> {
friend class detail::VariantAttorney;
template <typename T>
using ConversionSupported =
detail::is_same<typename detail::function_traits<
decltype(&Converter<T>::fromJson)>::arg1_type,
JsonVariantConst>;
public:
// Creates an unbound reference.
JsonVariantConst() : data_(nullptr), resources_(nullptr) {}
@ -62,23 +68,35 @@ class JsonVariantConst : public detail::VariantTag,
// Casts the value to the specified type.
// https://arduinojson.org/v7/api/jsonvariantconst/as/
template <typename T>
typename detail::enable_if<!detail::is_same<T, char*>::value &&
!detail::is_same<T, char>::value,
T>::type
as() const {
typename detail::enable_if<ConversionSupported<T>::value, T>::type as()
const {
return Converter<T>::fromJson(*this);
}
// Casts the value to the specified type.
// https://arduinojson.org/v7/api/jsonvariantconst/as/
template <typename T>
typename detail::enable_if<
!ConversionSupported<T>::value,
detail::InvalidConversion<JsonVariantConst, T>>::type
as() const;
// Returns true if the value is of the specified type.
// https://arduinojson.org/v7/api/jsonvariantconst/is/
template <typename T>
typename detail::enable_if<!detail::is_same<T, char*>::value &&
!detail::is_same<T, char>::value,
bool>::type
is() const {
typename detail::enable_if<ConversionSupported<T>::value, bool>::type is()
const {
return Converter<T>::checkJson(*this);
}
// Always returns false for the unsupported types.
// https://arduinojson.org/v7/api/jsonvariantconst/is/
template <typename T>
typename detail::enable_if<!ConversionSupported<T>::value, bool>::type is()
const {
return false;
}
template <typename T>
operator T() const {
return as<T>();

View File

@ -46,16 +46,7 @@ class VariantRefBase : public VariantTag {
// Casts the value to the specified type.
// https://arduinojson.org/v7/api/jsonvariant/as/
template <typename T>
typename enable_if<!ConverterNeedsWriteableRef<T>::value, T>::type as()
const {
return Converter<T>::fromJson(getVariantConst());
}
// Casts the value to the specified type.
// https://arduinojson.org/v7/api/jsonvariant/as/
template <typename T>
typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type as() const;
T as() const;
template <typename T,
typename = typename enable_if<!is_same<T, TDerived>::value>::type>
@ -83,18 +74,7 @@ class VariantRefBase : public VariantTag {
// Returns true if the value is of the specified type.
// https://arduinojson.org/v7/api/jsonvariant/is/
template <typename T>
FORCE_INLINE
typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
is() const;
// Returns true if the value is of the specified type.
// https://arduinojson.org/v7/api/jsonvariant/is/
template <typename T>
FORCE_INLINE
typename enable_if<!ConverterNeedsWriteableRef<T>::value, bool>::type
is() const {
return Converter<T>::checkJson(getVariantConst());
}
FORCE_INLINE bool is() const;
// Copies the specified value.
// https://arduinojson.org/v7/api/jsonvariant/set/
@ -298,6 +278,18 @@ class VariantRefBase : public VariantTag {
return ArduinoJson::JsonVariantConst(getData(), getResourceManager());
}
template <typename T>
FORCE_INLINE typename enable_if<is_same<T, JsonVariantConst>::value, T>::type
getVariant() const {
return getVariantConst();
}
template <typename T>
FORCE_INLINE typename enable_if<is_same<T, JsonVariant>::value, T>::type
getVariant() const {
return getVariant();
}
ArduinoJson::JsonVariant getOrCreateVariant() const;
};

View File

@ -17,9 +17,10 @@ inline JsonVariant VariantRefBase<TDerived>::add() const {
template <typename TDerived>
template <typename T>
inline typename enable_if<ConverterNeedsWriteableRef<T>::value, T>::type
VariantRefBase<TDerived>::as() const {
return Converter<T>::fromJson(getVariant());
inline T VariantRefBase<TDerived>::as() const {
using variant_type = // JsonVariantConst or JsonVariant?
typename function_traits<decltype(&Converter<T>::fromJson)>::arg1_type;
return Converter<T>::fromJson(getVariant<variant_type>());
}
template <typename TDerived>
@ -109,9 +110,10 @@ inline JsonVariant VariantRefBase<TDerived>::getOrCreateVariant() const {
template <typename TDerived>
template <typename T>
inline typename enable_if<ConverterNeedsWriteableRef<T>::value, bool>::type
VariantRefBase<TDerived>::is() const {
return Converter<T>::checkJson(getVariant());
inline bool VariantRefBase<TDerived>::is() const {
using variant_type = // JsonVariantConst or JsonVariant?
typename function_traits<decltype(&Converter<T>::checkJson)>::arg1_type;
return Converter<T>::checkJson(getVariant<variant_type>());
}
template <typename TDerived>