Merge branch 'develop' into feature/allocators
diff --git a/Makefile b/Makefile
index 56e46d1..1b8b83d 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@
FLAGS = -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal
# build unit tests (TODO: Does this want its own makefile?)
-json_unit: test/src/unit.cpp src/json.hpp test/src/catch.hpp
+json_unit: test/src/unit.cpp src/json.hpp test/src/catch.hpp test/src/StlAllocatorMock.hpp test/src/StlAllocatorMockState.hpp
$(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src -I test $< $(LDFLAGS) -o $@
@@ -76,7 +76,9 @@
--indent-col1-comments --pad-oper --pad-header --align-pointer=type \
--align-reference=type --add-brackets --convert-tabs --close-templates \
--lineend=linux --preserve-date --suffix=none --formatted \
- src/json.hpp src/json.hpp.re2c test/src/unit.cpp test/src/fuzz.cpp benchmarks/benchmarks.cpp doc/examples/*.cpp
+ src/json.hpp src/json.hpp.re2c test/src/unit.cpp test/src/fuzz.cpp \
+ test/src/StlAllocatorMock.hpp test/src/StlAllocatorMockState.hpp \
+ benchmarks/benchmarks.cpp doc/examples/*.cpp
##########################################################################
diff --git a/README.md b/README.md
index 24f6d7a..a62835c 100644
--- a/README.md
+++ b/README.md
@@ -505,7 +505,7 @@
$ ./json_unit "*"
===============================================================================
-All tests passed (5568718 assertions in 32 test cases)
+All tests passed (5568724 assertions in 33 test cases)
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/src/json.hpp b/src/json.hpp
index fa14b0e..b3115b4 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -269,9 +269,9 @@
/*!
@brief returns the allocator associated with the container
*/
- static allocator_type get_allocator()
+ allocator_type get_allocator() const
{
- return allocator_type();
+ return m_allocator;
}
@@ -373,6 +373,9 @@
AllocatorType<std::pair<const StringType,
basic_json>>>;
+ static_assert(std::is_member_function_pointer<decltype(&object_t::get_allocator)>::value,
+ "object type is not allocator aware");
+
/*!
@brief a type for an array
@@ -419,6 +422,9 @@
*/
using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+ static_assert(std::is_member_function_pointer<decltype(&array_t::get_allocator)>::value,
+ "array type is not allocator aware");
+
/*!
@brief a type for a string
@@ -466,6 +472,9 @@
*/
using string_t = StringType;
+ static_assert(std::is_member_function_pointer<decltype(&string_t::get_allocator)>::value,
+ "string type is not allocator aware");
+
/*!
@brief a type for a boolean
@@ -739,15 +748,17 @@
/// helper for exception-safe object creation
template<typename T, typename... Args>
- static T* create(Args&& ... args)
+ static T* create(const allocator_type& allocator, Args&& ... args)
{
- AllocatorType<T> alloc;
+ AllocatorType<T> alloc(allocator);
auto deleter = [&](T * object)
{
- alloc.deallocate(object, 1);
+ std::allocator_traits<AllocatorType<T>>::deallocate(alloc, object, 1);
};
- std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
- alloc.construct(object.get(), std::forward<Args>(args)...);
+ std::unique_ptr<T, decltype(deleter)> object(std::allocator_traits<AllocatorType<T>>::allocate(
+ alloc, 1), deleter);
+ std::allocator_traits<AllocatorType<T>>::construct(alloc, object.get(),
+ std::forward<Args>(args)...);
return object.release();
}
@@ -790,25 +801,25 @@
/// constructor for numbers (floating-point)
json_value(number_float_t v) noexcept : number_float(v) {}
/// constructor for empty values of a given type
- json_value(value_t t)
+ json_value(const allocator_type& allocator, value_t t)
{
switch (t)
{
case value_t::object:
{
- object = create<object_t>();
+ object = create<object_t>(allocator, allocator);
break;
}
case value_t::array:
{
- array = create<array_t>();
+ array = create<array_t>(allocator, allocator);
break;
}
case value_t::string:
{
- string = create<string_t>("");
+ string = create<string_t>(allocator, "");
break;
}
@@ -845,20 +856,35 @@
/// constructor for strings
json_value(const string_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for strings
+ json_value(const allocator_type& allocator, const string_t& value)
{
- string = create<string_t>(value);
+ string = create<string_t>(allocator, value);
}
/// constructor for objects
json_value(const object_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for objects
+ json_value(const allocator_type& allocator, const object_t& value)
{
- object = create<object_t>(value);
+ object = create<object_t>(allocator, value, allocator);
}
/// constructor for arrays
json_value(const array_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for arrays
+ json_value(const allocator_type& allocator, const array_t& value)
{
- array = create<array_t>(value);
+ array = create<array_t>(allocator, value, allocator);
}
};
@@ -969,6 +995,7 @@
array | `[]`
@param[in] value_type the type of the value to create
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -992,8 +1019,11 @@
@since version 1.0.0
*/
- basic_json(const value_t value_type)
- : m_type(value_type), m_value(value_type)
+ basic_json(const value_t value_type,
+ const allocator_type& allocator = {})
+ : m_type(value_type),
+ m_value(allocator, value_type),
+ m_allocator(allocator)
{}
/*!
@@ -1002,6 +1032,8 @@
Create a `null` JSON value. This is the implicit version of the `null`
value constructor as it takes no parameters.
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@complexity Constant.
@exceptionsafety No-throw guarantee: this constructor never throws
@@ -1020,7 +1052,9 @@
@since version 1.0.0
*/
- basic_json() = default;
+ basic_json(const allocator_type& allocator = {}) noexcept
+ : m_allocator(allocator)
+ {}
/*!
@brief create a null object (explicitly)
@@ -1031,6 +1065,8 @@
The passed null pointer itself is not read -- it is only used to choose
the right constructor.
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@complexity Constant.
@exceptionsafety No-throw guarantee: this constructor never throws
@@ -1044,8 +1080,9 @@
@since version 1.0.0
*/
- basic_json(std::nullptr_t) noexcept
- : basic_json(value_t::null)
+ basic_json(std::nullptr_t,
+ const allocator_type& allocator = {}) noexcept
+ : basic_json(value_t::null, allocator)
{}
/*!
@@ -1054,6 +1091,7 @@
Create an object JSON value with a given content.
@param[in] val a value for the object
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1067,8 +1105,11 @@
@since version 1.0.0
*/
- basic_json(const object_t& val)
- : m_type(value_t::object), m_value(val)
+ basic_json(const object_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::object),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1085,6 +1126,7 @@
basic_json value can be constructed.
@param[in] val a value for the object
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1102,12 +1144,17 @@
std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
= 0>
- basic_json(const CompatibleObjectType& val)
- : m_type(value_t::object)
+ basic_json(const CompatibleObjectType& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::object),
+ m_allocator(allocator)
{
using std::begin;
using std::end;
- m_value.object = create<object_t>(begin(val), end(val));
+ m_value.object = create<object_t>(m_allocator,
+ begin(val), end(val),
+ std::less<StringType>(),
+ m_allocator);
}
/*!
@@ -1116,6 +1163,7 @@
Create an array JSON value with a given content.
@param[in] val a value for the array
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1129,8 +1177,11 @@
@since version 1.0.0
*/
- basic_json(const array_t& val)
- : m_type(value_t::array), m_value(val)
+ basic_json(const array_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1147,6 +1198,7 @@
`value_type` from which a @ref basic_json value can be constructed.
@param[in] val a value for the array
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1169,12 +1221,16 @@
not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
= 0>
- basic_json(const CompatibleArrayType& val)
- : m_type(value_t::array)
+ basic_json(const CompatibleArrayType& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_allocator(allocator)
{
using std::begin;
using std::end;
- m_value.array = create<array_t>(begin(val), end(val));
+ m_value.array = create<array_t>(m_allocator,
+ begin(val), end(val),
+ m_allocator);
}
/*!
@@ -1183,6 +1239,7 @@
Create an string JSON value with a given content.
@param[in] val a value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1198,8 +1255,11 @@
@since version 1.0.0
*/
- basic_json(const string_t& val)
- : m_type(value_t::string), m_value(val)
+ basic_json(const string_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::string),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1208,6 +1268,7 @@
Create a string JSON value with a given content.
@param[in] val a literal value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1222,8 +1283,9 @@
@since version 1.0.0
*/
- basic_json(const typename string_t::value_type* val)
- : basic_json(string_t(val))
+ basic_json(const typename string_t::value_type* val,
+ const allocator_type& allocator = {})
+ : basic_json(string_t(val, allocator), allocator)
{}
/*!
@@ -1232,6 +1294,7 @@
Create a string JSON value with a given content.
@param[in] val a value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@tparam CompatibleStringType an string type which is compatible to @ref
string_t, for instance `std::string`.
@@ -1253,8 +1316,9 @@
std::enable_if<
std::is_constructible<string_t, CompatibleStringType>::value, int>::type
= 0>
- basic_json(const CompatibleStringType& val)
- : basic_json(string_t(val))
+ basic_json(const CompatibleStringType& val,
+ const allocator_type& allocator = {})
+ : basic_json(string_t(val, allocator), allocator)
{}
/*!
@@ -1263,6 +1327,7 @@
Creates a JSON boolean type from a given value.
@param[in] val a boolean value to store
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1271,8 +1336,11 @@
@since version 1.0.0
*/
- basic_json(boolean_t val) noexcept
- : m_type(value_t::boolean), m_value(val)
+ basic_json(boolean_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::boolean),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1286,6 +1354,7 @@
the helper type @a T is not visible in this constructor's interface.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1304,8 +1373,11 @@
and std::is_same<T, number_integer_t>::value
, int>::type
= 0>
- basic_json(const number_integer_t val) noexcept
- : m_type(value_t::number_integer), m_value(val)
+ basic_json(const number_integer_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_integer),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1314,6 +1386,7 @@
Create an integer number JSON value with a given content.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note This constructor allows to pass enums directly to a constructor. As
C++ has no way of specifying the type of an anonymous enum explicitly, we
@@ -1333,9 +1406,11 @@
@since version 1.0.0
*/
- basic_json(const int val) noexcept
+ basic_json(const int val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
+ m_value(static_cast<number_integer_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1350,6 +1425,7 @@
`long`, and `short`.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1370,9 +1446,11 @@
std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
CompatibleNumberIntegerType>::type
= 0>
- basic_json(const CompatibleNumberIntegerType val) noexcept
+ basic_json(const CompatibleNumberIntegerType val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
+ m_value(static_cast<number_integer_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1384,6 +1462,7 @@
(not visible in) the interface.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1398,8 +1477,11 @@
and std::is_same<T, number_unsigned_t>::value
, int>::type
= 0>
- basic_json(const number_unsigned_t val) noexcept
- : m_type(value_t::number_unsigned), m_value(val)
+ basic_json(const number_unsigned_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_unsigned),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1414,6 +1496,7 @@
`uint32_t`, or `unsigned short`.
@param[in] val an unsigned integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1429,9 +1512,11 @@
not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type
= 0>
- basic_json(const CompatibleNumberUnsignedType val) noexcept
+ basic_json(const CompatibleNumberUnsignedType val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_unsigned),
- m_value(static_cast<number_unsigned_t>(val))
+ m_value(static_cast<number_unsigned_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1440,6 +1525,7 @@
Create a floating-point number JSON value with a given content.
@param[in] val a floating-point value to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
disallows NaN values:
@@ -1458,8 +1544,11 @@
@since version 1.0.0
*/
- basic_json(const number_float_t val) noexcept
- : m_type(value_t::number_float), m_value(val)
+ basic_json(const number_float_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_float),
+ m_value(val),
+ m_allocator(allocator)
{
// replace infinity and NAN by null
if (not std::isfinite(val))
@@ -1481,6 +1570,7 @@
or `double`.
@param[in] val a floating-point to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
disallows NaN values:
@@ -1505,8 +1595,9 @@
std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
std::is_floating_point<CompatibleNumberFloatType>::value>::type
>
- basic_json(const CompatibleNumberFloatType val) noexcept
- : basic_json(number_float_t(val))
+ basic_json(const CompatibleNumberFloatType val,
+ const allocator_type& allocator = {}) noexcept
+ : basic_json(number_float_t(val), allocator)
{}
/*!
@@ -1561,6 +1652,8 @@
value_t::array and @ref value_t::object are valid); when @a type_deduction
is set to `true`, this parameter has no effect
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@throw std::domain_error if @a type_deduction is `false`, @a manual_type
is `value_t::object`, but @a init contains an element which is not a pair
whose first element is a string; example: `"cannot create object from
@@ -1580,7 +1673,9 @@
*/
basic_json(std::initializer_list<basic_json> init,
bool type_deduction = true,
- value_t manual_type = value_t::array)
+ value_t manual_type = value_t::array,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
// check if each element is an array with two elements whose first
// element is a string
@@ -1610,7 +1705,7 @@
{
// the initializer list is a list of pairs -> create object
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
assert(m_value.object != nullptr);
@@ -1623,7 +1718,7 @@
{
// the initializer list describes an array -> create array
m_type = value_t::array;
- m_value.array = create<array_t>(init);
+ m_value.array = create<array_t>(m_allocator, init, m_allocator);
}
}
@@ -1646,6 +1741,7 @@
@param[in] init initializer list with JSON values to create an array from
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return JSON array value
@@ -1662,9 +1758,10 @@
@since version 1.0.0
*/
static basic_json array(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ std::initializer_list<basic_json>(),
+ const allocator_type& allocator = {})
{
- return basic_json(init, false, value_t::array);
+ return basic_json(init, false, value_t::array, allocator);
}
/*!
@@ -1682,6 +1779,7 @@
value_t).
@param[in] init initializer list to create an object from (optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return JSON object value
@@ -1702,9 +1800,10 @@
@since version 1.0.0
*/
static basic_json object(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ std::initializer_list<basic_json>(),
+ const allocator_type& allocator = {})
{
- return basic_json(init, false, value_t::object);
+ return basic_json(init, false, value_t::object, allocator);
}
/*!
@@ -1716,6 +1815,7 @@
@param[in] cnt the number of JSON copies of @a val to create
@param[in] val the JSON value to copy
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in @a cnt.
@@ -1725,10 +1825,12 @@
@since version 1.0.0
*/
- basic_json(size_type cnt, const basic_json& val)
- : m_type(value_t::array)
+ basic_json(size_type cnt, const basic_json& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_allocator(allocator)
{
- m_value.array = create<array_t>(cnt, val);
+ m_value.array = create<array_t>(m_allocator, cnt, val, m_allocator);
}
/*!
@@ -1748,6 +1850,7 @@
@param[in] first begin of the range to copy from (included)
@param[in] last end of the range to copy from (excluded)
+ @param[in] allocator the allocator to use to construct objects (optional)
@throw std::domain_error if iterators are not compatible; that is, do not
belong to the same JSON value; example: `"iterators are not compatible"`
@@ -1771,7 +1874,10 @@
std::is_same<InputIT, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+ basic_json(InputIT first, InputIT last,
+ const allocator_type& allocator = {})
+ : m_type(first.m_object->m_type),
+ m_allocator(allocator)
{
// make sure iterator fits the current value
if (first.m_object != last.m_object)
@@ -1840,13 +1946,20 @@
case value_t::object:
{
- m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+ m_value.object = create<object_t>(m_allocator,
+ first.m_it.object_iterator,
+ last.m_it.object_iterator,
+ std::less<StringType>(),
+ m_allocator);
break;
}
case value_t::array:
{
- m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+ m_value.array = create<array_t>(m_allocator,
+ first.m_it.array_iterator,
+ last.m_it.array_iterator,
+ m_allocator);
break;
}
@@ -1865,6 +1978,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@@ -1878,9 +1992,11 @@
@since version 2.0.0
*/
- explicit basic_json(std::istream& i, parser_callback_t cb = nullptr)
+ explicit basic_json(std::istream& i, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
- *this = parser(i, cb).parse();
+ *this = parser(i, cb, allocator).parse();
}
///////////////////////////////////////
@@ -1910,7 +2026,8 @@
@since version 1.0.0
*/
basic_json(const basic_json& other)
- : m_type(other.m_type)
+ : m_type(other.m_type),
+ m_allocator(other.m_allocator)
{
switch (m_type)
{
@@ -1986,7 +2103,8 @@
*/
basic_json(basic_json&& other) noexcept
: m_type(std::move(other.m_type)),
- m_value(std::move(other.m_value))
+ m_value(std::move(other.m_value)),
+ m_allocator(std::move(other.m_allocator))
{
// invalidate payload
other.m_type = value_t::null;
@@ -2050,25 +2168,25 @@
{
case value_t::object:
{
- AllocatorType<object_t> alloc;
- alloc.destroy(m_value.object);
- alloc.deallocate(m_value.object, 1);
+ AllocatorType<object_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<object_t>>::destroy(alloc, m_value.object);
+ std::allocator_traits<AllocatorType<object_t>>::deallocate(alloc, m_value.object, 1);
break;
}
case value_t::array:
{
- AllocatorType<array_t> alloc;
- alloc.destroy(m_value.array);
- alloc.deallocate(m_value.array, 1);
+ AllocatorType<array_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<array_t>>::destroy(alloc, m_value.array);
+ std::allocator_traits<AllocatorType<array_t>>::deallocate(alloc, m_value.array, 1);
break;
}
case value_t::string:
{
- AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
+ AllocatorType<string_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<string_t>>::destroy(alloc, m_value.string);
+ std::allocator_traits<AllocatorType<string_t>>::deallocate(alloc, m_value.string, 1);
break;
}
@@ -2116,7 +2234,10 @@
*/
string_t dump(const int indent = -1) const
{
- std::stringstream ss;
+ std::basic_stringstream<typename string_t::value_type,
+ typename string_t::traits_type,
+ typename string_t::allocator_type> ss;
+
// fix locale problems
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
@@ -3283,7 +3404,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value.array = create<array_t>();
+ m_value.array = create<array_t>(m_allocator, m_allocator);
}
// operator[] only works for arrays
@@ -3372,7 +3493,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value.object = create<object_t>();
+ m_value.object = create<object_t>(m_allocator, m_allocator);
}
// operator[] only works for objects
@@ -3531,7 +3652,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
}
// at only works for objects
@@ -4773,7 +4894,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value = value_t::array;
+ m_value = json_value(m_allocator, value_t::array);
}
// add element to array (move semantics)
@@ -4809,7 +4930,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value = value_t::array;
+ m_value = json_value(m_allocator, value_t::array);
}
// add element to array
@@ -4859,7 +4980,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
}
// add element to array
@@ -5750,6 +5871,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return result of the deserialization
@@ -5767,9 +5889,11 @@
@since version 1.0.0
*/
- static basic_json parse(const string_t& s, parser_callback_t cb = nullptr)
+ static basic_json parse(const string_t& s,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(s, cb).parse();
+ return parser(s, cb, allocator).parse();
}
/*!
@@ -5779,6 +5903,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return result of the deserialization
@@ -5796,17 +5921,21 @@
@since version 1.0.0
*/
- static basic_json parse(std::istream& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream& i,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@copydoc parse(std::istream&, parser_callback_t)
*/
- static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream&& i,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@@ -5834,7 +5963,7 @@
*/
friend std::istream& operator<<(basic_json& j, std::istream& i)
{
- j = parser(i).parse();
+ j = parser(i, nullptr, j.get_allocator()).parse();
return i;
}
@@ -5844,7 +5973,7 @@
*/
friend std::istream& operator>>(std::istream& i, basic_json& j)
{
- j = parser(i).parse();
+ j = parser(i, nullptr, j.get_allocator()).parse();
return i;
}
@@ -6220,6 +6349,9 @@
/// the value of the current element
json_value m_value = {};
+ /// the allocator to be used when constructing objects.
+ const allocator_type m_allocator;
+
private:
///////////////
@@ -8530,16 +8662,18 @@
{
public:
/// constructor for strings
- parser(const string_t& s, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
+ parser(const string_t& s, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(s), m_allocator(allocator)
{
// read first token
get_token();
}
/// a parser reading from an input stream
- parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& _is, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(&_is), m_allocator(allocator)
{
// read first token
get_token();
@@ -8571,7 +8705,7 @@
{
// explicitly set result to object to cope with {}
result.m_type = value_t::object;
- result.m_value = json_value(value_t::object);
+ result.m_value = json_value(m_allocator, value_t::object);
}
// read next token
@@ -8647,9 +8781,9 @@
{
if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result))))
{
- // explicitly set result to object to cope with []
+ // explicitly set result to array to cope with []
result.m_type = value_t::array;
- result.m_value = json_value(value_t::array);
+ result.m_value = json_value(m_allocator, value_t::array);
}
// read next token
@@ -8791,6 +8925,8 @@
typename lexer::token_type last_token = lexer::token_type::uninitialized;
/// the lexer
lexer m_lexer;
+ /// allocator for any objects that are created
+ const allocator_type m_allocator;
};
public:
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index dd1bda1..03c22d0 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -269,9 +269,9 @@
/*!
@brief returns the allocator associated with the container
*/
- static allocator_type get_allocator()
+ allocator_type get_allocator() const
{
- return allocator_type();
+ return m_allocator;
}
@@ -373,6 +373,9 @@
AllocatorType<std::pair<const StringType,
basic_json>>>;
+ static_assert(std::is_member_function_pointer<decltype(&object_t::get_allocator)>::value,
+ "object type is not allocator aware");
+
/*!
@brief a type for an array
@@ -419,6 +422,9 @@
*/
using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+ static_assert(std::is_member_function_pointer<decltype(&array_t::get_allocator)>::value,
+ "array type is not allocator aware");
+
/*!
@brief a type for a string
@@ -466,6 +472,9 @@
*/
using string_t = StringType;
+ static_assert(std::is_member_function_pointer<decltype(&string_t::get_allocator)>::value,
+ "string type is not allocator aware");
+
/*!
@brief a type for a boolean
@@ -739,15 +748,17 @@
/// helper for exception-safe object creation
template<typename T, typename... Args>
- static T* create(Args&& ... args)
+ static T* create(const allocator_type& allocator, Args&& ... args)
{
- AllocatorType<T> alloc;
+ AllocatorType<T> alloc(allocator);
auto deleter = [&](T * object)
{
- alloc.deallocate(object, 1);
+ std::allocator_traits<AllocatorType<T>>::deallocate(alloc, object, 1);
};
- std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
- alloc.construct(object.get(), std::forward<Args>(args)...);
+ std::unique_ptr<T, decltype(deleter)> object(std::allocator_traits<AllocatorType<T>>::allocate(
+ alloc, 1), deleter);
+ std::allocator_traits<AllocatorType<T>>::construct(alloc, object.get(),
+ std::forward<Args>(args)...);
return object.release();
}
@@ -790,25 +801,25 @@
/// constructor for numbers (floating-point)
json_value(number_float_t v) noexcept : number_float(v) {}
/// constructor for empty values of a given type
- json_value(value_t t)
+ json_value(const allocator_type& allocator, value_t t)
{
switch (t)
{
case value_t::object:
{
- object = create<object_t>();
+ object = create<object_t>(allocator, allocator);
break;
}
case value_t::array:
{
- array = create<array_t>();
+ array = create<array_t>(allocator, allocator);
break;
}
case value_t::string:
{
- string = create<string_t>("");
+ string = create<string_t>(allocator, "");
break;
}
@@ -845,20 +856,35 @@
/// constructor for strings
json_value(const string_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for strings
+ json_value(const allocator_type& allocator, const string_t& value)
{
- string = create<string_t>(value);
+ string = create<string_t>(allocator, value);
}
/// constructor for objects
json_value(const object_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for objects
+ json_value(const allocator_type& allocator, const object_t& value)
{
- object = create<object_t>(value);
+ object = create<object_t>(allocator, value, allocator);
}
/// constructor for arrays
json_value(const array_t& value)
+ : json_value(value.get_allocator(), value)
+ {}
+
+ /// constructor for arrays
+ json_value(const allocator_type& allocator, const array_t& value)
{
- array = create<array_t>(value);
+ array = create<array_t>(allocator, value, allocator);
}
};
@@ -969,6 +995,7 @@
array | `[]`
@param[in] value_type the type of the value to create
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -992,8 +1019,11 @@
@since version 1.0.0
*/
- basic_json(const value_t value_type)
- : m_type(value_type), m_value(value_type)
+ basic_json(const value_t value_type,
+ const allocator_type& allocator = {})
+ : m_type(value_type),
+ m_value(allocator, value_type),
+ m_allocator(allocator)
{}
/*!
@@ -1002,6 +1032,8 @@
Create a `null` JSON value. This is the implicit version of the `null`
value constructor as it takes no parameters.
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@complexity Constant.
@exceptionsafety No-throw guarantee: this constructor never throws
@@ -1020,7 +1052,9 @@
@since version 1.0.0
*/
- basic_json() = default;
+ basic_json(const allocator_type& allocator = {}) noexcept
+ : m_allocator(allocator)
+ {}
/*!
@brief create a null object (explicitly)
@@ -1031,6 +1065,8 @@
The passed null pointer itself is not read -- it is only used to choose
the right constructor.
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@complexity Constant.
@exceptionsafety No-throw guarantee: this constructor never throws
@@ -1044,8 +1080,9 @@
@since version 1.0.0
*/
- basic_json(std::nullptr_t) noexcept
- : basic_json(value_t::null)
+ basic_json(std::nullptr_t,
+ const allocator_type& allocator = {}) noexcept
+ : basic_json(value_t::null, allocator)
{}
/*!
@@ -1054,6 +1091,7 @@
Create an object JSON value with a given content.
@param[in] val a value for the object
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1067,8 +1105,11 @@
@since version 1.0.0
*/
- basic_json(const object_t& val)
- : m_type(value_t::object), m_value(val)
+ basic_json(const object_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::object),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1085,6 +1126,7 @@
basic_json value can be constructed.
@param[in] val a value for the object
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1102,12 +1144,17 @@
std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
= 0>
- basic_json(const CompatibleObjectType& val)
- : m_type(value_t::object)
+ basic_json(const CompatibleObjectType& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::object),
+ m_allocator(allocator)
{
using std::begin;
using std::end;
- m_value.object = create<object_t>(begin(val), end(val));
+ m_value.object = create<object_t>(m_allocator,
+ begin(val), end(val),
+ std::less<StringType>(),
+ m_allocator);
}
/*!
@@ -1116,6 +1163,7 @@
Create an array JSON value with a given content.
@param[in] val a value for the array
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1129,8 +1177,11 @@
@since version 1.0.0
*/
- basic_json(const array_t& val)
- : m_type(value_t::array), m_value(val)
+ basic_json(const array_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1147,6 +1198,7 @@
`value_type` from which a @ref basic_json value can be constructed.
@param[in] val a value for the array
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1169,12 +1221,16 @@
not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
= 0>
- basic_json(const CompatibleArrayType& val)
- : m_type(value_t::array)
+ basic_json(const CompatibleArrayType& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_allocator(allocator)
{
using std::begin;
using std::end;
- m_value.array = create<array_t>(begin(val), end(val));
+ m_value.array = create<array_t>(m_allocator,
+ begin(val), end(val),
+ m_allocator);
}
/*!
@@ -1183,6 +1239,7 @@
Create an string JSON value with a given content.
@param[in] val a value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1198,8 +1255,11 @@
@since version 1.0.0
*/
- basic_json(const string_t& val)
- : m_type(value_t::string), m_value(val)
+ basic_json(const string_t& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::string),
+ m_value(allocator, val),
+ m_allocator(allocator)
{}
/*!
@@ -1208,6 +1268,7 @@
Create a string JSON value with a given content.
@param[in] val a literal value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the size of the passed @a val.
@@ -1222,8 +1283,9 @@
@since version 1.0.0
*/
- basic_json(const typename string_t::value_type* val)
- : basic_json(string_t(val))
+ basic_json(const typename string_t::value_type* val,
+ const allocator_type& allocator = {})
+ : basic_json(string_t(val, allocator), allocator)
{}
/*!
@@ -1232,6 +1294,7 @@
Create a string JSON value with a given content.
@param[in] val a value for the string
+ @param[in] allocator the allocator to use to construct objects (optional)
@tparam CompatibleStringType an string type which is compatible to @ref
string_t, for instance `std::string`.
@@ -1253,8 +1316,9 @@
std::enable_if<
std::is_constructible<string_t, CompatibleStringType>::value, int>::type
= 0>
- basic_json(const CompatibleStringType& val)
- : basic_json(string_t(val))
+ basic_json(const CompatibleStringType& val,
+ const allocator_type& allocator = {})
+ : basic_json(string_t(val, allocator), allocator)
{}
/*!
@@ -1263,6 +1327,7 @@
Creates a JSON boolean type from a given value.
@param[in] val a boolean value to store
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1271,8 +1336,11 @@
@since version 1.0.0
*/
- basic_json(boolean_t val) noexcept
- : m_type(value_t::boolean), m_value(val)
+ basic_json(boolean_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::boolean),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1286,6 +1354,7 @@
the helper type @a T is not visible in this constructor's interface.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1304,8 +1373,11 @@
and std::is_same<T, number_integer_t>::value
, int>::type
= 0>
- basic_json(const number_integer_t val) noexcept
- : m_type(value_t::number_integer), m_value(val)
+ basic_json(const number_integer_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_integer),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1314,6 +1386,7 @@
Create an integer number JSON value with a given content.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note This constructor allows to pass enums directly to a constructor. As
C++ has no way of specifying the type of an anonymous enum explicitly, we
@@ -1333,9 +1406,11 @@
@since version 1.0.0
*/
- basic_json(const int val) noexcept
+ basic_json(const int val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
+ m_value(static_cast<number_integer_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1350,6 +1425,7 @@
`long`, and `short`.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1370,9 +1446,11 @@
std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
CompatibleNumberIntegerType>::type
= 0>
- basic_json(const CompatibleNumberIntegerType val) noexcept
+ basic_json(const CompatibleNumberIntegerType val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
+ m_value(static_cast<number_integer_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1384,6 +1462,7 @@
(not visible in) the interface.
@param[in] val an integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1398,8 +1477,11 @@
and std::is_same<T, number_unsigned_t>::value
, int>::type
= 0>
- basic_json(const number_unsigned_t val) noexcept
- : m_type(value_t::number_unsigned), m_value(val)
+ basic_json(const number_unsigned_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_unsigned),
+ m_value(val),
+ m_allocator(allocator)
{}
/*!
@@ -1414,6 +1496,7 @@
`uint32_t`, or `unsigned short`.
@param[in] val an unsigned integer to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Constant.
@@ -1429,9 +1512,11 @@
not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type
= 0>
- basic_json(const CompatibleNumberUnsignedType val) noexcept
+ basic_json(const CompatibleNumberUnsignedType val,
+ const allocator_type& allocator = {}) noexcept
: m_type(value_t::number_unsigned),
- m_value(static_cast<number_unsigned_t>(val))
+ m_value(static_cast<number_unsigned_t>(val)),
+ m_allocator(allocator)
{}
/*!
@@ -1440,6 +1525,7 @@
Create a floating-point number JSON value with a given content.
@param[in] val a floating-point value to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
disallows NaN values:
@@ -1458,8 +1544,11 @@
@since version 1.0.0
*/
- basic_json(const number_float_t val) noexcept
- : m_type(value_t::number_float), m_value(val)
+ basic_json(const number_float_t val,
+ const allocator_type& allocator = {}) noexcept
+ : m_type(value_t::number_float),
+ m_value(val),
+ m_allocator(allocator)
{
// replace infinity and NAN by null
if (not std::isfinite(val))
@@ -1481,6 +1570,7 @@
or `double`.
@param[in] val a floating-point to create a JSON number from
+ @param[in] allocator the allocator to use to construct objects (optional)
@note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
disallows NaN values:
@@ -1505,8 +1595,9 @@
std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
std::is_floating_point<CompatibleNumberFloatType>::value>::type
>
- basic_json(const CompatibleNumberFloatType val) noexcept
- : basic_json(number_float_t(val))
+ basic_json(const CompatibleNumberFloatType val,
+ const allocator_type& allocator = {}) noexcept
+ : basic_json(number_float_t(val), allocator)
{}
/*!
@@ -1561,6 +1652,8 @@
value_t::array and @ref value_t::object are valid); when @a type_deduction
is set to `true`, this parameter has no effect
+ @param[in] allocator the allocator to use to construct objects (optional)
+
@throw std::domain_error if @a type_deduction is `false`, @a manual_type
is `value_t::object`, but @a init contains an element which is not a pair
whose first element is a string; example: `"cannot create object from
@@ -1580,7 +1673,9 @@
*/
basic_json(std::initializer_list<basic_json> init,
bool type_deduction = true,
- value_t manual_type = value_t::array)
+ value_t manual_type = value_t::array,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
// check if each element is an array with two elements whose first
// element is a string
@@ -1610,7 +1705,7 @@
{
// the initializer list is a list of pairs -> create object
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
assert(m_value.object != nullptr);
@@ -1623,7 +1718,7 @@
{
// the initializer list describes an array -> create array
m_type = value_t::array;
- m_value.array = create<array_t>(init);
+ m_value.array = create<array_t>(m_allocator, init, m_allocator);
}
}
@@ -1646,6 +1741,7 @@
@param[in] init initializer list with JSON values to create an array from
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return JSON array value
@@ -1662,9 +1758,10 @@
@since version 1.0.0
*/
static basic_json array(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ std::initializer_list<basic_json>(),
+ const allocator_type& allocator = {})
{
- return basic_json(init, false, value_t::array);
+ return basic_json(init, false, value_t::array, allocator);
}
/*!
@@ -1682,6 +1779,7 @@
value_t).
@param[in] init initializer list to create an object from (optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return JSON object value
@@ -1702,9 +1800,10 @@
@since version 1.0.0
*/
static basic_json object(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ std::initializer_list<basic_json>(),
+ const allocator_type& allocator = {})
{
- return basic_json(init, false, value_t::object);
+ return basic_json(init, false, value_t::object, allocator);
}
/*!
@@ -1716,6 +1815,7 @@
@param[in] cnt the number of JSON copies of @a val to create
@param[in] val the JSON value to copy
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in @a cnt.
@@ -1725,10 +1825,12 @@
@since version 1.0.0
*/
- basic_json(size_type cnt, const basic_json& val)
- : m_type(value_t::array)
+ basic_json(size_type cnt, const basic_json& val,
+ const allocator_type& allocator = {})
+ : m_type(value_t::array),
+ m_allocator(allocator)
{
- m_value.array = create<array_t>(cnt, val);
+ m_value.array = create<array_t>(m_allocator, cnt, val, m_allocator);
}
/*!
@@ -1748,6 +1850,7 @@
@param[in] first begin of the range to copy from (included)
@param[in] last end of the range to copy from (excluded)
+ @param[in] allocator the allocator to use to construct objects (optional)
@throw std::domain_error if iterators are not compatible; that is, do not
belong to the same JSON value; example: `"iterators are not compatible"`
@@ -1771,7 +1874,10 @@
std::is_same<InputIT, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+ basic_json(InputIT first, InputIT last,
+ const allocator_type& allocator = {})
+ : m_type(first.m_object->m_type),
+ m_allocator(allocator)
{
// make sure iterator fits the current value
if (first.m_object != last.m_object)
@@ -1840,13 +1946,20 @@
case value_t::object:
{
- m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+ m_value.object = create<object_t>(m_allocator,
+ first.m_it.object_iterator,
+ last.m_it.object_iterator,
+ std::less<StringType>(),
+ m_allocator);
break;
}
case value_t::array:
{
- m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+ m_value.array = create<array_t>(m_allocator,
+ first.m_it.array_iterator,
+ last.m_it.array_iterator,
+ m_allocator);
break;
}
@@ -1865,6 +1978,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@@ -1878,9 +1992,11 @@
@since version 2.0.0
*/
- explicit basic_json(std::istream& i, parser_callback_t cb = nullptr)
+ explicit basic_json(std::istream& i, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
- *this = parser(i, cb).parse();
+ *this = parser(i, cb, allocator).parse();
}
///////////////////////////////////////
@@ -1910,7 +2026,8 @@
@since version 1.0.0
*/
basic_json(const basic_json& other)
- : m_type(other.m_type)
+ : m_type(other.m_type),
+ m_allocator(other.m_allocator)
{
switch (m_type)
{
@@ -1986,7 +2103,8 @@
*/
basic_json(basic_json&& other) noexcept
: m_type(std::move(other.m_type)),
- m_value(std::move(other.m_value))
+ m_value(std::move(other.m_value)),
+ m_allocator(std::move(other.m_allocator))
{
// invalidate payload
other.m_type = value_t::null;
@@ -2050,25 +2168,25 @@
{
case value_t::object:
{
- AllocatorType<object_t> alloc;
- alloc.destroy(m_value.object);
- alloc.deallocate(m_value.object, 1);
+ AllocatorType<object_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<object_t>>::destroy(alloc, m_value.object);
+ std::allocator_traits<AllocatorType<object_t>>::deallocate(alloc, m_value.object, 1);
break;
}
case value_t::array:
{
- AllocatorType<array_t> alloc;
- alloc.destroy(m_value.array);
- alloc.deallocate(m_value.array, 1);
+ AllocatorType<array_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<array_t>>::destroy(alloc, m_value.array);
+ std::allocator_traits<AllocatorType<array_t>>::deallocate(alloc, m_value.array, 1);
break;
}
case value_t::string:
{
- AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
+ AllocatorType<string_t> alloc(m_allocator);
+ std::allocator_traits<AllocatorType<string_t>>::destroy(alloc, m_value.string);
+ std::allocator_traits<AllocatorType<string_t>>::deallocate(alloc, m_value.string, 1);
break;
}
@@ -2116,7 +2234,10 @@
*/
string_t dump(const int indent = -1) const
{
- std::stringstream ss;
+ std::basic_stringstream<typename string_t::value_type,
+ typename string_t::traits_type,
+ typename string_t::allocator_type> ss;
+
// fix locale problems
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
@@ -3283,7 +3404,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value.array = create<array_t>();
+ m_value.array = create<array_t>(m_allocator, m_allocator);
}
// operator[] only works for arrays
@@ -3372,7 +3493,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value.object = create<object_t>();
+ m_value.object = create<object_t>(m_allocator, m_allocator);
}
// operator[] only works for objects
@@ -3531,7 +3652,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
}
// at only works for objects
@@ -4773,7 +4894,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value = value_t::array;
+ m_value = json_value(m_allocator, value_t::array);
}
// add element to array (move semantics)
@@ -4809,7 +4930,7 @@
if (is_null())
{
m_type = value_t::array;
- m_value = value_t::array;
+ m_value = json_value(m_allocator, value_t::array);
}
// add element to array
@@ -4859,7 +4980,7 @@
if (is_null())
{
m_type = value_t::object;
- m_value = value_t::object;
+ m_value = json_value(m_allocator, value_t::object);
}
// add element to array
@@ -5750,6 +5871,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return result of the deserialization
@@ -5767,9 +5889,11 @@
@since version 1.0.0
*/
- static basic_json parse(const string_t& s, parser_callback_t cb = nullptr)
+ static basic_json parse(const string_t& s,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(s, cb).parse();
+ return parser(s, cb, allocator).parse();
}
/*!
@@ -5779,6 +5903,7 @@
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allocator the allocator to use to construct objects (optional)
@return result of the deserialization
@@ -5796,17 +5921,21 @@
@since version 1.0.0
*/
- static basic_json parse(std::istream& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream& i,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@copydoc parse(std::istream&, parser_callback_t)
*/
- static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream&& i,
+ parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@@ -5834,7 +5963,7 @@
*/
friend std::istream& operator<<(basic_json& j, std::istream& i)
{
- j = parser(i).parse();
+ j = parser(i, nullptr, j.get_allocator()).parse();
return i;
}
@@ -5844,7 +5973,7 @@
*/
friend std::istream& operator>>(std::istream& i, basic_json& j)
{
- j = parser(i).parse();
+ j = parser(i, nullptr, j.get_allocator()).parse();
return i;
}
@@ -6220,6 +6349,9 @@
/// the value of the current element
json_value m_value = {};
+ /// the allocator to be used when constructing objects.
+ const allocator_type m_allocator;
+
private:
///////////////
@@ -7827,16 +7959,18 @@
{
public:
/// constructor for strings
- parser(const string_t& s, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
+ parser(const string_t& s, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(s), m_allocator(allocator)
{
// read first token
get_token();
}
/// a parser reading from an input stream
- parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& _is, parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(&_is), m_allocator(allocator)
{
// read first token
get_token();
@@ -7868,7 +8002,7 @@
{
// explicitly set result to object to cope with {}
result.m_type = value_t::object;
- result.m_value = json_value(value_t::object);
+ result.m_value = json_value(m_allocator, value_t::object);
}
// read next token
@@ -7944,9 +8078,9 @@
{
if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result))))
{
- // explicitly set result to object to cope with []
+ // explicitly set result to array to cope with []
result.m_type = value_t::array;
- result.m_value = json_value(value_t::array);
+ result.m_value = json_value(m_allocator, value_t::array);
}
// read next token
@@ -8088,6 +8222,8 @@
typename lexer::token_type last_token = lexer::token_type::uninitialized;
/// the lexer
lexer m_lexer;
+ /// allocator for any objects that are created
+ const allocator_type m_allocator;
};
public:
diff --git a/test/src/StlAllocatorMock.hpp b/test/src/StlAllocatorMock.hpp
new file mode 100644
index 0000000..43ad450
--- /dev/null
+++ b/test/src/StlAllocatorMock.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include <limits>
+#include <string>
+#include <type_traits>
+
+#include "StlAllocatorMockState.hpp"
+
+template <class T>
+class StlAllocatorMock
+{
+ public:
+ typedef typename std::remove_const<T>::type value_type;
+
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ template <class U> struct rebind
+ {
+ typedef StlAllocatorMock<U> other;
+ };
+
+ // The default constructor is deleted to detect any cases where the state
+ // of the allocator will be lost.
+ // TODO: Uncomment this to find incorrect uses of the constructor.
+ //StlAllocatorMock() = delete;
+ StlAllocatorMock()
+ {
+ FAIL("default constructor should not be used");
+ }
+
+ explicit StlAllocatorMock(const StlAllocatorMockState* pState)
+ : m_pState(pState)
+ {}
+
+ template <class U>
+ StlAllocatorMock(const StlAllocatorMock<U>& stlAllocator)
+ : m_pState(stlAllocator.m_pState)
+ {}
+
+ StlAllocatorMock(const StlAllocatorMock& stlAllocator)
+ : m_pState(stlAllocator.m_pState)
+ {}
+
+ StlAllocatorMock& operator=(const StlAllocatorMock& rhs)
+ {
+ if (this != &rhs)
+ {
+ m_pState = rhs.m_pState;
+ }
+ return *this;
+ }
+
+ ~StlAllocatorMock()
+ {}
+
+ pointer address(reference x) const
+ {
+ return std::addressof(x);
+ }
+
+ const_pointer address(const_reference x) const
+ {
+ return std::addressof(x);
+ }
+
+ pointer allocate(size_type count, const void* = nullptr)
+ {
+ pointer ptr = static_cast<pointer>(m_pState->Allocate(sizeof(T) * count));
+
+ if (nullptr == ptr)
+ {
+ throw std::bad_alloc();
+ }
+
+ return ptr;
+ }
+
+ void deallocate(pointer p, size_type = 0)
+ {
+ m_pState->Free(p);
+ }
+
+ template <class U>
+ void construct(pointer p, U&& val)
+ {
+ new(reinterpret_cast<void*>(p)) T(std::forward<U>(val));
+ }
+
+ size_type max_size() const
+ {
+ size_type count = std::numeric_limits<size_type>::max() / sizeof(T);
+ return count > 0 ? count : 1;
+ }
+
+ void destroy(const_pointer p)
+ {
+ p->~T();
+ }
+
+ bool operator==(const StlAllocatorMock& rhs) const
+ {
+ return *m_pState == *rhs.m_pState;
+ }
+
+ bool operator!=(const StlAllocatorMock& rhs) const
+ {
+ return !(this->operator==(rhs));
+ }
+
+ private:
+ // Required to allow the copy instructor for a different templated type to
+ // access the private members of this class.
+ template <typename U>
+ friend class StlAllocatorMock;
+
+ const StlAllocatorMockState* m_pState = nullptr;
+};
diff --git a/test/src/StlAllocatorMockState.hpp b/test/src/StlAllocatorMockState.hpp
new file mode 100644
index 0000000..fe133b6
--- /dev/null
+++ b/test/src/StlAllocatorMockState.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <set>
+
+class StlAllocatorMockState
+{
+ public:
+
+ StlAllocatorMockState(bool failAllAllocations = false)
+ : m_allocatedBlocks(), m_failAllAllocations(failAllAllocations)
+ {}
+
+ ~StlAllocatorMockState()
+ {
+ AssertNoOutstandingBlocks();
+ }
+
+ void* Allocate(const size_t cb) const
+ {
+ if (m_failAllAllocations)
+ {
+ return nullptr;
+ }
+
+ void* pBlock = ::malloc(cb);
+ m_allocatedBlocks.emplace(pBlock);
+ return pBlock;
+ }
+
+ void Free(void* pv) const
+ {
+ if (0 == m_allocatedBlocks.erase(pv))
+ {
+ throw "Free of unknown block";
+ }
+
+ ::free(pv);
+ }
+
+ void AssertNoOutstandingBlocks() const
+ {
+ CHECK(m_allocatedBlocks.empty());
+ }
+
+ bool operator==(const StlAllocatorMockState& rhs) const
+ {
+ return this == &rhs;
+ }
+
+ private:
+ mutable std::set<void*> m_allocatedBlocks;
+ const bool m_failAllAllocations;
+};
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index a12f052..f89e066 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -40,15 +40,29 @@
#include <unordered_set>
#include <vector>
-#define private public
-#include "json.hpp"
-using nlohmann::json;
-
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
+#define private public
+#include "json.hpp"
+using nlohmann::json;
+
+#include "StlAllocatorMock.hpp"
+using string_with_allocator =
+ std::basic_string<char, std::char_traits<char>, StlAllocatorMock<char>>;
+
+using json_with_allocator = nlohmann::basic_json <
+ std::map,
+ std::vector,
+ string_with_allocator,
+ bool,
+ std::int64_t,
+ std::uint64_t,
+ double,
+ StlAllocatorMock >;
+
TEST_CASE("constructors")
{
SECTION("create an empty value with a given type")
@@ -150,6 +164,22 @@
json j(o);
CHECK(j.type() == json::value_t::object);
}
+
+ SECTION("filled object (custom allocator)")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ json_with_allocator::object_t o(allocator);
+ o.insert({json_with_allocator::string_t("a", allocator), json_with_allocator(1, allocator)});
+ o.insert({json_with_allocator::string_t("b", allocator), json_with_allocator(1u, allocator)});
+ o.insert({json_with_allocator::string_t("c", allocator), json_with_allocator(2.2, allocator)});
+ o.insert({json_with_allocator::string_t("d", allocator), json_with_allocator(false, allocator)});
+ o.insert({json_with_allocator::string_t("e", allocator), json_with_allocator("string", allocator)});
+ o.insert({json_with_allocator::string_t("f", allocator), json_with_allocator(allocator)});
+ json_with_allocator j(o, allocator);
+ CHECK((j.type() == json_with_allocator::value_t::object));
+ }
}
SECTION("create an object (implicit)")
@@ -221,6 +251,24 @@
json j(a);
CHECK(j.type() == json::value_t::array);
}
+
+ SECTION("filled array (custom allocator)")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ json_with_allocator::array_t a({json_with_allocator(1, allocator),
+ json_with_allocator(1u, allocator),
+ json_with_allocator(2.2, allocator),
+ json_with_allocator(false, allocator),
+ json_with_allocator("string", allocator),
+ json_with_allocator(allocator)
+ },
+ allocator);
+ json_with_allocator j(a, allocator);
+
+ CHECK((j.type() == json_with_allocator::value_t::array));
+ }
}
SECTION("create an array (implicit)")
@@ -308,6 +356,17 @@
json j(s);
CHECK(j.type() == json::value_t::string);
}
+
+ SECTION("filled string (custom allocator)")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ json_with_allocator::string_t s("Hello world", allocator);
+ json_with_allocator j(s, allocator);
+
+ CHECK((j.type() == json_with_allocator::value_t::string));
+ }
}
SECTION("create a string (implicit)")
@@ -966,7 +1025,7 @@
SECTION("create an array of n copies of a given value")
{
json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}};
- json arr(3, v);
+ json arr(static_cast<size_t>(3), v);
CHECK(arr.size() == 3);
for (auto& x : arr)
{
@@ -1685,6 +1744,17 @@
json j_discarded(json::value_t::discarded);
CHECK(j_discarded.dump() == "<discarded>");
}
+
+ /*
+ SECTION("no indent (custom allocator)")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ json_with_allocator ja(allocator);
+ ja.dump();
+ }
+ */
}
SECTION("return the type of the object (explicit)")
@@ -8709,6 +8779,16 @@
{
CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
+
+ /*
+ SECTION("string (custom allocator)")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ json_with_allocator j = json_with_allocator::parse("[]");
+ }
+ */
}
TEST_CASE("iterator class")
@@ -14139,8 +14219,32 @@
}
}
-// special test case to check if memory is leaked if constructor throws
+TEST_CASE("custom Allocators")
+{
+ SECTION("constructor with state persists state")
+ {
+ StlAllocatorMockState allocatorState;
+ StlAllocatorMock<json_with_allocator> originalAllocator(&allocatorState);
+ json_with_allocator j(originalAllocator);
+ StlAllocatorMock<json_with_allocator> finalAllocator = j.get_allocator();
+
+ CHECK(originalAllocator == finalAllocator);
+ }
+
+ SECTION("bad_alloc thrown when allocation fails")
+ {
+ // Create a state that will fail all allocations.
+ StlAllocatorMockState allocatorState(true);
+ StlAllocatorMock<json_with_allocator> allocator(&allocatorState);
+
+ // creating an object should throw
+ CHECK_THROWS_AS(json_with_allocator j(json_with_allocator::value_t::object, allocator),
+ std::bad_alloc);
+ }
+}
+
+// special test case to check if memory is leaked if constructor throws
template<class T>
struct my_allocator : std::allocator<T>
{
@@ -14149,6 +14253,9 @@
{
throw std::bad_alloc();
}
+
+ my_allocator() = default;
+ template <class U> my_allocator(const my_allocator<U>&) {}
};
TEST_CASE("bad_alloc")