Support multimap and unordered_multimap
The previously (undocumented) implementation used non-unique keys in JSON objects. This is not strictly forbidden by the RFC but not recommended. Multiple values are now stored within an array instead.
This commit is contained in:
parent
022a174028
commit
450588af89
38
README.md
38
README.md
|
@ -49,22 +49,22 @@ For a full list of further ideas, see [TODOs.md](./TODOs.md).
|
|||
## Supported datatypes
|
||||
The following table shows the mapping of supported C++ types to supported JSON types:
|
||||
|
||||
| C++ type | JSON type |
|
||||
| ------------------------------------------------------------- |:------------:|
|
||||
| custom structures/classes | object |
|
||||
| `bool` | true/false |
|
||||
| signed and unsigned integral types | number |
|
||||
| `float` and `double` | number |
|
||||
| `enum` and `enum class` | number |
|
||||
| `std::string` | string |
|
||||
| `const char *` | string |
|
||||
| iteratable lists (`std::vector`, `std::list`, ...) | array |
|
||||
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array |
|
||||
| `std::tuple` | array |
|
||||
| `std::unique_ptr`, `std::shared_ptr` | depends/null |
|
||||
| `std::map`, `std::unordered_map` | object |
|
||||
| `std::variant` | object |
|
||||
| `JsonSerializable` | object |
|
||||
| C++ type | JSON type |
|
||||
| ---------------------------------------------------------------------------- |:------------:|
|
||||
| custom structures/classes | object |
|
||||
| `bool` | true/false |
|
||||
| signed and unsigned integral types | number |
|
||||
| `float` and `double` | number |
|
||||
| `enum` and `enum class` | number |
|
||||
| `std::string` | string |
|
||||
| `const char *` | string |
|
||||
| iteratable lists (`std::vector`, `std::list`, ...) | array |
|
||||
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array |
|
||||
| `std::tuple` | array |
|
||||
| `std::unique_ptr`, `std::shared_ptr` | depends/null |
|
||||
| `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` | object |
|
||||
| `std::variant` | object |
|
||||
| `JsonSerializable` | object |
|
||||
|
||||
### Remarks
|
||||
* Raw pointer are not supported. This prevents
|
||||
|
@ -85,7 +85,11 @@ The following table shows the mapping of supported C++ types to supported JSON t
|
|||
* It is possible to treat custom types as set/map using the macro `REFLECTIVE_RAPIDJSON_TREAT_AS_MAP_OR_HASH`,
|
||||
`REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_MAP_OR_HASH`, `REFLECTIVE_RAPIDJSON_TREAT_AS_SET` or
|
||||
`REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET`.
|
||||
* The key type of `std::map` and `std::unordered_map` must be `std::string`.
|
||||
* The key type of `std::map`, `std::unordered_map`, `std::multimap` and `std::unordered_multimap` must be
|
||||
`std::string`.
|
||||
* An array is used to represent the multiple values of an `std::multimap` and `std::unordered_multimap` (for
|
||||
consistency also when there is only one value present). This is because the JSON RFC says that
|
||||
"The names within an object SHOULD be unique".
|
||||
* An `std::variant` is represented by an object like `{"index": ..., "data": ...}` where `index` is the
|
||||
zero-based index of the alternative held by the variant and `data` the value held by the variant. The
|
||||
type of `data` is `null` for `std::monostate` and otherwise deduced as usual.
|
||||
|
|
|
@ -17,9 +17,13 @@
|
|||
#include <rapidjson/writer.h>
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
#include "./errorhandling.h"
|
||||
|
@ -213,10 +217,9 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Pushes the specified map (std::map, std::unordered_map) or multimap (std::multimap, std::unordered_multimap) to the
|
||||
* specified value.
|
||||
* \brief Pushes the specified map (std::map, std::unordered_map) to the specified value.
|
||||
*/
|
||||
template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>, IsMultiMapOrHash<Type>> * = nullptr>
|
||||
template <typename Type, Traits::EnableIfAny<IsMapOrHash<Type>> * = nullptr>
|
||||
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||
{
|
||||
value.SetObject();
|
||||
|
@ -226,6 +229,27 @@ void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Pushes the specified multimap (std::multimap, std::unordered_multimap) to the specified value.
|
||||
*/
|
||||
template <typename Type, Traits::EnableIfAny<IsMultiMapOrHash<Type>> * = nullptr>
|
||||
void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
|
||||
{
|
||||
value.SetObject();
|
||||
for (const auto &item : reflectable) {
|
||||
auto arrayValue = RAPIDJSON_NAMESPACE::Value(RAPIDJSON_NAMESPACE::Type::kArrayType);
|
||||
const auto memberName = RAPIDJSON_NAMESPACE::Value::StringRefType(item.first.data(), rapidJsonSize(item.first.size()));
|
||||
const auto existingMember = value.FindMember(memberName);
|
||||
if (existingMember != value.MemberEnd() && existingMember->value.GetType() == RAPIDJSON_NAMESPACE::Type::kArrayType) {
|
||||
arrayValue = existingMember->value;
|
||||
} else {
|
||||
value.AddMember(memberName, arrayValue, allocator);
|
||||
}
|
||||
RAPIDJSON_NAMESPACE::Value::Array array = arrayValue.GetArray();
|
||||
push(item.second, array, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
|
||||
/*!
|
||||
|
@ -686,8 +710,16 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
|
|||
}
|
||||
auto obj = value.GetObject();
|
||||
for (auto i = obj.MemberBegin(), end = obj.MemberEnd(); i != end; ++i) {
|
||||
auto insertedIterator = reflectable.insert(typename Type::value_type(i->name.GetString(), typename Type::mapped_type()));
|
||||
pull(insertedIterator->second, i->value, errors);
|
||||
if (i->value.GetType() != RAPIDJSON_NAMESPACE::kArrayType) {
|
||||
auto insertedIterator = reflectable.insert(typename Type::value_type(i->name.GetString(), typename Type::mapped_type()));
|
||||
pull(insertedIterator->second, i->value, errors);
|
||||
continue;
|
||||
}
|
||||
const auto array = i->value.GetArray();
|
||||
for (const auto &value : array) {
|
||||
auto insertedIterator = reflectable.insert(typename Type::value_type(i->name.GetString(), typename Type::mapped_type()));
|
||||
pull(insertedIterator->second, value, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ struct TestObject : public JsonSerializable<TestObject> {
|
|||
bool boolean;
|
||||
map<string, int> someMap;
|
||||
unordered_map<string, bool> someHash;
|
||||
multimap<string, int> someMultimap;
|
||||
unordered_multimap<string, int> someMultiHash;
|
||||
set<string> someSet;
|
||||
multiset<string> someMultiset;
|
||||
unordered_set<string> someUnorderedSet;
|
||||
|
@ -84,6 +86,8 @@ template <> inline void push<TestObject>(const TestObject &reflectable, Value::O
|
|||
push(reflectable.boolean, "boolean", value, allocator);
|
||||
push(reflectable.someMap, "someMap", value, allocator);
|
||||
push(reflectable.someHash, "someHash", value, allocator);
|
||||
push(reflectable.someMultimap, "someMultimap", value, allocator);
|
||||
push(reflectable.someMultiHash, "someMultiHash", value, allocator);
|
||||
push(reflectable.someSet, "someSet", value, allocator);
|
||||
push(reflectable.someMultiset, "someMultiset", value, allocator);
|
||||
push(reflectable.someUnorderedSet, "someUnorderedSet", value, allocator);
|
||||
|
@ -120,6 +124,8 @@ inline void pull<TestObject>(TestObject &reflectable, const GenericValue<UTF8<ch
|
|||
pull(reflectable.boolean, "boolean", value, errors);
|
||||
pull(reflectable.someMap, "someMap", value, errors);
|
||||
pull(reflectable.someHash, "someHash", value, errors);
|
||||
pull(reflectable.someMultimap, "someMultimap", value, errors);
|
||||
pull(reflectable.someMultiHash, "someMultiHash", value, errors);
|
||||
pull(reflectable.someSet, "someSet", value, errors);
|
||||
pull(reflectable.someMultiset, "someMultiset", value, errors);
|
||||
pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
|
||||
|
@ -270,6 +276,8 @@ void JsonReflectorTests::testSerializeSimpleObjects()
|
|||
testObj.boolean = false;
|
||||
testObj.someMap = { { "a", 1 }, { "b", 2 } };
|
||||
testObj.someHash = { { "c", true }, { "d", false } };
|
||||
testObj.someMultimap = { { "a", 1 }, { "a", 2 }, { "b", 3 } };
|
||||
testObj.someMultiHash = { { "a", 1 } };
|
||||
testObj.someSet = { "a", "b", "c" };
|
||||
testObj.someMultiset = { "a", "b", "b" };
|
||||
testObj.someUnorderedSet = { "a" };
|
||||
|
@ -278,7 +286,7 @@ void JsonReflectorTests::testSerializeSimpleObjects()
|
|||
testObj.anotherVariant = "foo";
|
||||
testObj.yetAnotherVariant = 42;
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"d\":false,\"c\":true},\"someSet\":[\"a\",\"b\",\"c\"],\"someMultiset\":[\"a\",\"b\",\"b\"],\"someUnorderedSet\":[\"a\"],\"someUnorderedMultiset\":[\"b\",\"b\",\"b\"],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}"s,
|
||||
"{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"d\":false,\"c\":true},\"someMultimap\":{\"a\":[1,2],\"b\":[3]},\"someMultiHash\":{\"a\":[1]},\"someSet\":[\"a\",\"b\",\"c\"],\"someMultiset\":[\"a\",\"b\",\"b\"],\"someUnorderedSet\":[\"a\"],\"someUnorderedMultiset\":[\"b\",\"b\",\"b\"],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}"s,
|
||||
string(testObj.toJson().GetString()));
|
||||
}
|
||||
|
||||
|
@ -296,7 +304,7 @@ void JsonReflectorTests::testSerializeNestedObjects()
|
|||
testObj.text = "test";
|
||||
testObj.boolean = false;
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}}"s,
|
||||
"{\"name\":\"nesting\",\"testObj\":{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}}"s,
|
||||
string(nestingObj.toJson().GetString()));
|
||||
|
||||
NestingArray nestingArray;
|
||||
|
@ -305,13 +313,13 @@ void JsonReflectorTests::testSerializeNestedObjects()
|
|||
nestingArray.testObjects.emplace_back(testObj);
|
||||
nestingArray.testObjects.back().number = 43;
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}},{\"number\":43,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]}"s,
|
||||
"{\"name\":\"nesting2\",\"testObjects\":[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}},{\"number\":43,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]}"s,
|
||||
string(nestingArray.toJson().GetString()));
|
||||
|
||||
vector<TestObject> nestedInVector;
|
||||
nestedInVector.emplace_back(testObj);
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
"[{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
string(JsonReflector::toJson(nestedInVector).GetString()));
|
||||
}
|
||||
|
||||
|
@ -339,7 +347,7 @@ void JsonReflectorTests::testSerializeUniquePtr()
|
|||
Writer<StringBuffer> jsonWriter(strbuf);
|
||||
doc.Accept(jsonWriter);
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
string(strbuf.GetString()));
|
||||
}
|
||||
|
||||
|
@ -367,7 +375,7 @@ void JsonReflectorTests::testSerializeSharedPtr()
|
|||
Writer<StringBuffer> jsonWriter(strbuf);
|
||||
doc.Accept(jsonWriter);
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
"[\"foo\",null,{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"bar\",\"boolean\":false,\"someMap\":{},\"someHash\":{},\"someMultimap\":{},\"someMultiHash\":{},\"someSet\":[],\"someMultiset\":[],\"someUnorderedSet\":[],\"someUnorderedMultiset\":[],\"someVariant\":{\"index\":0,\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"\"},\"yetAnotherVariant\":{\"index\":0,\"data\":\"\"}}]"s,
|
||||
string(strbuf.GetString()));
|
||||
}
|
||||
|
||||
|
@ -432,11 +440,13 @@ void JsonReflectorTests::testDeserializePrimitives()
|
|||
*/
|
||||
void JsonReflectorTests::testDeserializeSimpleObjects()
|
||||
{
|
||||
const TestObject testObj(
|
||||
TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
|
||||
"false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
|
||||
"\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"],\"someVariant\":{\"index\":0,"
|
||||
"\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}"));
|
||||
const auto testObj
|
||||
= TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":"
|
||||
"false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someMultimap\":{\"a\":[1,2],\"b\":[3]},"
|
||||
"\"someMultiHash\":{\"a\":[4,5],\"b\":[6]},"
|
||||
"\"someSet\":[\"a\",\"b\"],\"someMultiset\":["
|
||||
"\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"],\"someVariant\":{\"index\":0,"
|
||||
"\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}");
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(42, testObj.number);
|
||||
CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
|
||||
|
@ -447,6 +457,10 @@ void JsonReflectorTests::testDeserializeSimpleObjects()
|
|||
CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
|
||||
const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
|
||||
CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash);
|
||||
const multimap<string, int> expectedMultiMap{ { "a", 1 }, { "a", 2 }, { "b", 3 } };
|
||||
CPPUNIT_ASSERT_EQUAL(expectedMultiMap, testObj.someMultimap);
|
||||
const unordered_multimap<string, int> expectedUnorderedMultiMap{ { "a", 4 }, { "a", 5 }, { "b", 6 } };
|
||||
CPPUNIT_ASSERT_EQUAL(expectedUnorderedMultiMap, testObj.someMultiHash);
|
||||
CPPUNIT_ASSERT_EQUAL(set<string>({ "a", "b" }), testObj.someSet);
|
||||
CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
|
||||
CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);
|
||||
|
|
|
@ -59,7 +59,7 @@ using IsArrayOrSet = Traits::Any<Traits::All<Traits::IsIteratable<Type>, Traits:
|
|||
TreatAsSet<Type>, TreatAsMultiSet<Type>>;
|
||||
template <typename Type>
|
||||
using IsArray = Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>,
|
||||
Traits::Not<IsMapOrHash<Type>>, Traits::Not<IsSet<Type>>, Traits::Not<IsMultiSet<Type>>>;
|
||||
Traits::Not<IsMapOrHash<Type>>, Traits::Not<IsMultiMapOrHash<Type>>, Traits::Not<IsSet<Type>>, Traits::Not<IsMultiSet<Type>>>;
|
||||
template <typename Type>
|
||||
using IsIteratableExceptString = Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>;
|
||||
template <typename Type> using IsVariant = Traits::All<Traits::IsSpecializationOf<Type, std::variant>>;
|
||||
|
|
Loading…
Reference in New Issue