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:
Martchus 2019-12-27 01:40:34 +01:00
parent 022a174028
commit 450588af89
4 changed files with 84 additions and 34 deletions

View File

@ -49,22 +49,22 @@ For a full list of further ideas, see [TODOs.md](./TODOs.md).
## Supported datatypes ## Supported datatypes
The following table shows the mapping of supported C++ types to supported JSON types: The following table shows the mapping of supported C++ types to supported JSON types:
| C++ type | JSON type | | C++ type | JSON type |
| ------------------------------------------------------------- |:------------:| | ---------------------------------------------------------------------------- |:------------:|
| custom structures/classes | object | | custom structures/classes | object |
| `bool` | true/false | | `bool` | true/false |
| signed and unsigned integral types | number | | signed and unsigned integral types | number |
| `float` and `double` | number | | `float` and `double` | number |
| `enum` and `enum class` | number | | `enum` and `enum class` | number |
| `std::string` | string | | `std::string` | string |
| `const char *` | string | | `const char *` | string |
| iteratable lists (`std::vector`, `std::list`, ...) | array | | iteratable lists (`std::vector`, `std::list`, ...) | array |
| sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array | | sets (`std::set`, `std::unordered_set`, `std::multiset`, ...) | array |
| `std::tuple` | array | | `std::tuple` | array |
| `std::unique_ptr`, `std::shared_ptr` | depends/null | | `std::unique_ptr`, `std::shared_ptr` | depends/null |
| `std::map`, `std::unordered_map` | object | | `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` | object |
| `std::variant` | object | | `std::variant` | object |
| `JsonSerializable` | object | | `JsonSerializable` | object |
### Remarks ### Remarks
* Raw pointer are not supported. This prevents * 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`, * 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_MAP_OR_HASH`, `REFLECTIVE_RAPIDJSON_TREAT_AS_SET` or
`REFLECTIVE_RAPIDJSON_TREAT_AS_MULTI_SET`. `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 * 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 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. type of `data` is `null` for `std::monostate` and otherwise deduced as usual.

View File

@ -17,9 +17,13 @@
#include <rapidjson/writer.h> #include <rapidjson/writer.h>
#include <limits> #include <limits>
#include <map>
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <variant> #include <variant>
#include "./errorhandling.h" #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 * \brief Pushes the specified map (std::map, std::unordered_map) to the specified value.
* 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) void push(const Type &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{ {
value.SetObject(); 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 { namespace Detail {
/*! /*!
@ -686,8 +710,16 @@ void pull(Type &reflectable, const rapidjson::GenericValue<RAPIDJSON_NAMESPACE::
} }
auto obj = value.GetObject(); auto obj = value.GetObject();
for (auto i = obj.MemberBegin(), end = obj.MemberEnd(); i != end; ++i) { 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())); if (i->value.GetType() != RAPIDJSON_NAMESPACE::kArrayType) {
pull(insertedIterator->second, i->value, errors); 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);
}
} }
} }

View File

@ -52,6 +52,8 @@ struct TestObject : public JsonSerializable<TestObject> {
bool boolean; bool boolean;
map<string, int> someMap; map<string, int> someMap;
unordered_map<string, bool> someHash; unordered_map<string, bool> someHash;
multimap<string, int> someMultimap;
unordered_multimap<string, int> someMultiHash;
set<string> someSet; set<string> someSet;
multiset<string> someMultiset; multiset<string> someMultiset;
unordered_set<string> someUnorderedSet; 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.boolean, "boolean", value, allocator);
push(reflectable.someMap, "someMap", value, allocator); push(reflectable.someMap, "someMap", value, allocator);
push(reflectable.someHash, "someHash", 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.someSet, "someSet", value, allocator);
push(reflectable.someMultiset, "someMultiset", value, allocator); push(reflectable.someMultiset, "someMultiset", value, allocator);
push(reflectable.someUnorderedSet, "someUnorderedSet", 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.boolean, "boolean", value, errors);
pull(reflectable.someMap, "someMap", value, errors); pull(reflectable.someMap, "someMap", value, errors);
pull(reflectable.someHash, "someHash", 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.someSet, "someSet", value, errors);
pull(reflectable.someMultiset, "someMultiset", value, errors); pull(reflectable.someMultiset, "someMultiset", value, errors);
pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors); pull(reflectable.someUnorderedSet, "someUnorderedSet", value, errors);
@ -270,6 +276,8 @@ void JsonReflectorTests::testSerializeSimpleObjects()
testObj.boolean = false; testObj.boolean = false;
testObj.someMap = { { "a", 1 }, { "b", 2 } }; testObj.someMap = { { "a", 1 }, { "b", 2 } };
testObj.someHash = { { "c", true }, { "d", false } }; testObj.someHash = { { "c", true }, { "d", false } };
testObj.someMultimap = { { "a", 1 }, { "a", 2 }, { "b", 3 } };
testObj.someMultiHash = { { "a", 1 } };
testObj.someSet = { "a", "b", "c" }; testObj.someSet = { "a", "b", "c" };
testObj.someMultiset = { "a", "b", "b" }; testObj.someMultiset = { "a", "b", "b" };
testObj.someUnorderedSet = { "a" }; testObj.someUnorderedSet = { "a" };
@ -278,7 +286,7 @@ void JsonReflectorTests::testSerializeSimpleObjects()
testObj.anotherVariant = "foo"; testObj.anotherVariant = "foo";
testObj.yetAnotherVariant = 42; testObj.yetAnotherVariant = 42;
CPPUNIT_ASSERT_EQUAL( 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())); string(testObj.toJson().GetString()));
} }
@ -296,7 +304,7 @@ void JsonReflectorTests::testSerializeNestedObjects()
testObj.text = "test"; testObj.text = "test";
testObj.boolean = false; testObj.boolean = false;
CPPUNIT_ASSERT_EQUAL( 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())); string(nestingObj.toJson().GetString()));
NestingArray nestingArray; NestingArray nestingArray;
@ -305,13 +313,13 @@ void JsonReflectorTests::testSerializeNestedObjects()
nestingArray.testObjects.emplace_back(testObj); nestingArray.testObjects.emplace_back(testObj);
nestingArray.testObjects.back().number = 43; nestingArray.testObjects.back().number = 43;
CPPUNIT_ASSERT_EQUAL( 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())); string(nestingArray.toJson().GetString()));
vector<TestObject> nestedInVector; vector<TestObject> nestedInVector;
nestedInVector.emplace_back(testObj); nestedInVector.emplace_back(testObj);
CPPUNIT_ASSERT_EQUAL( 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())); string(JsonReflector::toJson(nestedInVector).GetString()));
} }
@ -339,7 +347,7 @@ void JsonReflectorTests::testSerializeUniquePtr()
Writer<StringBuffer> jsonWriter(strbuf); Writer<StringBuffer> jsonWriter(strbuf);
doc.Accept(jsonWriter); doc.Accept(jsonWriter);
CPPUNIT_ASSERT_EQUAL( 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())); string(strbuf.GetString()));
} }
@ -367,7 +375,7 @@ void JsonReflectorTests::testSerializeSharedPtr()
Writer<StringBuffer> jsonWriter(strbuf); Writer<StringBuffer> jsonWriter(strbuf);
doc.Accept(jsonWriter); doc.Accept(jsonWriter);
CPPUNIT_ASSERT_EQUAL( 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())); string(strbuf.GetString()));
} }
@ -432,11 +440,13 @@ void JsonReflectorTests::testDeserializePrimitives()
*/ */
void JsonReflectorTests::testDeserializeSimpleObjects() void JsonReflectorTests::testDeserializeSimpleObjects()
{ {
const TestObject testObj( const auto testObj
TestObject::fromJson("{\"number\":42,\"number2\":3.141592653589793,\"numbers\":[1,2,3,4],\"text\":\"test\",\"boolean\":" = 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\":[" "false,\"someMap\":{\"a\":1,\"b\":2},\"someHash\":{\"c\":true,\"d\":false},\"someMultimap\":{\"a\":[1,2],\"b\":[3]},"
"\"a\",\"a\"],\"someUnorderedSet\":[\"a\",\"b\"],\"someUnorderedMultiset\":[\"a\",\"a\"],\"someVariant\":{\"index\":0," "\"someMultiHash\":{\"a\":[4,5],\"b\":[6]},"
"\"data\":null},\"anotherVariant\":{\"index\":0,\"data\":\"foo\"},\"yetAnotherVariant\":{\"index\":1,\"data\":42}}")); "\"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(42, testObj.number);
CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2); CPPUNIT_ASSERT_EQUAL(3.141592653589793, testObj.number2);
@ -447,6 +457,10 @@ void JsonReflectorTests::testDeserializeSimpleObjects()
CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap); CPPUNIT_ASSERT_EQUAL(expectedMap, testObj.someMap);
const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } }; const unordered_map<string, bool> expectedHash{ { "c", true }, { "d", false } };
CPPUNIT_ASSERT_EQUAL(expectedHash, testObj.someHash); 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(set<string>({ "a", "b" }), testObj.someSet);
CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset); CPPUNIT_ASSERT_EQUAL(multiset<string>({ "a", "a" }), testObj.someMultiset);
CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet); CPPUNIT_ASSERT_EQUAL(unordered_set<string>({ "a", "b" }), testObj.someUnorderedSet);

View File

@ -59,7 +59,7 @@ using IsArrayOrSet = Traits::Any<Traits::All<Traits::IsIteratable<Type>, Traits:
TreatAsSet<Type>, TreatAsMultiSet<Type>>; TreatAsSet<Type>, TreatAsMultiSet<Type>>;
template <typename Type> template <typename Type>
using IsArray = Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>, 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> template <typename Type>
using IsIteratableExceptString = Traits::All<Traits::IsIteratable<Type>, Traits::Not<Traits::IsSpecializationOf<Type, std::basic_string>>>; 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>>; template <typename Type> using IsVariant = Traits::All<Traits::IsSpecializationOf<Type, std::variant>>;