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
4cdc61e4
Commit
4cdc61e4
authored
Oct 21, 2016
by
Théo DELRIEU
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
move most SFINAE trickery in to/from_json_fn
parent
03b391c3
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
142 additions
and
54 deletions
+142
-54
json.hpp
src/json.hpp
+82
-46
unit-constructor3.cpp
test/src/unit-constructor3.cpp
+60
-8
No files found.
src/json.hpp
View file @
4cdc61e4
...
@@ -161,22 +161,70 @@ struct has_mapped_type
...
@@ -161,22 +161,70 @@ struct has_mapped_type
std
::
is_integral
<
decltype
(
detect
(
std
::
declval
<
T
>
()))
>::
value
;
std
::
is_integral
<
decltype
(
detect
(
std
::
declval
<
T
>
()))
>::
value
;
};
};
void
to_json
();
void
from_json
();
struct
to_json_fn
struct
to_json_fn
{
{
template
<
typename
T
>
private
:
constexpr
auto
operator
()(
T
&&
val
)
const
->
decltype
(
to_json
(
std
::
forward
<
T
>
(
val
)))
// fallback overload
{
template
<
typename
T
>
return
to_json
(
std
::
forward
<
T
>
(
val
));
static
constexpr
auto
}
impl
(
T
&&
val
,
long
)
noexcept
(
noexcept
(
to_json
(
std
::
forward
<
T
>
(
val
))))
->
decltype
(
to_json
(
std
::
forward
<
T
>
(
val
)))
{
return
to_json
(
std
::
forward
<
T
>
(
val
));
}
// preferred overload
template
<
typename
T
>
static
constexpr
auto
impl
(
T
&&
val
,
int
)
noexcept
(
noexcept
(
json_traits
<
uncvref_t
<
T
>>::
to_json
(
std
::
forward
<
T
>
(
val
))))
->
decltype
(
json_traits
<
uncvref_t
<
T
>>::
to_json
(
std
::
forward
<
T
>
(
val
)))
{
return
json_traits
<
uncvref_t
<
T
>>::
to_json
(
std
::
forward
<
T
>
(
val
));
}
public
:
template
<
typename
T
>
constexpr
auto
operator
()(
T
&&
val
)
const
noexcept
(
noexcept
(
to_json_fn
::
impl
(
std
::
forward
<
T
>
(
val
),
0
)))
->
decltype
(
to_json_fn
::
impl
(
std
::
forward
<
T
>
(
val
),
0
))
{
// decltype(0) -> int, so the compiler will try to take the 'preferred overload'
// if there is no specialization, the 'fallback overload' will be taken by converting 0 to long
return
to_json_fn
::
impl
(
std
::
forward
<
T
>
(
val
),
0
);
}
};
};
struct
from_json_fn
struct
from_json_fn
{
{
template
<
typename
Json
,
typename
T
>
private
:
constexpr
auto
operator
()(
Json
const
&
from
,
T
&
to
)
const
->
decltype
(
from_json
(
from
,
to
))
template
<
typename
T
,
typename
Json
>
{
static
constexpr
auto
impl
(
Json
const
&
j
,
T
&
val
,
return
from_json
(
from
,
to
);
long
)
noexcept
(
noexcept
(
from_json
(
j
,
val
)))
}
->
decltype
(
from_json
(
j
,
val
))
{
return
from_json
(
j
,
val
);
}
template
<
typename
T
,
typename
Json
>
static
constexpr
auto
impl
(
Json
const
&
j
,
T
&
val
,
int
)
noexcept
(
noexcept
(
json_traits
<
T
>::
from_json
(
j
,
val
)))
->
decltype
(
json_traits
<
T
>::
from_json
(
j
,
val
))
{
return
json_traits
<
T
>::
from_json
(
j
,
val
);
}
public
:
template
<
typename
T
,
typename
Json
>
constexpr
auto
operator
()(
Json
const
&
j
,
T
&
val
)
const
noexcept
(
noexcept
(
from_json_fn
::
impl
(
j
,
val
,
0
)))
->
decltype
(
from_json_fn
::
impl
(
j
,
val
,
0
))
{
return
from_json_fn
::
impl
(
j
,
val
,
0
);
}
};
};
/*!
/*!
...
@@ -1373,7 +1421,13 @@ class basic_json
...
@@ -1373,7 +1421,13 @@ class basic_json
assert_invariant
();
assert_invariant
();
}
}
// constructor chosen if json_traits is specialized for type T
// constructor chosen for user-defined types that either have:
// - a to_json free function in their type's namespace
// - a json_traits specialization for their type
//
// If there is both a free function and a specialization, the latter will be chosen,
// since it is a more advanced use
//
// note: constructor is marked explicit to avoid the following issue:
// note: constructor is marked explicit to avoid the following issue:
//
//
// struct not_equality_comparable{};
// struct not_equality_comparable{};
...
@@ -1383,15 +1437,15 @@ class basic_json
...
@@ -1383,15 +1437,15 @@ class basic_json
// this will construct implicitely 2 json objects and call operator== on them
// this will construct implicitely 2 json objects and call operator== on them
// which can cause nasty bugs on the user's in json-unrelated code
// which can cause nasty bugs on the user's in json-unrelated code
//
//
// the trade-off is expressive
ty
in initializer-lists
// the trade-off is expressive
ness
in initializer-lists
// auto j = json{{"a", json(not_equality_comparable{})}};
// auto j = json{{"a", json(not_equality_comparable{})}};
//
//
// we can remove this constraint though, since lots of ctor are not explicit already
// we can remove this constraint though, since lots of ctor are not explicit already
template
<
typename
T
,
typename
=
decltype
(
json_traits
<
uncvref_t
<
T
>>::
to_json
(
std
::
declval
<
uncvref_t
<
T
>>
()))
>
template
<
typename
T
,
typename
=
decltype
(
::
nlohmann
::
to_json
(
std
::
declval
<
uncvref_t
<
T
>>
()))
>
explicit
basic_json
(
T
&&
val
)
explicit
basic_json
(
T
&&
val
)
:
basic_json
(
json_traits
<
uncvref_t
<
T
>>::
to_json
(
std
::
forward
<
T
>
(
val
)))
:
basic_json
(
::
nlohmann
::
to_json
(
std
::
forward
<
T
>
(
val
)))
{}
{
}
/*!
/*!
@brief create a string (explicit)
@brief create a string (explicit)
...
@@ -2752,32 +2806,6 @@ class basic_json
...
@@ -2752,32 +2806,6 @@ class basic_json
// value access //
// value access //
//////////////////
//////////////////
// get_impl overload chosen if json_traits struct is specialized for type T
// simply returns json_traits<T>::from_json(*this);
// dual argument to avoid conflicting with get_impl overloads taking a pointer
template
<
typename
T
>
auto
get_impl
(
int
,
int
)
const
->
decltype
(
json_traits
<
uncvref_t
<
T
>>::
from_json
(
*
this
))
{
return
json_traits
<
uncvref_t
<
T
>>::
from_json
(
*
this
);
}
// this overload is chosen ONLY if json_traits struct is not specialized, and if the expression nlohmann::from_json(*this, T&) is valid
// I chose to prefer the json_traits specialization if it exists, since it's a more advanced use.
// But we can of course change this behaviour
template
<
typename
T
>
auto
get_impl
(
long
,
long
)
const
->
uncvref_t
<
decltype
(
::
nlohmann
::
from_json
(
*
this
,
std
::
declval
<
T
&>
()),
std
::
declval
<
T
>
())
>
{
remove_cv_t
<
T
>
ret
;
// I guess this output parameter is the only way to get ADL
// Even if users can use the get<T> method to have a more 'functional' behaviour
// i.e. having a return type, could there be a way to have the same behaviour with from_json?
// e.g. auto t = nlohmann::from_json<T>(json{});
// this seems to require variable templates though... (at least it did when I tried to implement it)
::
nlohmann
::
from_json
(
*
this
,
ret
);
return
ret
;
}
template
<
class
T
,
typename
std
::
enable_if
<
template
<
class
T
,
typename
std
::
enable_if
<
std
::
is_convertible
<
typename
object_t
::
key_type
,
typename
T
::
key_type
>::
value
and
std
::
is_convertible
<
typename
object_t
::
key_type
,
typename
T
::
key_type
>::
value
and
std
::
is_convertible
<
basic_json_t
,
typename
T
::
mapped_type
>::
value
,
int
>::
type
=
0
>
std
::
is_convertible
<
basic_json_t
,
typename
T
::
mapped_type
>::
value
,
int
>::
type
=
0
>
...
@@ -3082,16 +3110,24 @@ class basic_json
...
@@ -3082,16 +3110,24 @@ class basic_json
*/
*/
template
<
typename
ValueType
,
typename
std
::
enable_if
<
template
<
typename
ValueType
,
typename
std
::
enable_if
<
not
std
::
is_pointer
<
ValueType
>::
value
,
int
>::
type
=
0
>
not
std
::
is_pointer
<
ValueType
>::
value
,
int
>::
type
=
0
>
auto
get
()
const
->
decltype
(
get_impl
(
static_cast
<
ValueType
*>
(
nullptr
)))
auto
get
()
const
->
decltype
(
this
->
get_impl
(
static_cast
<
ValueType
*>
(
nullptr
)))
{
{
return
get_impl
(
static_cast
<
ValueType
*>
(
nullptr
));
return
get_impl
(
static_cast
<
ValueType
*>
(
nullptr
));
}
}
template
<
typename
ValueType
>
template
<
typename
ValueType
>
auto
get
()
const
->
decltype
(
get_impl
<
ValueType
>
(
0
,
0
))
auto
get
()
const
->
remove_reference_t
<
{
decltype
(
::
nlohmann
::
from_json
(
*
this
,
std
::
declval
<
ValueType
&>
()),
return
get_impl
<
ValueType
>
(
0
,
0
);
std
::
declval
<
ValueType
>
())
>
{
static_assert
(
std
::
is_default_constructible
<
ValueType
>::
value
,
"ValueType must be default-constructible when user-defined "
"from_json method is used"
);
ValueType
ret
;
::
nlohmann
::
from_json
(
*
this
,
ret
);
return
ret
;
}
}
/*!
/*!
@brief get a pointer value (explicit)
@brief get a pointer value (explicit)
...
...
test/src/unit-constructor3.cpp
View file @
4cdc61e4
...
@@ -35,6 +35,9 @@ using nlohmann::json;
...
@@ -35,6 +35,9 @@ using nlohmann::json;
namespace
udt
namespace
udt
{
{
// only used by counter_type
auto
nb_free_function_calls
=
0
;
struct
empty_type
{};
struct
empty_type
{};
struct
pod_type
{
struct
pod_type
{
int
a
;
int
a
;
...
@@ -48,6 +51,10 @@ struct bit_more_complex_type {
...
@@ -48,6 +51,10 @@ struct bit_more_complex_type {
std
::
string
c
;
std
::
string
c
;
};
};
struct
counter_type
{
};
// best optional implementation ever
// best optional implementation ever
template
<
typename
T
>
template
<
typename
T
>
class
optional_type
class
optional_type
...
@@ -97,14 +104,18 @@ json to_json(optional_type<T> const& opt)
...
@@ -97,14 +104,18 @@ json to_json(optional_type<T> const& opt)
using
nlohmann
::
to_json
;
using
nlohmann
::
to_json
;
if
(
!
opt
)
if
(
!
opt
)
return
nullptr
;
return
nullptr
;
return
to_
json
(
*
opt
);
return
json
(
*
opt
);
}
}
json
to_json
(
no_json_traits_type
const
&
p
)
json
to_json
(
no_json_traits_type
const
&
p
)
{
{
json
ret
;
return
{{
"a"
,
p
.
a
}};
ret
[
"a"
]
=
p
.
a
;
}
return
ret
;
json
to_json
(
counter_type
)
{
++
nb_free_function_calls
;
return
json
::
object
();
}
}
void
from_json
(
json
const
&
j
,
empty_type
&
t
)
void
from_json
(
json
const
&
j
,
empty_type
&
t
)
...
@@ -139,6 +150,11 @@ void from_json(json const& j, optional_type<T>& t)
...
@@ -139,6 +150,11 @@ void from_json(json const& j, optional_type<T>& t)
t
=
j
.
get
<
T
>
();
t
=
j
.
get
<
T
>
();
}
}
void
from_json
(
json
const
&
,
counter_type
&
)
{
++
nb_free_function_calls
;
}
inline
bool
operator
==
(
pod_type
const
&
lhs
,
pod_type
const
&
rhs
)
noexcept
inline
bool
operator
==
(
pod_type
const
&
lhs
,
pod_type
const
&
rhs
)
noexcept
{
{
return
std
::
tie
(
lhs
.
a
,
lhs
.
b
,
lhs
.
c
)
==
std
::
tie
(
rhs
.
a
,
rhs
.
b
,
rhs
.
c
);
return
std
::
tie
(
lhs
.
a
,
lhs
.
b
,
lhs
.
c
)
==
std
::
tie
(
rhs
.
a
,
rhs
.
b
,
rhs
.
c
);
...
@@ -176,7 +192,7 @@ struct json_traits<udt::empty_type>
...
@@ -176,7 +192,7 @@ struct json_traits<udt::empty_type>
{
{
return
json
::
object
();
return
json
::
object
();
}
}
static
type
from_json
(
json
const
&
j
)
static
type
from_json
(
json
const
&
j
)
{
{
assert
(
j
.
is_object
()
and
j
.
empty
());
assert
(
j
.
is_object
()
and
j
.
empty
());
...
@@ -193,7 +209,7 @@ struct json_traits<udt::pod_type>
...
@@ -193,7 +209,7 @@ struct json_traits<udt::pod_type>
{
{
return
{{
"a"
,
t
.
a
},
{
"b"
,
t
.
b
},
{
"c"
,
t
.
c
}};
return
{{
"a"
,
t
.
a
},
{
"b"
,
t
.
b
},
{
"c"
,
t
.
c
}};
}
}
static
type
from_json
(
json
const
&
j
)
static
type
from_json
(
json
const
&
j
)
{
{
assert
(
j
.
is_object
());
assert
(
j
.
is_object
());
...
@@ -237,6 +253,25 @@ struct json_traits<udt::optional_type<T>>
...
@@ -237,6 +253,25 @@ struct json_traits<udt::optional_type<T>>
return
type
{
j
.
get
<
T
>
()};
return
type
{
j
.
get
<
T
>
()};
}
}
};
};
template
<>
struct
json_traits
<
udt
::
counter_type
>
{
using
type
=
udt
::
counter_type
;
static
int
nb_calls
;
static
json
to_json
(
type
)
{
++
nb_calls
;
return
json
::
object
();
}
static
void
from_json
(
json
const
&
,
type
&
)
{
++
nb_calls
;
}
};
int
json_traits
<
udt
::
counter_type
>::
nb_calls
{
0
};
}
}
...
@@ -380,6 +415,24 @@ TEST_CASE("get<> for user-defined types", "[udt]")
...
@@ -380,6 +415,24 @@ TEST_CASE("get<> for user-defined types", "[udt]")
CHECK
(
*
v
==
expected
);
CHECK
(
*
v
==
expected
);
}
}
}
}
SECTION
(
"no json_traits specialization, use of ADL"
)
{
udt
::
no_json_traits_type
val
{
42
};
auto
const
expected
=
json
{{
"a"
,
42
}};
auto
const
j
=
json
(
val
);
CHECK
(
j
==
expected
);
}
SECTION
(
"counter_type"
)
{
// check that the traits specialization is chosen
auto
const
j
=
json
{
udt
::
counter_type
{}};
CHECK
(
nlohmann
::
json_traits
<
udt
::
counter_type
>::
nb_calls
==
1
);
auto
const
elem
=
j
.
get
<
udt
::
counter_type
>
();
CHECK
(
nlohmann
::
json_traits
<
udt
::
counter_type
>::
nb_calls
==
2
);
CHECK
(
udt
::
nb_free_function_calls
==
0
);
}
}
}
TEST_CASE
(
"to_json free function"
,
"[udt]"
)
TEST_CASE
(
"to_json free function"
,
"[udt]"
)
...
@@ -487,8 +540,7 @@ TEST_CASE("from_json free function", "[udt]")
...
@@ -487,8 +540,7 @@ TEST_CASE("from_json free function", "[udt]")
{
{
udt
::
no_json_traits_type
expected
{
42
};
udt
::
no_json_traits_type
expected
{
42
};
udt
::
no_json_traits_type
res
;
udt
::
no_json_traits_type
res
;
json
j
;
auto
const
j
=
json
{{
"a"
,
42
}};
j
[
"a"
]
=
42
;
nlohmann
::
from_json
(
j
,
res
);
nlohmann
::
from_json
(
j
,
res
);
CHECK
(
res
==
expected
);
CHECK
(
res
==
expected
);
...
...
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