Implement VariantRefBase with a CRTP

This commit is contained in:
Benoit Blanchon
2022-11-26 18:34:25 +01:00
parent 21db92af47
commit 0f85a55cac
15 changed files with 153 additions and 199 deletions

View File

@ -9,7 +9,6 @@ HEAD
* Fix comparison operators for `JsonArray`, `JsonArrayConst`, `JsonObject`, and `JsonObjectConst`
* Fix lax parsing of `true`, `false`, and `null` (issue #1781)
* Remove undocumented `accept()` functions
* Remove undocumented `ElementProxy` and `MemberProxy` classes
* Rename `addElement()` to `add()`
* Remove `getElement()`, `getOrAddElement()`, `getMember()`, and `getOrAddMember()`
* Remove undocumented `JsonDocument::data()` and `JsonDocument::memoryPool()`

View File

@ -5,9 +5,7 @@
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
typedef VariantProxy<ElementDataSource<JsonDocument&> > ElementProxy;
typedef ARDUINOJSON_NAMESPACE::ElementProxy<JsonDocument&> ElementProxy;
TEST_CASE("ElementProxy::add()") {
DynamicJsonDocument doc(4096);

View File

@ -5,9 +5,8 @@
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
typedef VariantProxy<MemberDataSource<JsonDocument&, const char*> > MemberProxy;
typedef ARDUINOJSON_NAMESPACE::MemberProxy<JsonDocument&, const char*>
MemberProxy;
TEST_CASE("MemberProxy::add()") {
DynamicJsonDocument doc(4096);

View File

@ -184,13 +184,12 @@ TEST_CASE("Polyfills/type_traits") {
CHECK((is_convertible<VariantRef, JsonVariantConst>::value == true));
CHECK((is_convertible<VariantConstRef, JsonVariantConst>::value == true));
CHECK((is_convertible<ArrayRef, JsonVariantConst>::value == true));
CHECK((is_convertible<VariantProxy<ElementDataSource<ArrayRef> >,
JsonVariantConst>::value == true));
CHECK((is_convertible<ElementProxy<ArrayRef>, JsonVariantConst>::value ==
true));
CHECK((is_convertible<ArrayConstRef, JsonVariantConst>::value == true));
CHECK((is_convertible<ObjectRef, JsonVariantConst>::value == true));
CHECK(
(is_convertible<VariantProxy<MemberDataSource<ObjectRef, const char*> >,
JsonVariantConst>::value == true));
CHECK((is_convertible<MemberProxy<ObjectRef, const char*>,
JsonVariantConst>::value == true));
CHECK((is_convertible<ObjectConstRef, JsonVariantConst>::value == true));
CHECK(
(is_convertible<DynamicJsonDocument, JsonVariantConst>::value == true));

View File

@ -13,21 +13,20 @@ inline ObjectRef ArrayRef::createNestedObject() const {
return add().to<ObjectRef>();
}
template <typename TDataSource>
inline ArrayRef VariantRefBase<TDataSource>::createNestedArray() const {
template <typename TDerived>
inline ArrayRef VariantRefBase<TDerived>::createNestedArray() const {
return add().template to<ArrayRef>();
}
template <typename TDataSource>
inline ObjectRef VariantRefBase<TDataSource>::createNestedObject() const {
template <typename TDerived>
inline ObjectRef VariantRefBase<TDerived>::createNestedObject() const {
return add().template to<ObjectRef>();
}
template <typename TDataSource>
inline VariantProxy<ElementDataSource<VariantRefBase<TDataSource> > >
VariantRefBase<TDataSource>::operator[](size_t index) const {
return VariantProxy<ElementDataSource<VariantRefBase<TDataSource> > >(
ElementDataSource<VariantRefBase<TDataSource> >(*this, index));
template <typename TDerived>
inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[](
size_t index) const {
return ElementProxy<TDerived>(derived(), index);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -184,10 +184,8 @@ class ArrayRef : public ArrayRefBase<CollectionData>,
}
// Returns the element at specified index if the variant is an array.
FORCE_INLINE VariantProxy<ElementDataSource<ArrayRef> > operator[](
size_t index) const {
return VariantProxy<ElementDataSource<ArrayRef> >(
ElementDataSource<ArrayRef>(*this, index));
FORCE_INLINE ElementProxy<ArrayRef> operator[](size_t index) const {
return ElementProxy<ArrayRef>(*this, index);
}
FORCE_INLINE ObjectRef createNestedObject() const;

View File

@ -4,16 +4,40 @@
#pragma once
#include <ArduinoJson/Variant/VariantProxy.hpp>
#include <ArduinoJson/Variant/VariantRefBase.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <typename TUpstream>
class ElementDataSource {
class ElementProxy : public VariantRefBase<ElementProxy<TUpstream> >,
public VariantOperators<ElementProxy<TUpstream> > {
friend class VariantAttorney;
public:
ElementDataSource(TUpstream upstream, size_t index)
ElementProxy(TUpstream upstream, size_t index)
: _upstream(upstream), _index(index) {}
ElementProxy(const ElementProxy& src)
: _upstream(src._upstream), _index(src._index) {}
FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE ElementProxy& operator=(const T& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE ElementProxy& operator=(T* src) {
this->set(src);
return *this;
}
private:
FORCE_INLINE MemoryPool* getPool() const {
return VariantAttorney::getPool(_upstream);
}
@ -27,12 +51,6 @@ class ElementDataSource {
_index, VariantAttorney::getPool(_upstream));
}
private:
#if defined _MSC_VER && _MSC_VER <= 1800 // Visual Studio 2013 or below
// Prevent "assignment operator could not be generated"
ElementDataSource& operator=(const ElementDataSource&);
#endif
TUpstream _upstream;
size_t _index;
};

View File

@ -139,24 +139,20 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
// operator[](const std::string&)
// operator[](const String&)
template <typename TString>
FORCE_INLINE typename enable_if<
IsString<TString>::value,
VariantProxy<MemberDataSource<JsonDocument&, TString> > >::type
FORCE_INLINE typename enable_if<IsString<TString>::value,
MemberProxy<JsonDocument&, TString> >::type
operator[](const TString& key) {
return VariantProxy<MemberDataSource<JsonDocument&, TString> >(
MemberDataSource<JsonDocument&, TString>(*this, key));
return MemberProxy<JsonDocument&, TString>(*this, key);
}
// operator[](char*)
// operator[](const char*)
// operator[](const __FlashStringHelper*)
template <typename TChar>
FORCE_INLINE typename enable_if<
IsString<TChar*>::value,
VariantProxy<MemberDataSource<JsonDocument&, TChar*> > >::type
FORCE_INLINE typename enable_if<IsString<TChar*>::value,
MemberProxy<JsonDocument&, TChar*> >::type
operator[](TChar* key) {
return VariantProxy<MemberDataSource<JsonDocument&, TChar*> >(
MemberDataSource<JsonDocument&, TChar*>(*this, key));
return MemberProxy<JsonDocument&, TChar*>(*this, key);
}
// operator[](const std::string&) const
@ -178,10 +174,8 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
return VariantConstRef(_data.getMember(adaptString(key)));
}
FORCE_INLINE VariantProxy<ElementDataSource<JsonDocument&> > operator[](
size_t index) {
return VariantProxy<ElementDataSource<JsonDocument&> >(
ElementDataSource<JsonDocument&>(*this, index));
FORCE_INLINE ElementProxy<JsonDocument&> operator[](size_t index) {
return ElementProxy<JsonDocument&>(*this, index);
}
FORCE_INLINE VariantConstRef operator[](size_t index) const {

View File

@ -4,16 +4,41 @@
#pragma once
#include <ArduinoJson/Variant/VariantProxy.hpp>
#include <ArduinoJson/Variant/VariantRefBase.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <typename TUpstream, typename TStringRef>
class MemberDataSource {
class MemberProxy
: public VariantRefBase<MemberProxy<TUpstream, TStringRef> >,
public VariantOperators<MemberProxy<TUpstream, TStringRef> > {
friend class VariantAttorney;
public:
FORCE_INLINE MemberDataSource(TUpstream upstream, TStringRef key)
FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key)
: _upstream(upstream), _key(key) {}
MemberProxy(const MemberProxy& src)
: _upstream(src._upstream), _key(src._key) {}
FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE MemberProxy& operator=(const T& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE MemberProxy& operator=(T* src) {
this->set(src);
return *this;
}
private:
FORCE_INLINE MemoryPool* getPool() const {
return VariantAttorney::getPool(_upstream);
}
@ -30,11 +55,6 @@ class MemberDataSource {
}
private:
#if defined _MSC_VER && _MSC_VER <= 1800 // Visual Studio 2013 or below
// Prevent "assignment operator could not be generated"
MemberDataSource& operator=(const MemberDataSource&);
#endif
TUpstream _upstream;
TStringRef _key;
};

View File

@ -19,68 +19,63 @@ inline ArrayRef ObjectRef::createNestedArray(TChar* key) const {
return operator[](key).template to<ArrayRef>();
}
template <typename TDataSource>
template <typename TDerived>
template <typename TString>
inline ArrayRef VariantRefBase<TDataSource>::createNestedArray(
inline ArrayRef VariantRefBase<TDerived>::createNestedArray(
const TString& key) const {
return operator[](key).template to<ArrayRef>();
}
template <typename TDataSource>
template <typename TDerived>
template <typename TChar>
inline ArrayRef VariantRefBase<TDataSource>::createNestedArray(
TChar* key) const {
inline ArrayRef VariantRefBase<TDerived>::createNestedArray(TChar* key) const {
return operator[](key).template to<ArrayRef>();
}
template <typename TDataSource>
template <typename TDerived>
template <typename TString>
inline ObjectRef VariantRefBase<TDataSource>::createNestedObject(
inline ObjectRef VariantRefBase<TDerived>::createNestedObject(
const TString& key) const {
return operator[](key).template to<ObjectRef>();
}
template <typename TDataSource>
template <typename TDerived>
template <typename TChar>
inline ObjectRef VariantRefBase<TDataSource>::createNestedObject(
inline ObjectRef VariantRefBase<TDerived>::createNestedObject(
TChar* key) const {
return operator[](key).template to<ObjectRef>();
}
template <typename TDataSource>
template <typename TDerived>
template <typename TString>
inline typename enable_if<IsString<TString>::value, bool>::type
VariantRefBase<TDataSource>::containsKey(const TString& key) const {
return variantGetMember(VariantAttorney::getData(*this), adaptString(key)) !=
0;
VariantRefBase<TDerived>::containsKey(const TString& key) const {
return variantGetMember(VariantAttorney::getData(derived()),
adaptString(key)) != 0;
}
template <typename TDataSource>
template <typename TDerived>
template <typename TChar>
inline typename enable_if<IsString<TChar*>::value, bool>::type
VariantRefBase<TDataSource>::containsKey(TChar* key) const {
return variantGetMember(VariantAttorney::getData(*this), adaptString(key)) !=
0;
VariantRefBase<TDerived>::containsKey(TChar* key) const {
return variantGetMember(VariantAttorney::getData(derived()),
adaptString(key)) != 0;
}
template <typename TDataSource>
template <typename TDerived>
template <typename TString>
inline typename enable_if<IsString<TString*>::value,
VariantProxy<MemberDataSource<
VariantRefBase<TDataSource>, TString*> > >::type
VariantRefBase<TDataSource>::operator[](TString* key) const {
return VariantProxy<MemberDataSource<VariantRefBase, TString*> >(
MemberDataSource<VariantRefBase, TString*>(*this, key));
MemberProxy<TDerived, TString*> >::type
VariantRefBase<TDerived>::operator[](TString* key) const {
return MemberProxy<TDerived, TString*>(derived(), key);
}
template <typename TDataSource>
template <typename TDerived>
template <typename TString>
inline typename enable_if<IsString<TString>::value,
VariantProxy<MemberDataSource<
VariantRefBase<TDataSource>, TString> > >::type
VariantRefBase<TDataSource>::operator[](const TString& key) const {
return VariantProxy<MemberDataSource<VariantRefBase, TString> >(
MemberDataSource<VariantRefBase, TString>(*this, key));
MemberProxy<TDerived, TString> >::type
VariantRefBase<TDerived>::operator[](const TString& key) const {
return MemberProxy<TDerived, TString>(derived(), key);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -176,21 +176,17 @@ class ObjectRef : public ObjectRefBase<CollectionData>,
}
template <typename TString>
FORCE_INLINE typename enable_if<
IsString<TString>::value,
VariantProxy<MemberDataSource<ObjectRef, TString> > >::type
FORCE_INLINE typename enable_if<IsString<TString>::value,
MemberProxy<ObjectRef, TString> >::type
operator[](const TString& key) const {
return VariantProxy<MemberDataSource<ObjectRef, TString> >(
MemberDataSource<ObjectRef, TString>(*this, key));
return MemberProxy<ObjectRef, TString>(*this, key);
}
template <typename TChar>
FORCE_INLINE typename enable_if<
IsString<TChar*>::value,
VariantProxy<MemberDataSource<ObjectRef, TChar*> > >::type
FORCE_INLINE typename enable_if<IsString<TChar*>::value,
MemberProxy<ObjectRef, TChar*> >::type
operator[](TChar* key) const {
return VariantProxy<MemberDataSource<ObjectRef, TChar*> >(
MemberDataSource<ObjectRef, TChar*>(*this, key));
return MemberProxy<ObjectRef, TChar*>(*this, key);
}
FORCE_INLINE void remove(iterator it) const {

View File

@ -104,52 +104,51 @@ inline bool VariantData::copyFrom(const VariantData& src, MemoryPool* pool) {
}
}
template <typename TSource>
inline VariantRef VariantRefBase<TSource>::add() const {
template <typename TDerived>
inline VariantRef VariantRefBase<TDerived>::add() const {
return VariantRef(getPool(), variantAddElement(getOrCreateData(), getPool()));
}
template <typename TSource>
inline VariantRef VariantRefBase<TSource>::getVariant() const {
template <typename TDerived>
inline VariantRef VariantRefBase<TDerived>::getVariant() const {
return VariantRef(getPool(), getData());
}
template <typename TSource>
inline VariantRef VariantRefBase<TSource>::getOrCreateVariant() const {
template <typename TDerived>
inline VariantRef VariantRefBase<TDerived>::getOrCreateVariant() const {
return VariantRef(getPool(), getOrCreateData());
}
template <typename TSource>
template <typename TDerived>
template <typename T>
inline typename enable_if<is_same<T, ArrayRef>::value, ArrayRef>::type
VariantRefBase<TSource>::to() const {
VariantRefBase<TDerived>::to() const {
return ArrayRef(getPool(), variantToArray(getOrCreateData()));
}
template <typename TSource>
template <typename TDerived>
template <typename T>
typename enable_if<is_same<T, ObjectRef>::value, ObjectRef>::type
VariantRefBase<TSource>::to() const {
VariantRefBase<TDerived>::to() const {
return ObjectRef(getPool(), variantToObject(getOrCreateData()));
}
template <typename TSource>
template <typename TDerived>
template <typename T>
typename enable_if<is_same<T, VariantRef>::value, VariantRef>::type
VariantRefBase<TSource>::to() const {
VariantRefBase<TDerived>::to() const {
variantSetNull(getOrCreateData());
return *this;
}
// Out of class definition to avoid #1560
template <typename TSource>
inline bool VariantRefBase<TSource>::set(char value) const {
template <typename TDerived>
inline bool VariantRefBase<TDerived>::set(char value) const {
return set(static_cast<signed char>(value));
}
template <typename TDataSource>
inline void convertToJson(const VariantRefBase<TDataSource>& src,
VariantRef dst) {
template <typename TDerived>
inline void convertToJson(const VariantRefBase<TDerived>& src, VariantRef dst) {
dst.set(src.template as<VariantConstRef>());
}

View File

@ -1,51 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2022, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantRefBase.hpp>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4522)
#endif
namespace ARDUINOJSON_NAMESPACE {
template <typename TDataSource>
class VariantProxy : public VariantRefBase<TDataSource>,
public VariantOperators<VariantProxy<TDataSource> > {
public:
explicit FORCE_INLINE VariantProxy(TDataSource source)
: VariantRefBase<TDataSource>(source) {}
// Copy-constructor required because of user-defined copy-assignment
// operator
FORCE_INLINE VariantProxy(const VariantProxy& src)
: VariantRefBase<TDataSource>(src) {}
FORCE_INLINE VariantProxy& operator=(const VariantProxy& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE VariantProxy& operator=(const T& src) {
this->set(src);
return *this;
}
template <typename T>
FORCE_INLINE VariantProxy& operator=(T* src) {
this->set(src);
return *this;
}
};
} // namespace ARDUINOJSON_NAMESPACE
#ifdef _MSC_VER
# pragma warning(pop)
#endif

View File

@ -8,13 +8,16 @@
namespace ARDUINOJSON_NAMESPACE {
class VariantDataSource {
class VariantRef : public VariantRefBase<VariantRef>,
public VariantOperators<VariantRef> {
friend class VariantAttorney;
public:
VariantDataSource() : _data(0), _pool(0) {}
VariantRef() : _data(0), _pool(0) {}
VariantDataSource(MemoryPool* pool, VariantData* data)
: _data(data), _pool(pool) {}
VariantRef(MemoryPool* pool, VariantData* data) : _data(data), _pool(pool) {}
private:
FORCE_INLINE MemoryPool* getPool() const {
return _pool;
}
@ -27,20 +30,10 @@ class VariantDataSource {
return _data;
}
private:
VariantData* _data;
MemoryPool* _pool;
};
class VariantRef : public VariantRefBase<VariantDataSource>,
public VariantOperators<VariantRef> {
public:
VariantRef() : VariantRefBase<VariantDataSource>(VariantDataSource()) {}
VariantRef(MemoryPool* pool, VariantData* data)
: VariantRefBase<VariantDataSource>(VariantDataSource(pool, data)) {}
};
template <>
struct Converter<VariantRef> : private VariantAttorney {
static void toJson(VariantRef src, VariantRef dst) {

View File

@ -15,21 +15,16 @@ namespace ARDUINOJSON_NAMESPACE {
class VariantRef;
template <typename>
class ElementDataSource;
class ElementProxy;
template <typename, typename>
class MemberDataSource;
class MemberProxy;
template <typename>
class VariantProxy;
template <typename TDataSource>
template <typename TDerived>
class VariantRefBase : public VariantTag {
friend class VariantAttorney;
public:
explicit FORCE_INLINE VariantRefBase(TDataSource source) : _source(source) {}
FORCE_INLINE void clear() const {
variantSetNull(getData());
}
@ -206,8 +201,7 @@ class VariantRefBase : public VariantTag {
FORCE_INLINE ArrayRef createNestedArray() const;
FORCE_INLINE ObjectRef createNestedObject() const;
FORCE_INLINE VariantProxy<ElementDataSource<VariantRefBase> > operator[](
size_t index) const;
FORCE_INLINE ElementProxy<TDerived> operator[](size_t index) const;
template <typename TString>
FORCE_INLINE typename enable_if<IsString<TString>::value, bool>::type
@ -218,15 +212,13 @@ class VariantRefBase : public VariantTag {
containsKey(TChar* key) const;
template <typename TString>
FORCE_INLINE typename enable_if<
IsString<TString>::value,
VariantProxy<MemberDataSource<VariantRefBase, TString> > >::type
FORCE_INLINE typename enable_if<IsString<TString>::value,
MemberProxy<TDerived, TString> >::type
operator[](const TString& key) const;
template <typename TChar>
FORCE_INLINE typename enable_if<
IsString<TChar*>::value,
VariantProxy<MemberDataSource<VariantRefBase, TChar*> > >::type
FORCE_INLINE typename enable_if<IsString<TChar*>::value,
MemberProxy<TDerived, TChar*> >::type
operator[](TChar* key) const;
template <typename TString>
@ -241,17 +233,25 @@ class VariantRefBase : public VariantTag {
template <typename TChar>
ObjectRef createNestedObject(TChar* key) const;
protected:
private:
TDerived& derived() {
return static_cast<TDerived&>(*this);
}
const TDerived& derived() const {
return static_cast<const TDerived&>(*this);
}
FORCE_INLINE MemoryPool* getPool() const {
return _source.getPool();
return VariantAttorney::getPool(derived());
}
FORCE_INLINE VariantData* getData() const {
return _source.getData();
return VariantAttorney::getData(derived());
}
FORCE_INLINE VariantData* getOrCreateData() const {
return _source.getOrCreateData();
return VariantAttorney::getOrCreateData(derived());
}
private:
@ -262,8 +262,6 @@ class VariantRefBase : public VariantTag {
}
FORCE_INLINE VariantRef getOrCreateVariant() const;
TDataSource _source;
};
} // namespace ARDUINOJSON_NAMESPACE