#ifndef SIMDJSON_CONCEPTS_H #define SIMDJSON_CONCEPTS_H #if SIMDJSON_SUPPORTS_CONCEPTS #include #include namespace simdjson { namespace concepts { namespace details { #define SIMDJSON_IMPL_CONCEPT(name, method) \ template \ concept supports_##name = !std::is_const_v && requires { \ typename std::remove_cvref_t::value_type; \ requires requires(typename std::remove_cvref_t::value_type &&val, \ T obj) { \ obj.method(std::move(val)); \ requires !requires { obj = std::move(val); }; \ }; \ }; SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back) SIMDJSON_IMPL_CONCEPT(emplace, emplace) SIMDJSON_IMPL_CONCEPT(push_back, push_back) SIMDJSON_IMPL_CONCEPT(add, add) SIMDJSON_IMPL_CONCEPT(push, push) SIMDJSON_IMPL_CONCEPT(append, append) SIMDJSON_IMPL_CONCEPT(insert, insert) SIMDJSON_IMPL_CONCEPT(op_append, operator+=) #undef SIMDJSON_IMPL_CONCEPT } // namespace details template concept is_pair = requires { typename T::first_type; typename T::second_type; } && std::same_as>; template concept string_view_like = std::is_convertible_v && !std::is_convertible_v; template concept constructible_from_string_view = std::is_constructible_v && !std::is_same_v && std::is_default_constructible_v; template concept string_view_keyed_map = string_view_like && requires(std::remove_cvref_t& m, typename M::key_type sv, typename M::mapped_type v) { { m.emplace(sv, v) } -> std::same_as>; }; /// Check if T is a container that we can append to, including: /// std::vector, std::deque, std::list, std::string, ... template concept appendable_containers = (details::supports_emplace_back || details::supports_emplace || details::supports_push_back || details::supports_push || details::supports_add || details::supports_append || details::supports_insert) && !string_view_keyed_map; /// Insert into the container however possible template constexpr decltype(auto) emplace_one(T &vec, Args &&...args) { if constexpr (details::supports_emplace_back) { return vec.emplace_back(std::forward(args)...); } else if constexpr (details::supports_emplace) { return vec.emplace(std::forward(args)...); } else if constexpr (details::supports_push_back) { return vec.push_back(std::forward(args)...); } else if constexpr (details::supports_push) { return vec.push(std::forward(args)...); } else if constexpr (details::supports_add) { return vec.add(std::forward(args)...); } else if constexpr (details::supports_append) { return vec.append(std::forward(args)...); } else if constexpr (details::supports_insert) { return vec.insert(std::forward(args)...); } else if constexpr (details::supports_op_append && sizeof...(Args) == 1) { return vec.operator+=(std::forward(args)...); } else { static_assert(!sizeof(T *), "We don't know how to add things to this container"); } } /// This checks if the container will return a reference to the newly added /// element after an insert which for example `std::vector::emplace_back` does /// since C++17; this will allow some optimizations. template concept returns_reference = appendable_containers && requires { typename std::remove_cvref_t::reference; requires requires(typename std::remove_cvref_t::value_type &&val, T obj) { { emplace_one(obj, std::move(val)) } -> std::same_as::reference>; }; }; template concept smart_pointer = requires(std::remove_cvref_t ptr) { // Check if T has a member type named element_type typename std::remove_cvref_t::element_type; // Check if T has a get() member function { ptr.get() } -> std::same_as::element_type *>; // Check if T can be dereferenced { *ptr } -> std::same_as::element_type &>; }; template concept optional_type = requires(std::remove_cvref_t obj) { typename std::remove_cvref_t::value_type; { obj.value() } -> std::same_as::value_type&>; requires requires(typename std::remove_cvref_t::value_type &&val) { obj.emplace(std::move(val)); { obj.value_or(val) } -> std::convertible_to::value_type>; }; { static_cast(obj) } -> std::same_as; // convertible to bool { obj.reset() } noexcept -> std::same_as; }; // Types we serialize as JSON strings (not as containers) template concept string_like = std::is_same_v, std::string> || std::is_same_v, std::string_view> || std::is_same_v, const char*> || std::is_same_v, char*>; // Concept that checks if a type is a container but not a string (because // strings handling must be handled differently) // Now uses iterator-based approach for broader container support template concept container_but_not_string = std::ranges::input_range && !string_like && !concepts::string_view_keyed_map; // Concept: Indexable container that is not a string or associative container // Accepts: std::vector, std::array, std::deque (have operator[], value_type, not string_like) // Rejects: std::string (string_like), std::list (no operator[]), std::map (has key_type) template concept indexable_container = requires { typename Container::value_type; requires !concepts::string_like; requires !requires { typename Container::key_type; }; // Reject maps/sets requires requires(Container& c, std::size_t i) { { c[i] } -> std::convertible_to; }; }; // Variable template to use with std::meta::substitute template constexpr bool indexable_container_v = indexable_container; } // namespace concepts /** * We use tag_invoke as our customization point mechanism. */ template concept tag_invocable = requires(Tag tag, Args... args) { tag_invoke(std::forward(tag), std::forward(args)...); }; template concept nothrow_tag_invocable = tag_invocable && requires(Tag tag, Args... args) { { tag_invoke(std::forward(tag), std::forward(args)...) } noexcept; }; } // namespace simdjson #endif // SIMDJSON_SUPPORTS_CONCEPTS #endif // SIMDJSON_CONCEPTS_H