/* * Copyright (C) 2017 Charybdis Development Team * Copyright (C) 2017 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #pragma once #define HAVE_IRCD_JSON_TUPLE_H // // Here we represent a JSON object with a named tuple, allowing the programmer // to create a structure specifying all of the potentially valid members of the // object. Access to a specific member is O(1) just like a native `struct` // rather than a linear or logn lookup into a map. The size of the tuple is // extremely minimal: only the size of the values it stores. This is because // the member keys and type data are all static or dealt with at compile time. // // Create and use a tuple to efficiently extract members from a json::object. // The tuple will populate its own members during a single-pass iteration of // the JSON input. If the JSON does not contain a member specified in the // tuple, the value will be default initialized. If the JSON contains a member // not specified in the tuple, it is ignored. If you need to know all of the // members specified in the JSON dynamically, use a json::index or iterate // manually. // namespace ircd { namespace json { // // Non-template common base for all ircd::json::tuple templates. // struct tuple_base { // class must be empty for EBO struct member; }; // // Non-template common base for all tuple members // struct tuple_base::member { }; /////////////////////////////////////////////////////////////////////////////// // // ircd::json::tuple template. Create your own struct inheriting this // class template with the members. // template struct tuple :tuple_base ,std::tuple { using tuple_type = std::tuple; using super_type = tuple; static constexpr size_t size(); tuple(const json::object &); tuple() = default; }; template constexpr size_t tuple::size() { return std::tuple_size(); } /////////////////////////////////////////////////////////////////////////////// // // tuple member template. Specify a list of these in the tuple template to // form the members of the object. // template struct member :tuple_base::member { using key_type = const char *; using value_type = T; static constexpr auto &key{name}; T value; operator const T &() const; operator T &(); member(T&& value); member() = default; }; template member::member(T&& value) :value{value} { } template member::operator T &() { return value; } template member::operator const T &() const { return value; } /////////////////////////////////////////////////////////////////////////////// // // Tools // template using tuple_type = typename tuple::tuple_type; template using tuple_size = std::tuple_size>; template using tuple_element = typename std::tuple_element>::type; template using tuple_value_type = typename tuple_element::value_type; template auto & stdcast(const tuple &o) { return static_cast(o); } template auto & stdcast(tuple &o) { return static_cast(o); } template constexpr size_t size() { return tuple_size::value; } template constexpr auto & key() { return tuple_element::key; } template auto & key(const tuple &t) { return get(t).key; } template constexpr typename std::enable_if(), size_t>::type indexof(const char *const &name) { return size(); } template constexpr typename std::enable_if(), size_t>::type indexof(const char *const &name) { const auto equal { _constexpr_equal(key(), name) }; return equal? i : indexof(name); } template auto & get(const tuple &t) { return std::get(t); } template auto & get(tuple &t) { return std::get(t); } template auto & val(const tuple &t) { using value_type = tuple_value_type, i>; return static_cast(get(t)); } template auto & val(tuple &t) { using value_type = tuple_value_type, i>; return static_cast(get(t)); } template auto & val(const tuple &t) { return val>(name)>(t); } template auto & val(tuple &t) { return val>(name)>(t); } template const tuple_value_type, indexof>(name)> & at(const tuple &t) { constexpr size_t idx { indexof>(name) }; const auto &ret { val(t) }; using value_type = tuple_value_type, idx>; //TODO: does tcmalloc zero this or huh? if(ret == value_type{}) throw not_found("%s", name); return ret; } template tuple_value_type, indexof>(name)> & at(tuple &t) { constexpr size_t idx { indexof>(name) }; auto &ret { val(t) }; using value_type = tuple_value_type, idx>; //TODO: does tcmalloc zero this or huh? if(ret == value_type{}) throw not_found("%s", name); return ret; } template typename std::enable_if(), void>::type for_each(const tuple &t, function&& f) {} template typename std::enable_if(), void>::type for_each(tuple &t, function&& f) {} template typename std::enable_if(), void>::type for_each(const tuple &t, function&& f) { f(key(t), val(t)); for_each(t, std::forward(f)); } template typename std::enable_if(), void>::type for_each(tuple &t, function&& f) { f(key(t), val(t)); for_each(t, std::forward(f)); } template typename std::enable_if<(i < 0), void>::type rfor_each(const tuple &t, function&& f) {} template typename std::enable_if<(i < 0), void>::type rfor_each(tuple &t, function&& f) {} template() - 1> typename std::enable_if(), void>::type rfor_each(const tuple &t, function&& f) { f(key(t), val(t)); rfor_each(t, std::forward(f)); } template() - 1> typename std::enable_if(), void>::type rfor_each(tuple &t, function&& f) { f(key(t), val(t)); rfor_each(t, std::forward(f)); } template typename std::enable_if(), bool>::type until(const tuple &t, function&& f) { return true; } template typename std::enable_if(), bool>::type until(tuple &t, function&& f) { return true; } template typename std::enable_if(), bool>::type until(const tuple &t, function&& f) { return f(key(t), val(t))? until(t, std::forward(f)): false; } template typename std::enable_if(), bool>::type until(tuple &t, function&& f) { return f(key(t), val(t))? until(t, std::forward(f)): false; } template typename std::enable_if<(i < 0), bool>::type runtil(const tuple &t, function&& f) { return true; } template typename std::enable_if<(i < 0), bool>::type runtil(tuple &t, function&& f) { return true; } template() - 1> typename std::enable_if(), bool>::type runtil(const tuple &t, function&& f) { return f(key(t), val(t))? runtil(t, std::forward(f)): false; } template() - 1> typename std::enable_if(), bool>::type runtil(tuple &t, function&& f) { return f(key(t), val(t))? runtil(t, std::forward(f)): false; } template bool at(tuple &t, const string_view &name, function&& f) { return until(t, [&name, &f] (const string_view &key, auto &member) { if(key != name) return true; f(member); return false; }); } template bool at(const tuple &t, const string_view &name, function&& f) { return until(t, [&name, &f] (const string_view &key, const auto &member) { if(key != name) return true; f(member); return false; }); } template tuple::tuple(const json::object &object) { //TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that? std::for_each(std::begin(object), std::end(object), [this] (const auto &member) { at(*this, member.first, [&member] (auto &target) { using target_type = decltype(target); using cast_type = typename std::remove_reference::type; try { target = lex_cast(member.second); } catch(const bad_lex_cast &e) { throw parse_error("member '%s' must convert to '%s'", member.first, typeid(target_type).name()); } }); }); } } // namespace json } // namespace ircd