Merge branch 'develop' into feature/allocators
diff --git a/Makefile b/Makefile
index 98f0768..46d176c 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,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 $@
@@ -77,7 +77,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 5849067..ed1e807 100644
--- a/README.md
+++ b/README.md
@@ -505,7 +505,7 @@
$ ./json_unit "*"
===============================================================================
-All tests passed (8905012 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 9d2305d..d4b6f40 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
@@ -750,15 +759,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)...);
assert(object.get() != nullptr);
return object.release();
}
@@ -819,25 +830,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;
}
@@ -874,20 +885,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);
}
};
@@ -1005,6 +1031,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.
@@ -1028,8 +1055,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)
{}
/*!
@@ -1038,6 +1068,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
@@ -1056,7 +1088,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)
@@ -1067,6 +1101,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
@@ -1080,8 +1116,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)
{}
/*!
@@ -1090,6 +1127,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.
@@ -1103,8 +1141,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)
{}
/*!
@@ -1121,6 +1162,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.
@@ -1138,12 +1180,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);
}
/*!
@@ -1152,6 +1199,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.
@@ -1165,8 +1213,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)
{}
/*!
@@ -1183,6 +1234,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.
@@ -1205,12 +1257,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);
}
/*!
@@ -1219,6 +1275,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.
@@ -1234,8 +1291,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)
{}
/*!
@@ -1244,6 +1304,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.
@@ -1258,8 +1319,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)
{}
/*!
@@ -1268,6 +1330,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`.
@@ -1289,8 +1352,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)
{}
/*!
@@ -1299,6 +1363,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.
@@ -1307,8 +1372,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)
{}
/*!
@@ -1322,6 +1390,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.
@@ -1340,8 +1409,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)
{}
/*!
@@ -1350,6 +1422,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
@@ -1369,9 +1442,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)
{}
/*!
@@ -1386,6 +1461,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.
@@ -1406,9 +1482,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)
{}
/*!
@@ -1420,6 +1498,7 @@
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.
@@ -1434,8 +1513,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)
{}
/*!
@@ -1450,6 +1532,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.
@@ -1465,9 +1548,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)
{}
/*!
@@ -1476,6 +1561,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:
@@ -1494,8 +1580,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))
@@ -1517,6 +1606,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:
@@ -1541,8 +1631,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)
{}
/*!
@@ -1597,6 +1688,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
@@ -1616,7 +1709,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
@@ -1646,7 +1741,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);
@@ -1659,7 +1754,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);
}
}
@@ -1682,6 +1777,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
@@ -1698,9 +1794,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);
}
/*!
@@ -1718,6 +1815,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
@@ -1738,9 +1836,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);
}
/*!
@@ -1752,6 +1851,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.
@@ -1761,10 +1861,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);
}
/*!
@@ -1784,6 +1886,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"`
@@ -1807,7 +1910,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)
@@ -1876,13 +1982,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;
}
@@ -1901,6 +2014,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
@@ -1914,9 +2028,11 @@
@since version 2.0.0
*/
- explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
+ explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
- *this = parser(i, cb).parse();
+ *this = parser(i, cb, allocator).parse();
}
///////////////////////////////////////
@@ -1946,7 +2062,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)
{
@@ -2022,7 +2139,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;
@@ -2086,25 +2204,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;
}
@@ -2152,7 +2270,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));
@@ -3327,7 +3448,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
@@ -3416,7 +3537,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
@@ -3575,7 +3696,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
@@ -4906,7 +5027,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)
@@ -4942,7 +5063,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
@@ -4992,7 +5113,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
@@ -5896,6 +6017,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
@@ -5914,9 +6036,10 @@
@since version 1.0.0
*/
static basic_json parse(const string_t& s,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(s, cb).parse();
+ return parser(s, cb, allocator).parse();
}
/*!
@@ -5926,6 +6049,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
@@ -5944,18 +6068,20 @@
@since version 1.0.0
*/
static basic_json parse(std::istream& i,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@copydoc parse(std::istream&, const parser_callback_t)
*/
static basic_json parse(std::istream&& i,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@@ -5983,7 +6109,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;
}
@@ -5993,7 +6119,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;
}
@@ -6374,6 +6500,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:
///////////////
@@ -8735,16 +8864,18 @@
{
public:
/// constructor for strings
- parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
+ parser(const string_t& s, const 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, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& _is, const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(&_is), m_allocator(allocator)
{
// read first token
get_token();
@@ -8776,7 +8907,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
@@ -8852,9 +8983,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
@@ -8996,6 +9127,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 537d4f4..e0cb669 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
@@ -750,15 +759,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)...);
assert(object.get() != nullptr);
return object.release();
}
@@ -819,25 +830,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;
}
@@ -874,20 +885,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);
}
};
@@ -1005,6 +1031,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.
@@ -1028,8 +1055,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)
{}
/*!
@@ -1038,6 +1068,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
@@ -1056,7 +1088,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)
@@ -1067,6 +1101,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
@@ -1080,8 +1116,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)
{}
/*!
@@ -1090,6 +1127,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.
@@ -1103,8 +1141,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)
{}
/*!
@@ -1121,6 +1162,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.
@@ -1138,12 +1180,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);
}
/*!
@@ -1152,6 +1199,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.
@@ -1165,8 +1213,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)
{}
/*!
@@ -1183,6 +1234,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.
@@ -1205,12 +1257,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);
}
/*!
@@ -1219,6 +1275,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.
@@ -1234,8 +1291,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)
{}
/*!
@@ -1244,6 +1304,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.
@@ -1258,8 +1319,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)
{}
/*!
@@ -1268,6 +1330,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`.
@@ -1289,8 +1352,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)
{}
/*!
@@ -1299,6 +1363,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.
@@ -1307,8 +1372,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)
{}
/*!
@@ -1322,6 +1390,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.
@@ -1340,8 +1409,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)
{}
/*!
@@ -1350,6 +1422,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
@@ -1369,9 +1442,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)
{}
/*!
@@ -1386,6 +1461,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.
@@ -1406,9 +1482,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)
{}
/*!
@@ -1420,6 +1498,7 @@
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.
@@ -1434,8 +1513,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)
{}
/*!
@@ -1450,6 +1532,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.
@@ -1465,9 +1548,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)
{}
/*!
@@ -1476,6 +1561,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:
@@ -1494,8 +1580,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))
@@ -1517,6 +1606,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:
@@ -1541,8 +1631,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)
{}
/*!
@@ -1597,6 +1688,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
@@ -1616,7 +1709,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
@@ -1646,7 +1741,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);
@@ -1659,7 +1754,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);
}
}
@@ -1682,6 +1777,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
@@ -1698,9 +1794,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);
}
/*!
@@ -1718,6 +1815,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
@@ -1738,9 +1836,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);
}
/*!
@@ -1752,6 +1851,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.
@@ -1761,10 +1861,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);
}
/*!
@@ -1784,6 +1886,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"`
@@ -1807,7 +1910,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)
@@ -1876,13 +1982,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;
}
@@ -1901,6 +2014,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
@@ -1914,9 +2028,11 @@
@since version 2.0.0
*/
- explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
+ explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
+ : m_allocator(allocator)
{
- *this = parser(i, cb).parse();
+ *this = parser(i, cb, allocator).parse();
}
///////////////////////////////////////
@@ -1946,7 +2062,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)
{
@@ -2022,7 +2139,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;
@@ -2086,25 +2204,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;
}
@@ -2152,7 +2270,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));
@@ -3327,7 +3448,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
@@ -3416,7 +3537,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
@@ -3575,7 +3696,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
@@ -4906,7 +5027,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)
@@ -4942,7 +5063,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
@@ -4992,7 +5113,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
@@ -5896,6 +6017,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
@@ -5914,9 +6036,10 @@
@since version 1.0.0
*/
static basic_json parse(const string_t& s,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(s, cb).parse();
+ return parser(s, cb, allocator).parse();
}
/*!
@@ -5926,6 +6049,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
@@ -5944,18 +6068,20 @@
@since version 1.0.0
*/
static basic_json parse(std::istream& i,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@copydoc parse(std::istream&, const parser_callback_t)
*/
static basic_json parse(std::istream&& i,
- const parser_callback_t cb = nullptr)
+ const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {})
{
- return parser(i, cb).parse();
+ return parser(i, cb, allocator).parse();
}
/*!
@@ -5983,7 +6109,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;
}
@@ -5993,7 +6119,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;
}
@@ -6374,6 +6500,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:
///////////////
@@ -8032,16 +8161,18 @@
{
public:
/// constructor for strings
- parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
+ parser(const string_t& s, const 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, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& _is, const parser_callback_t cb = nullptr,
+ const allocator_type& allocator = {}) noexcept
+ : callback(cb), m_lexer(&_is), m_allocator(allocator)
{
// read first token
get_token();
@@ -8073,7 +8204,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
@@ -8149,9 +8280,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
@@ -8293,6 +8424,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 edbafac..ff82ebd 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)
{
@@ -1686,6 +1745,17 @@
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("check that precision is reset after serialization")
{
// create stringstream and set precision
@@ -8862,6 +8932,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")
@@ -14375,8 +14455,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>
{
@@ -14385,6 +14489,9 @@
{
throw std::bad_alloc();
}
+
+ my_allocator() = default;
+ template <class U> my_allocator(const my_allocator<U>&) {}
};
TEST_CASE("bad_alloc")