1 #pragma once
2 
3 #include <limits> // numeric_limits
4 #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
5 #include <utility> // declval
6 
7 #include <nlohmann/detail/iterators/iterator_traits.hpp>
8 #include <nlohmann/detail/macro_scope.hpp>
9 #include <nlohmann/detail/meta/cpp_future.hpp>
10 #include <nlohmann/detail/meta/detected.hpp>
11 #include <nlohmann/json_fwd.hpp>
12 
13 namespace nlohmann
14 {
15 /*!
16 @brief detail namespace with internal helper functions
17 
18 This namespace collects functions that should not be exposed,
19 implementations of some @ref basic_json methods, and meta-programming helpers.
20 
21 @since version 2.1.0
22 */
23 namespace detail
24 {
25 /////////////
26 // helpers //
27 /////////////
28 
29 // Note to maintainers:
30 //
31 // Every trait in this file expects a non CV-qualified type.
32 // The only exceptions are in the 'aliases for detected' section
33 // (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
34 //
35 // In this case, T has to be properly CV-qualified to constraint the function arguments
36 // (e.g. to_json(BasicJsonType&, const T&))
37 
38 template<typename> struct is_basic_json : std::false_type {};
39 
40 NLOHMANN_BASIC_JSON_TPL_DECLARATION
41 struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
42 
43 //////////////////////
44 // json_ref helpers //
45 //////////////////////
46 
47 template<typename>
48 class json_ref;
49 
50 template<typename>
51 struct is_json_ref : std::false_type {};
52 
53 template<typename T>
54 struct is_json_ref<json_ref<T>> : std::true_type {};
55 
56 //////////////////////////
57 // aliases for detected //
58 //////////////////////////
59 
60 template<typename T>
61 using mapped_type_t = typename T::mapped_type;
62 
63 template<typename T>
64 using key_type_t = typename T::key_type;
65 
66 template<typename T>
67 using value_type_t = typename T::value_type;
68 
69 template<typename T>
70 using difference_type_t = typename T::difference_type;
71 
72 template<typename T>
73 using pointer_t = typename T::pointer;
74 
75 template<typename T>
76 using reference_t = typename T::reference;
77 
78 template<typename T>
79 using iterator_category_t = typename T::iterator_category;
80 
81 template<typename T>
82 using iterator_t = typename T::iterator;
83 
84 template<typename T, typename... Args>
85 using to_json_function = decltype(T::to_json(std::declval<Args>()...));
86 
87 template<typename T, typename... Args>
88 using from_json_function = decltype(T::from_json(std::declval<Args>()...));
89 
90 template<typename T, typename U>
91 using get_template_function = decltype(std::declval<T>().template get<U>());
92 
93 // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
94 template<typename BasicJsonType, typename T, typename = void>
95 struct has_from_json : std::false_type {};
96 
97 // trait checking if j.get<T> is valid
98 // use this trait instead of std::is_constructible or std::is_convertible,
99 // both rely on, or make use of implicit conversions, and thus fail when T
100 // has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
101 template <typename BasicJsonType, typename T>
102 struct is_getable
103 {
104     static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
105 };
106 
107 template<typename BasicJsonType, typename T>
108 struct has_from_json < BasicJsonType, T,
109            enable_if_t < !is_basic_json<T>::value >>
110 {
111     using serializer = typename BasicJsonType::template json_serializer<T, void>;
112 
113     static constexpr bool value =
114         is_detected_exact<void, from_json_function, serializer,
115         const BasicJsonType&, T&>::value;
116 };
117 
118 // This trait checks if JSONSerializer<T>::from_json(json const&) exists
119 // this overload is used for non-default-constructible user-defined-types
120 template<typename BasicJsonType, typename T, typename = void>
121 struct has_non_default_from_json : std::false_type {};
122 
123 template<typename BasicJsonType, typename T>
124 struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
125 {
126     using serializer = typename BasicJsonType::template json_serializer<T, void>;
127 
128     static constexpr bool value =
129         is_detected_exact<T, from_json_function, serializer,
130         const BasicJsonType&>::value;
131 };
132 
133 // This trait checks if BasicJsonType::json_serializer<T>::to_json exists
134 // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
135 template<typename BasicJsonType, typename T, typename = void>
136 struct has_to_json : std::false_type {};
137 
138 template<typename BasicJsonType, typename T>
139 struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
140 {
141     using serializer = typename BasicJsonType::template json_serializer<T, void>;
142 
143     static constexpr bool value =
144         is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
145         T>::value;
146 };
147 
148 
149 ///////////////////
150 // is_ functions //
151 ///////////////////
152 
153 template<typename T, typename = void>
154 struct is_iterator_traits : std::false_type {};
155 
156 template<typename T>
157 struct is_iterator_traits<iterator_traits<T>>
158 {
159   private:
160     using traits = iterator_traits<T>;
161 
162   public:
163     static constexpr auto value =
164         is_detected<value_type_t, traits>::value &&
165         is_detected<difference_type_t, traits>::value &&
166         is_detected<pointer_t, traits>::value &&
167         is_detected<iterator_category_t, traits>::value &&
168         is_detected<reference_t, traits>::value;
169 };
170 
171 // source: https://stackoverflow.com/a/37193089/4116453
172 
173 template<typename T, typename = void>
174 struct is_complete_type : std::false_type {};
175 
176 template<typename T>
177 struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
178 
179 template<typename BasicJsonType, typename CompatibleObjectType,
180          typename = void>
181 struct is_compatible_object_type_impl : std::false_type {};
182 
183 template<typename BasicJsonType, typename CompatibleObjectType>
184 struct is_compatible_object_type_impl <
185     BasicJsonType, CompatibleObjectType,
186     enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
187     is_detected<key_type_t, CompatibleObjectType>::value >>
188 {
189 
190     using object_t = typename BasicJsonType::object_t;
191 
192     // macOS's is_constructible does not play well with nonesuch...
193     static constexpr bool value =
194         std::is_constructible<typename object_t::key_type,
195         typename CompatibleObjectType::key_type>::value &&
196         std::is_constructible<typename object_t::mapped_type,
197         typename CompatibleObjectType::mapped_type>::value;
198 };
199 
200 template<typename BasicJsonType, typename CompatibleObjectType>
201 struct is_compatible_object_type
202     : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
203 
204 template<typename BasicJsonType, typename ConstructibleObjectType,
205          typename = void>
206 struct is_constructible_object_type_impl : std::false_type {};
207 
208 template<typename BasicJsonType, typename ConstructibleObjectType>
209 struct is_constructible_object_type_impl <
210     BasicJsonType, ConstructibleObjectType,
211     enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
212     is_detected<key_type_t, ConstructibleObjectType>::value >>
213 {
214     using object_t = typename BasicJsonType::object_t;
215 
216     static constexpr bool value =
217         (std::is_default_constructible<ConstructibleObjectType>::value &&
218          (std::is_move_assignable<ConstructibleObjectType>::value ||
219           std::is_copy_assignable<ConstructibleObjectType>::value) &&
220          (std::is_constructible<typename ConstructibleObjectType::key_type,
221           typename object_t::key_type>::value &&
222           std::is_same <
223           typename object_t::mapped_type,
224           typename ConstructibleObjectType::mapped_type >::value)) ||
225         (has_from_json<BasicJsonType,
226          typename ConstructibleObjectType::mapped_type>::value ||
227          has_non_default_from_json <
228          BasicJsonType,
229          typename ConstructibleObjectType::mapped_type >::value);
230 };
231 
232 template<typename BasicJsonType, typename ConstructibleObjectType>
233 struct is_constructible_object_type
234     : is_constructible_object_type_impl<BasicJsonType,
235       ConstructibleObjectType> {};
236 
237 template<typename BasicJsonType, typename CompatibleStringType,
238          typename = void>
239 struct is_compatible_string_type_impl : std::false_type {};
240 
241 template<typename BasicJsonType, typename CompatibleStringType>
242 struct is_compatible_string_type_impl <
243     BasicJsonType, CompatibleStringType,
244     enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
245     value_type_t, CompatibleStringType>::value >>
246 {
247     static constexpr auto value =
248         std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
249 };
250 
251 template<typename BasicJsonType, typename ConstructibleStringType>
252 struct is_compatible_string_type
253     : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
254 
255 template<typename BasicJsonType, typename ConstructibleStringType,
256          typename = void>
257 struct is_constructible_string_type_impl : std::false_type {};
258 
259 template<typename BasicJsonType, typename ConstructibleStringType>
260 struct is_constructible_string_type_impl <
261     BasicJsonType, ConstructibleStringType,
262     enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
263     value_type_t, ConstructibleStringType>::value >>
264 {
265     static constexpr auto value =
266         std::is_constructible<ConstructibleStringType,
267         typename BasicJsonType::string_t>::value;
268 };
269 
270 template<typename BasicJsonType, typename ConstructibleStringType>
271 struct is_constructible_string_type
272     : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
273 
274 template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
275 struct is_compatible_array_type_impl : std::false_type {};
276 
277 template<typename BasicJsonType, typename CompatibleArrayType>
278 struct is_compatible_array_type_impl <
279     BasicJsonType, CompatibleArrayType,
280     enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&&
281     is_detected<iterator_t, CompatibleArrayType>::value&&
282 // This is needed because json_reverse_iterator has a ::iterator type...
283 // Therefore it is detected as a CompatibleArrayType.
284 // The real fix would be to have an Iterable concept.
285     !is_iterator_traits <
286     iterator_traits<CompatibleArrayType >>::value >>
287 {
288     static constexpr bool value =
289         std::is_constructible<BasicJsonType,
290         typename CompatibleArrayType::value_type>::value;
291 };
292 
293 template<typename BasicJsonType, typename CompatibleArrayType>
294 struct is_compatible_array_type
295     : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
296 
297 template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
298 struct is_constructible_array_type_impl : std::false_type {};
299 
300 template<typename BasicJsonType, typename ConstructibleArrayType>
301 struct is_constructible_array_type_impl <
302     BasicJsonType, ConstructibleArrayType,
303     enable_if_t<std::is_same<ConstructibleArrayType,
304     typename BasicJsonType::value_type>::value >>
305             : std::true_type {};
306 
307 template<typename BasicJsonType, typename ConstructibleArrayType>
308 struct is_constructible_array_type_impl <
309     BasicJsonType, ConstructibleArrayType,
310     enable_if_t < !std::is_same<ConstructibleArrayType,
311     typename BasicJsonType::value_type>::value&&
312     std::is_default_constructible<ConstructibleArrayType>::value&&
313 (std::is_move_assignable<ConstructibleArrayType>::value ||
314  std::is_copy_assignable<ConstructibleArrayType>::value)&&
315 is_detected<value_type_t, ConstructibleArrayType>::value&&
316 is_detected<iterator_t, ConstructibleArrayType>::value&&
317 is_complete_type <
318 detected_t<value_type_t, ConstructibleArrayType >>::value >>
319 {
320     static constexpr bool value =
321         // This is needed because json_reverse_iterator has a ::iterator type,
322         // furthermore, std::back_insert_iterator (and other iterators) have a
323         // base class `iterator`... Therefore it is detected as a
324         // ConstructibleArrayType. The real fix would be to have an Iterable
325         // concept.
326         !is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&
327 
328         (std::is_same<typename ConstructibleArrayType::value_type,
329          typename BasicJsonType::array_t::value_type>::value ||
330          has_from_json<BasicJsonType,
331          typename ConstructibleArrayType::value_type>::value ||
332          has_non_default_from_json <
333          BasicJsonType, typename ConstructibleArrayType::value_type >::value);
334 };
335 
336 template<typename BasicJsonType, typename ConstructibleArrayType>
337 struct is_constructible_array_type
338     : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
339 
340 template<typename RealIntegerType, typename CompatibleNumberIntegerType,
341          typename = void>
342 struct is_compatible_integer_type_impl : std::false_type {};
343 
344 template<typename RealIntegerType, typename CompatibleNumberIntegerType>
345 struct is_compatible_integer_type_impl <
346     RealIntegerType, CompatibleNumberIntegerType,
347     enable_if_t < std::is_integral<RealIntegerType>::value&&
348     std::is_integral<CompatibleNumberIntegerType>::value&&
349     !std::is_same<bool, CompatibleNumberIntegerType>::value >>
350 {
351     // is there an assert somewhere on overflows?
352     using RealLimits = std::numeric_limits<RealIntegerType>;
353     using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
354 
355     static constexpr auto value =
356         std::is_constructible<RealIntegerType,
357         CompatibleNumberIntegerType>::value &&
358         CompatibleLimits::is_integer &&
359         RealLimits::is_signed == CompatibleLimits::is_signed;
360 };
361 
362 template<typename RealIntegerType, typename CompatibleNumberIntegerType>
363 struct is_compatible_integer_type
364     : is_compatible_integer_type_impl<RealIntegerType,
365       CompatibleNumberIntegerType> {};
366 
367 template<typename BasicJsonType, typename CompatibleType, typename = void>
368 struct is_compatible_type_impl: std::false_type {};
369 
370 template<typename BasicJsonType, typename CompatibleType>
371 struct is_compatible_type_impl <
372     BasicJsonType, CompatibleType,
373     enable_if_t<is_complete_type<CompatibleType>::value >>
374 {
375     static constexpr bool value =
376         has_to_json<BasicJsonType, CompatibleType>::value;
377 };
378 
379 template<typename BasicJsonType, typename CompatibleType>
380 struct is_compatible_type
381     : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
382 
383 // https://en.cppreference.com/w/cpp/types/conjunction
384 template<class...> struct conjunction : std::true_type { };
385 template<class B1> struct conjunction<B1> : B1 { };
386 template<class B1, class... Bn>
387 struct conjunction<B1, Bn...>
388 : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
389 
390 template<typename T1, typename T2>
391 struct is_constructible_tuple : std::false_type {};
392 
393 template<typename T1, typename... Args>
394 struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {};
395 }  // namespace detail
396 }  // namespace nlohmann
397