simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
concepts.h
1#ifndef SIMDJSON_CONCEPTS_H
2#define SIMDJSON_CONCEPTS_H
3#if SIMDJSON_SUPPORTS_CONCEPTS
4
5#include <concepts>
6#include <string_view>
7#include <type_traits>
8
9namespace simdjson {
10namespace concepts {
11
12namespace details {
13#define SIMDJSON_IMPL_CONCEPT(name, method) \
14 template <typename T> \
15 concept supports_##name = !std::is_const_v<T> && requires { \
16 typename std::remove_cvref_t<T>::value_type; \
17 requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
18 T obj) { \
19 obj.method(std::move(val)); \
20 requires !requires { obj = std::move(val); }; \
21 }; \
22 };
23
24SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back)
25SIMDJSON_IMPL_CONCEPT(emplace, emplace)
26SIMDJSON_IMPL_CONCEPT(push_back, push_back)
27SIMDJSON_IMPL_CONCEPT(add, add)
28SIMDJSON_IMPL_CONCEPT(push, push)
29SIMDJSON_IMPL_CONCEPT(append, append)
30SIMDJSON_IMPL_CONCEPT(insert, insert)
31SIMDJSON_IMPL_CONCEPT(op_append, operator+=)
32
33#undef SIMDJSON_IMPL_CONCEPT
34} // namespace details
35
36template <typename T>
37concept is_pair = requires { typename T::first_type; typename T::second_type; } &&
38 std::same_as<T, std::pair<typename T::first_type, typename T::second_type>>;
39template <typename T>
40concept string_view_like = std::is_convertible_v<T, std::string_view> &&
41 !std::is_convertible_v<T, const char*>;
42
43template<typename T>
44concept constructible_from_string_view = std::is_constructible_v<T, std::string_view>
45 && !std::is_same_v<T, std::string_view>
46 && std::is_default_constructible_v<T>;
47
48#if SIMDJSON_SUPPORTS_CHAR8_T
54template<typename T>
55concept constructible_from_u8string_view = std::is_constructible_v<T, std::u8string_view>
56 && !std::is_same_v<T, std::u8string_view>
57 && !std::is_constructible_v<T, std::string_view>
58 && std::is_default_constructible_v<T>;
59#endif // SIMDJSON_SUPPORTS_CHAR8_T
60
61template<typename M>
62concept string_view_keyed_map = string_view_like<typename M::key_type>
63 && requires(std::remove_cvref_t<M>& m, typename M::key_type sv, typename M::mapped_type v) {
64 { m.emplace(sv, v) } -> std::same_as<std::pair<typename M::iterator, bool>>;
65};
66
69template <typename T>
70concept appendable_containers =
71 (details::supports_emplace_back<T> || details::supports_emplace<T> ||
72 details::supports_push_back<T> || details::supports_push<T> ||
73 details::supports_add<T> || details::supports_append<T> ||
74 details::supports_insert<T>) && !string_view_keyed_map<T>;
75
77template <appendable_containers T, typename... Args>
78constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
79 if constexpr (details::supports_emplace_back<T>) {
80 return vec.emplace_back(std::forward<Args>(args)...);
81 } else if constexpr (details::supports_emplace<T>) {
82 return vec.emplace(std::forward<Args>(args)...);
83 } else if constexpr (details::supports_push_back<T>) {
84 return vec.push_back(std::forward<Args>(args)...);
85 } else if constexpr (details::supports_push<T>) {
86 return vec.push(std::forward<Args>(args)...);
87 } else if constexpr (details::supports_add<T>) {
88 return vec.add(std::forward<Args>(args)...);
89 } else if constexpr (details::supports_append<T>) {
90 return vec.append(std::forward<Args>(args)...);
91 } else if constexpr (details::supports_insert<T>) {
92 return vec.insert(std::forward<Args>(args)...);
93 } else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
94 return vec.operator+=(std::forward<Args>(args)...);
95 } else {
96 static_assert(!sizeof(T *),
97 "We don't know how to add things to this container");
98 }
99}
100
104template <typename T>
105concept returns_reference = appendable_containers<T> && requires {
106 typename std::remove_cvref_t<T>::reference;
107 requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
108 {
109 emplace_one(obj, std::move(val))
110 } -> std::same_as<typename std::remove_cvref_t<T>::reference>;
111 };
112};
113
114template <typename T>
115concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
116 // Check if T has a member type named element_type
117 typename std::remove_cvref_t<T>::element_type;
118
119 // Check if T has a get() member function
120 {
121 ptr.get()
122 } -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
123
124 // Check if T can be dereferenced
125 { *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
126};
127
128template <typename T>
129concept optional_type = requires(std::remove_cvref_t<T> obj) {
130 typename std::remove_cvref_t<T>::value_type;
131 { obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
132 requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
133 obj.emplace(std::move(val));
134 {
135 obj.value_or(val)
136 } -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
137 };
138 { static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
139 { obj.reset() } noexcept -> std::same_as<void>;
140};
141
142
143
144// Types we serialize as JSON strings (not as containers)
145template <typename T>
146concept string_like =
147 std::is_same_v<std::remove_cvref_t<T>, std::string> ||
148 std::is_same_v<std::remove_cvref_t<T>, std::string_view> ||
149 std::is_same_v<std::remove_cvref_t<T>, const char*> ||
150 std::is_same_v<std::remove_cvref_t<T>, char*>;
151
152// Concept that checks if a type is a container but not a string (because
153// strings handling must be handled differently)
154// Now uses iterator-based approach for broader container support
155//
156// Optional types are excluded on purpose. Since C++26 (P3168), std::optional
157// is itself a range, so without the exclusion an std::optional would match
158// both this concept and optional_type, making the container and the optional
159// overloads of atom()/append() ambiguous. See issue 2827.
160template <typename T>
161concept container_but_not_string =
162 std::ranges::input_range<T> && !string_like<T> && !concepts::string_view_keyed_map<T>
163 && !concepts::optional_type<T>;
164
165
166
167// Concept: Indexable container that is not a string or associative container
168// Accepts: std::vector, std::array, std::deque (have operator[], value_type, not string_like)
169// Rejects: std::string (string_like), std::list (no operator[]), std::map (has key_type)
170template<typename Container>
171concept indexable_container = requires {
172 typename Container::value_type;
173 requires !concepts::string_like<Container>;
174 requires !requires { typename Container::key_type; }; // Reject maps/sets
175 requires requires(Container& c, std::size_t i) {
176 { c[i] } -> std::convertible_to<typename Container::value_type>;
177 };
178};
179
180
181// Variable template to use with std::meta::substitute
182template<typename Container>
183constexpr bool indexable_container_v = indexable_container<Container>;
184
185} // namespace concepts
186
187
191template <typename Tag, typename... Args>
192concept tag_invocable = requires(Tag tag, Args... args) {
193 tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
194};
195
196template <typename Tag, typename... Args>
197concept nothrow_tag_invocable =
198 tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
199 {
200 tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
201 } noexcept;
202 };
203
204} // namespace simdjson
205#endif // SIMDJSON_SUPPORTS_CONCEPTS
206#endif // SIMDJSON_CONCEPTS_H
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8