Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
J
json
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
json
Commits
c847e0ee
Commit
c847e0ee
authored
Jan 08, 2017
by
Théo DELRIEU
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
replace constructor by from/to_json: array_t
- tweaked a bit how `get<container<json>>` is handled - added a from_json overload for forward list
parent
6d427acd
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
251 additions
and
173 deletions
+251
-173
json.hpp
src/json.hpp
+118
-80
json.hpp.re2c
src/json.hpp.re2c
+118
-80
unit-conversions.cpp
test/src/unit-conversions.cpp
+2
-0
unit-udt.cpp
test/src/unit-udt.cpp
+13
-13
No files found.
src/json.hpp
View file @
c847e0ee
...
...
@@ -39,6 +39,7 @@ SOFTWARE.
#include <cstdint> // int64_t, uint64_t
#include <cstdlib> // strtod, strtof, strtold, strtoul
#include <cstring> // strlen
#include <forward_list> // forward_list
#include <functional> // function, hash, less
#include <initializer_list> // initializer_list
#include <iomanip> // setw
...
...
@@ -272,6 +273,32 @@ struct external_constructor<value_t::number_integer>
}
};
template
<>
struct
external_constructor
<
value_t
::
array
>
{
template
<
typename
Json
>
static
void
construct
(
Json
&
j
,
const
typename
Json
::
array_t
&
arr
)
{
j
.
m_type
=
value_t
::
array
;
j
.
m_value
=
arr
;
j
.
assert_invariant
();
}
template
<
typename
Json
,
typename
CompatibleArrayType
,
enable_if_t
<
not
std
::
is_same
<
CompatibleArrayType
,
typename
Json
::
array_t
>::
value
,
int
>
=
0
>
static
void
construct
(
Json
&
j
,
const
CompatibleArrayType
&
arr
)
{
using
std
::
begin
;
using
std
::
end
;
j
.
m_type
=
value_t
::
array
;
j
.
m_value
.
array
=
j
.
template
create
<
typename
Json
::
array_t
>
(
begin
(
arr
),
end
(
arr
));
j
.
assert_invariant
();
}
};
// very useful construct against boilerplate (more boilerplate needed than in
// C++17: http://en.cppreference.com/w/cpp/types/void_t)
template
<
typename
...
>
struct
make_void
...
...
@@ -432,8 +459,6 @@ template <typename T, typename BasicJson>
struct
is_compatible_basic_json_type
{
static
auto
constexpr
value
=
std
::
is_same
<
T
,
BasicJson
>::
value
or
is_compatible_array_type
<
BasicJson
,
T
>::
value
or
is_compatible_object_type
<
typename
BasicJson
::
object_t
,
T
>::
value
;
};
...
...
@@ -575,6 +600,17 @@ void to_json(Json &j, UnscopedEnumType e)
external_constructor
<
value_t
::
number_integer
>::
construct
(
j
,
e
);
}
template
<
typename
Json
,
typename
CompatibleArrayType
,
enable_if_t
<
is_compatible_array_type
<
Json
,
CompatibleArrayType
>::
value
or
std
::
is_same
<
typename
Json
::
array_t
,
CompatibleArrayType
>::
value
,
int
>
=
0
>
void
to_json
(
Json
&
j
,
CompatibleArrayType
const
&
arr
)
{
external_constructor
<
value_t
::
array
>::
construct
(
j
,
arr
);
}
template
<
typename
Json
>
void
from_json
(
Json
const
&
j
,
typename
Json
::
boolean_t
&
b
)
{
...
...
@@ -618,6 +654,59 @@ void from_json(Json const &j, UnscopedEnumType& e)
e
=
static_cast
<
UnscopedEnumType
>
(
val
);
}
template
<
typename
Json
>
void
from_json
(
Json
const
&
j
,
typename
Json
::
array_t
&
arr
)
{
if
(
!
j
.
is_array
())
throw
std
::
domain_error
(
"type must be array, but is "
+
type_name
(
j
));
arr
=
*
const_cast
<
Json
&>
(
j
).
template
get_ptr
<
typename
Json
::
array_t
*>
();
}
// forward_list doesn't have an insert method, TODO find a way to avoid including forward_list
template
<
typename
Json
,
typename
T
,
typename
Allocator
>
void
from_json
(
Json
const
&
j
,
std
::
forward_list
<
T
,
Allocator
>&
l
)
{
// do not perform the check when user wants to retrieve jsons
// (except when it's null.. ?)
if
(
j
.
is_null
())
throw
std
::
domain_error
(
"type must be array, but is "
+
type_name
(
j
));
if
(
not
std
::
is_same
<
T
,
Json
>::
value
)
{
if
(
!
j
.
is_array
())
throw
std
::
domain_error
(
"type must be array, but is "
+
type_name
(
j
));
}
for
(
auto
it
=
j
.
rbegin
(),
end
=
j
.
rend
();
it
!=
end
;
++
it
)
l
.
push_front
(
it
->
template
get
<
T
>
());
}
template
<
typename
Json
,
typename
CompatibleArrayType
,
enable_if_t
<
is_compatible_array_type
<
Json
,
CompatibleArrayType
>::
value
and
not
std
::
is_same
<
typename
Json
::
array_t
,
CompatibleArrayType
>::
value
,
int
>
=
0
>
void
from_json
(
Json
const
&
j
,
CompatibleArrayType
&
arr
)
{
if
(
j
.
is_null
())
throw
std
::
domain_error
(
"type must be array, but is "
+
type_name
(
j
));
// when T == Json, do not check if value_t is correct
if
(
not
std
::
is_same
<
typename
CompatibleArrayType
::
value_type
,
Json
>::
value
)
{
if
(
!
j
.
is_array
())
throw
std
::
domain_error
(
"type must be array, but is "
+
type_name
(
j
));
}
using
std
::
begin
;
using
std
::
end
;
std
::
transform
(
j
.
begin
(),
j
.
end
(),
std
::
inserter
(
arr
,
end
(
arr
)),
[](
Json
const
&
i
)
{
// get<Json>() returns *this, this won't call a from_json method when
// value_type is Json
return
i
.
template
get
<
typename
CompatibleArrayType
::
value_type
>
();
});
}
// overload for arithmetic types, not chosen for basic_json template arguments (BooleanType, etc..)
//
// note: Is it really necessary to provide explicit overloads for boolean_t etc..
...
...
@@ -1780,69 +1869,6 @@ class basic_json
assert_invariant
();
}
/*!
@brief create an array (explicit)
Create an array JSON value with a given content.
@param[in] val a value for the array
@complexity Linear in the size of the passed @a val.
@throw std::bad_alloc if allocation for array value fails
@liveexample{The following code shows the constructor with an @ref array_t
parameter.,basic_json__array_t}
@sa @ref basic_json(const CompatibleArrayType&) -- create an array value
from a compatible STL containers
@since version 1.0.0
*/
basic_json
(
const
array_t
&
val
)
:
m_type
(
value_t
::
array
),
m_value
(
val
)
{
assert_invariant
();
}
/*!
@brief create an array (implicit)
Create an array JSON value with a given content. This constructor allows
any type @a CompatibleArrayType that can be used to construct values of
type @ref array_t.
@tparam CompatibleArrayType An object type whose `value_type` is
compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
`std::list`, `std::forward_list`, `std::array`, `std::set`,
`std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
`value_type` from which a @ref basic_json value can be constructed.
@param[in] val a value for the array
@complexity Linear in the size of the passed @a val.
@throw std::bad_alloc if allocation for array value fails
@liveexample{The following code shows the constructor with several
compatible array type parameters.,basic_json__CompatibleArrayType}
@sa @ref basic_json(const array_t&) -- create an array value
@since version 1.0.0
*/
template
<
class
CompatibleArrayType
,
enable_if_t
<
detail
::
is_compatible_array_type
<
basic_json_t
,
CompatibleArrayType
>::
value
,
int
>
=
0
>
basic_json
(
const
CompatibleArrayType
&
val
)
:
m_type
(
value_t
::
array
)
{
using
std
::
begin
;
using
std
::
end
;
m_value
.
array
=
create
<
array_t
>
(
begin
(
val
),
end
(
val
));
assert_invariant
();
}
// constructor chosen when:
// - JSONSerializer::to_json exists for type T
// - T is not a istream, nor convertible to basic_json (float, vectors, etc)
...
...
@@ -1851,6 +1877,7 @@ class basic_json
typename
T
,
enable_if_t
<
not
std
::
is_base_of
<
std
::
istream
,
uncvref_t
<
T
>>::
value
and
not
detail
::
is_basic_json_nested_class
<
uncvref_t
<
T
>
,
basic_json_t
,
primitive_iterator_t
>::
value
and
not
std
::
is_same
<
uncvref_t
<
T
>
,
basic_json_t
>::
value
and
not
std
::
is_same
<
uncvref_t
<
T
>
,
typename
basic_json_t
::
array_t
::
iterator
>::
value
and
not
std
::
is_same
<
uncvref_t
<
T
>
,
typename
basic_json_t
::
object_t
::
iterator
>::
value
and
detail
::
conjunction
<
detail
::
negation
<
detail
::
is_compatible_basic_json_type
<
...
...
@@ -3211,23 +3238,34 @@ class basic_json
return
get_impl
(
static_cast
<
ValueType
*>
(
nullptr
));
}
// if T is basic_json, simply returns *this
template
<
typename
T
,
enable_if_t
<
std
::
is_same
<
T
,
basic_json_t
>::
value
,
int
>
=
0
>
basic_json
get
()
const
{
return
*
this
;
}
template
<
typename
T
,
enable_if_t
<
detail
::
conjunction
<
detail
::
negation
<
detail
::
is_compatible_basic_json_type
<
uncvref_t
<
T
>
,
basic_json_t
>>
,
detail
::
has_from_json
<
JSONSerializer
,
basic_json_t
,
uncvref_t
<
T
>>>::
value
,
int
>
=
0
>
auto
get
()
const
->
uncvref_t
<
T
>
{
using
type
=
uncvref_t
<
T
>
;
static_assert
(
std
::
is_default_constructible
<
type
>::
value
&&
std
::
is_copy_constructible
<
type
>::
value
,
"user-defined types must be DefaultConstructible and "
"CopyConstructible when used with get"
);
type
ret
;
JSONSerializer
<
type
>::
from_json
(
*
this
,
ret
);
return
ret
;
enable_if_t
<
detail
::
conjunction
<
detail
::
negation
<
detail
::
is_compatible_basic_json_type
<
uncvref_t
<
T
>
,
basic_json_t
>>
,
detail
::
has_from_json
<
JSONSerializer
,
basic_json_t
,
uncvref_t
<
T
>>>::
value
and
not
std
::
is_same
<
basic_json_t
,
uncvref_t
<
T
>>::
value
,
int
>
=
0
>
// do we really want the uncvref ? if a user call get<int &>, shouldn't we static assert ?
auto
get
()
const
->
uncvref_t
<
T
>
{
using
type
=
uncvref_t
<
T
>
;
static_assert
(
std
::
is_default_constructible
<
type
>::
value
&&
std
::
is_copy_constructible
<
type
>::
value
,
"user-defined types must be DefaultConstructible and "
"CopyConstructible when used with get"
);
type
ret
;
JSONSerializer
<
type
>::
from_json
(
*
this
,
ret
);
return
ret
;
}
// This overload is chosen for non-default constructible user-defined-types
...
...
src/json.hpp.re2c
View file @
c847e0ee
...
...
@@ -39,6 +39,7 @@ SOFTWARE.
#include <cstdint> // int64_t, uint64_t
#include <cstdlib> // strtod, strtof, strtold, strtoul
#include <cstring> // strlen
#include <forward_list> // forward_list
#include <functional> // function, hash, less
#include <initializer_list> // initializer_list
#include <iomanip> // setw
...
...
@@ -272,6 +273,32 @@ struct external_constructor<value_t::number_integer>
}
};
template <>
struct external_constructor<value_t::array>
{
template <typename Json>
static void construct(Json &j, const typename Json::array_t& arr)
{
j.m_type = value_t::array;
j.m_value = arr;
j.assert_invariant();
}
template <typename Json, typename CompatibleArrayType,
enable_if_t<not std::is_same<CompatibleArrayType,
typename Json::array_t>::value,
int> = 0>
static void construct(Json &j, const CompatibleArrayType &arr)
{
using std::begin;
using std::end;
j.m_type = value_t::array;
j.m_value.array =
j.template create<typename Json::array_t>(begin(arr), end(arr));
j.assert_invariant();
}
};
// very useful construct against boilerplate (more boilerplate needed than in
// C++17: http://en.cppreference.com/w/cpp/types/void_t)
template <typename...> struct make_void
...
...
@@ -432,8 +459,6 @@ template <typename T, typename BasicJson>
struct is_compatible_basic_json_type
{
static auto constexpr value =
std::is_same<T, BasicJson>::value or
is_compatible_array_type<BasicJson, T>::value or
is_compatible_object_type<typename BasicJson::object_t, T>::value;
};
...
...
@@ -575,6 +600,17 @@ void to_json(Json &j, UnscopedEnumType e)
external_constructor<value_t::number_integer>::construct(j, e);
}
template <
typename Json, typename CompatibleArrayType,
enable_if_t<
is_compatible_array_type<Json, CompatibleArrayType>::value or
std::is_same<typename Json::array_t, CompatibleArrayType>::value,
int> = 0>
void to_json(Json &j, CompatibleArrayType const &arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
template <typename Json>
void from_json(Json const& j, typename Json::boolean_t& b)
{
...
...
@@ -618,6 +654,59 @@ void from_json(Json const &j, UnscopedEnumType& e)
e = static_cast<UnscopedEnumType>(val);
}
template <typename Json>
void from_json(Json const &j, typename Json::array_t &arr)
{
if (!j.is_array())
throw std::domain_error("type must be array, but is " + type_name(j));
arr = *const_cast<Json&>(j).template get_ptr<typename Json::array_t*>();
}
// forward_list doesn't have an insert method, TODO find a way to avoid including forward_list
template <typename Json, typename T, typename Allocator>
void from_json(Json const&j, std::forward_list<T, Allocator>& l)
{
// do not perform the check when user wants to retrieve jsons
// (except when it's null.. ?)
if (j.is_null())
throw std::domain_error("type must be array, but is " + type_name(j));
if (not std::is_same<T, Json>::value)
{
if (!j.is_array())
throw std::domain_error("type must be array, but is " + type_name(j));
}
for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
l.push_front(it->template get<T>());
}
template <
typename Json, typename CompatibleArrayType,
enable_if_t<is_compatible_array_type<Json, CompatibleArrayType>::value and
not std::is_same<typename Json::array_t,
CompatibleArrayType>::value,
int> = 0>
void from_json(Json const &j, CompatibleArrayType &arr)
{
if (j.is_null())
throw std::domain_error("type must be array, but is " + type_name(j));
// when T == Json, do not check if value_t is correct
if (not std::is_same<typename CompatibleArrayType::value_type, Json>::value)
{
if (!j.is_array())
throw std::domain_error("type must be array, but is " + type_name(j));
}
using std::begin;
using std::end;
std::transform(
j.begin(), j.end(), std::inserter(arr, end(arr)), [](Json const &i)
{
// get<Json>() returns *this, this won't call a from_json method when
// value_type is Json
return i.template get<typename CompatibleArrayType::value_type>();
});
}
// overload for arithmetic types, not chosen for basic_json template arguments (BooleanType, etc..)
//
// note: Is it really necessary to provide explicit overloads for boolean_t etc..
...
...
@@ -1781,69 +1870,6 @@ class basic_json
assert_invariant();
}
/*!
@brief create an array (explicit)
Create an array JSON value with a given content.
@param[in] val a value for the array
@complexity Linear in the size of the passed @a val.
@throw std::bad_alloc if allocation for array value fails
@liveexample{The following code shows the constructor with an @ref array_t
parameter.,basic_json__array_t}
@sa @ref basic_json(const CompatibleArrayType&) -- create an array value
from a compatible STL containers
@since version 1.0.0
*/
basic_json(const array_t& val)
: m_type(value_t::array), m_value(val)
{
assert_invariant();
}
/*!
@brief create an array (implicit)
Create an array JSON value with a given content. This constructor allows
any type @a CompatibleArrayType that can be used to construct values of
type @ref array_t.
@tparam CompatibleArrayType An object type whose `value_type` is
compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
`std::list`, `std::forward_list`, `std::array`, `std::set`,
`std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
`value_type` from which a @ref basic_json value can be constructed.
@param[in] val a value for the array
@complexity Linear in the size of the passed @a val.
@throw std::bad_alloc if allocation for array value fails
@liveexample{The following code shows the constructor with several
compatible array type parameters.,basic_json__CompatibleArrayType}
@sa @ref basic_json(const array_t&) -- create an array value
@since version 1.0.0
*/
template <class CompatibleArrayType,
enable_if_t<detail::is_compatible_array_type<basic_json_t, CompatibleArrayType>::value,
int> = 0>
basic_json(const CompatibleArrayType& val) : m_type(value_t::array)
{
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
assert_invariant();
}
// constructor chosen when:
// - JSONSerializer::to_json exists for type T
// - T is not a istream, nor convertible to basic_json (float, vectors, etc)
...
...
@@ -1852,6 +1878,7 @@ class basic_json
typename T,
enable_if_t<not std::is_base_of<std::istream, uncvref_t<T>>::value and
not detail::is_basic_json_nested_class<uncvref_t<T>, basic_json_t, primitive_iterator_t>::value and
not std::is_same<uncvref_t<T>, basic_json_t>::value and
not std::is_same<uncvref_t<T>, typename basic_json_t::array_t::iterator>::value and
not std::is_same<uncvref_t<T>, typename basic_json_t::object_t::iterator>::value and
detail::conjunction<detail::negation<detail::is_compatible_basic_json_type<
...
...
@@ -3209,23 +3236,34 @@ class basic_json
return get_impl(static_cast<ValueType *>(nullptr));
}
// if T is basic_json, simply returns *this
template <typename T,
enable_if_t<std::is_same<T, basic_json_t>::value, int> = 0>
basic_json get() const
{
return *this;
}
template <
typename T,
enable_if_t<detail::conjunction<detail::negation<detail::is_compatible_basic_json_type<
uncvref_t<T>, basic_json_t>>,
detail::has_from_json<JSONSerializer, basic_json_t,
uncvref_t<T>>>::value,
int> = 0 >
auto get() const -> uncvref_t<T>
{
using type = uncvref_t<T>;
static_assert(std::is_default_constructible<type>::value&&
std::is_copy_constructible<type>::value,
"user-defined types must be DefaultConstructible and "
"CopyConstructible when used with get");
type ret;
JSONSerializer<type>::from_json(*this, ret);
return ret;
enable_if_t<detail::conjunction<
detail::negation<detail::is_compatible_basic_json_type<
uncvref_t<T>, basic_json_t>>,
detail::has_from_json<JSONSerializer, basic_json_t,
uncvref_t<T>>>::value and
not std::is_same<basic_json_t, uncvref_t<T>>::value,
int> = 0>
// do we really want the uncvref ? if a user call get<int &>, shouldn't we static assert ?
auto get() const -> uncvref_t<T>
{
using type = uncvref_t<T>;
static_assert(std::is_default_constructible<type>::value &&
std::is_copy_constructible<type>::value,
"user-defined types must be DefaultConstructible and "
"CopyConstructible when used with get");
type ret;
JSONSerializer<type>::from_json(*this, ret);
return ret;
}
// This overload is chosen for non-default constructible user-defined-types
...
...
test/src/unit-conversions.cpp
View file @
c847e0ee
...
...
@@ -1004,6 +1004,8 @@ TEST_CASE("value conversion")
CHECK_THROWS_AS
((
json
().
get
<
std
::
vector
<
json
>>
()),
std
::
logic_error
);
CHECK_THROWS_AS
((
json
().
get
<
std
::
list
<
json
>>
()),
std
::
logic_error
);
// does type really must be an array? or it rather must not be null?
// that's what I thought when other test like this one broke
CHECK_THROWS_WITH
((
json
().
get
<
std
::
list
<
int
>>
()),
"type must be array, but is null"
);
CHECK_THROWS_WITH
((
json
().
get
<
std
::
vector
<
int
>>
()),
"type must be array, but is null"
);
CHECK_THROWS_WITH
((
json
().
get
<
std
::
vector
<
json
>>
()),
"type must be array, but is null"
);
...
...
test/src/unit-udt.cpp
View file @
c847e0ee
...
...
@@ -391,19 +391,19 @@ TEST_CASE("adl_serializer specialization", "[udt]")
namespace
nlohmann
{
// this might work in the future, not in the scope of this PR though
//
we have to make this very clear in the doc
template
<
typename
T
>
struct
adl_serializer
<
std
::
vector
<
T
>>
{
static
void
to_json
(
json
&
j
,
std
::
vector
<
T
>
const
&
opt
)
{
}
static
void
from_json
(
json
const
&
j
,
std
::
vector
<
T
>&
opt
)
{
}
};
// TODO provide a real example, since this works now :)
//
template <typename T>
// struct adl_serializer<std::vector<T>
>
// {
// static void to_json(json& j, std::vector<T> const& opt)
// {
//
//
}
//
//
static void from_json(json const& j, std::vector<T>& opt)
//
{
//
}
//
};
}
TEST_CASE
(
"current supported types are preferred over specializations"
,
"[udt]"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment