#ifndef XRPL_JSON_MULTIAPIJSON_H_INCLUDED #define XRPL_JSON_MULTIAPIJSON_H_INCLUDED #include #include #include #include #include #include #include #include #include namespace ripple { namespace detail { template constexpr bool is_integral_constant = false; template constexpr bool is_integral_constant&> = true; template constexpr bool is_integral_constant const&> = true; template concept some_integral_constant = detail::is_integral_constant; // This class is designed to wrap a collection of _almost_ identical Json::Value // objects, indexed by version (i.e. there is some mapping of version to object // index). It is used e.g. when we need to publish JSON data to users supporting // different API versions. We allow manipulation and inspection of all objects // at once with `isMember` and `set`, and also individual inspection and updates // of an object selected by the user by version, using `visitor_t` nested type. template struct MultiApiJson { static_assert(MinVer <= MaxVer); static constexpr auto valid(unsigned int v) noexcept -> bool { return v >= MinVer && v <= MaxVer; } static constexpr auto index(unsigned int v) noexcept -> std::size_t { return (v < MinVer) ? 0 : static_cast(v - MinVer); } constexpr static std::size_t size = MaxVer + 1 - MinVer; std::array val = {}; explicit MultiApiJson(Json::Value const& init = {}) { if (init == Json::Value{}) return; // All elements are already default-initialized for (auto& v : val) v = init; } void set(char const* key, auto const& v) requires std::constructible_from { for (auto& a : this->val) a[key] = v; } // Intentionally not using class enum here, MultivarJson is scope enough enum IsMemberResult : int { none = 0, some, all }; [[nodiscard]] IsMemberResult isMember(char const* key) const { int count = 0; for (auto& a : this->val) if (a.isMember(key)) count += 1; return (count == 0 ? none : (count < size ? some : all)); } static constexpr struct visitor_t final { // integral_constant version, extra arguments template < typename Json, unsigned int Version, typename... Args, typename Fn> requires std::same_as, MultiApiJson> auto operator()( Json& json, std::integral_constant const version, Fn fn, Args&&... args) const -> std::invoke_result_t< Fn, decltype(json.val[0]), std::integral_constant, Args&&...> { static_assert( valid(Version) && index(Version) >= 0 && index(Version) < size); return std::invoke( fn, json.val[index(Version)], version, std::forward(args)...); } // integral_constant version, Json only template requires std::same_as, MultiApiJson> auto operator()( Json& json, std::integral_constant const, Fn fn) const -> std::invoke_result_t { static_assert( valid(Version) && index(Version) >= 0 && index(Version) < size); return std::invoke(fn, json.val[index(Version)]); } // unsigned int version, extra arguments template < typename Json, typename Version, typename... Args, typename Fn> requires(!some_integral_constant) && std::convertible_to && std::same_as, MultiApiJson> auto operator()(Json& json, Version version, Fn fn, Args&&... args) const -> std:: invoke_result_t { XRPL_ASSERT( valid(version) && index(version) >= 0 && index(version) < size, "ripple::detail::MultiApiJson::operator() : valid " "version"); return std::invoke( fn, json.val[index(version)], version, std::forward(args)...); } // unsigned int version, Json only template requires(!some_integral_constant) && std::convertible_to && std::same_as, MultiApiJson> auto operator()(Json& json, Version version, Fn fn) const -> std::invoke_result_t { XRPL_ASSERT( valid(version) && index(version) >= 0 && index(version) < size, "ripple::detail::MultiApiJson::operator() : valid version"); return std::invoke(fn, json.val[index(version)]); } } visitor = {}; auto visit() { return [self = this](auto... args) requires requires { visitor( std::declval(), std::declval()...); } { return visitor(*self, std::forward(args)...); }; } auto visit() const { return [self = this](auto... args) requires requires { visitor( std::declval(), std::declval()...); } { return visitor(*self, std::forward(args)...); }; } template auto visit(Args... args) -> std::invoke_result_t requires(sizeof...(args) > 0) && requires { visitor(*this, std::forward(args)...); } { return visitor(*this, std::forward(args)...); } template auto visit(Args... args) const -> std::invoke_result_t requires(sizeof...(args) > 0) && requires { visitor(*this, std::forward(args)...); } { return visitor(*this, std::forward(args)...); } }; } // namespace detail // Wrapper for Json for all supported API versions. using MultiApiJson = detail:: MultiApiJson; } // namespace ripple #endif