#pragma once #include #include #include #include namespace utils { namespace numeric_literals { constexpr std::string_view NaN = "NaN"; constexpr std::string_view infinity = "inf"; constexpr std::string_view infinity_negative = "-inf"; } // namespace numeric_literals inline void from_json(const nlohmann::json& j, sdbusplus::message::object_path& o) { o = j.get(); } inline void from_json(const nlohmann::json& j, std::vector& o) { o.clear(); for (const nlohmann::json& item : j) { o.emplace_back(item.get()); } } inline void to_json(nlohmann::json& j, const double& val) { if (std::isnan(val)) { j = numeric_literals::NaN; } else if (val == std::numeric_limits::infinity()) { j = numeric_literals::infinity; } else if (val == -std::numeric_limits::infinity()) { j = numeric_literals::infinity_negative; } else { j = val; } } inline void from_json(const nlohmann::json& j, double& val) { if (j.is_number()) { val = j.get(); } else { auto str_val = j.get(); if (str_val == numeric_literals::NaN) { val = std::numeric_limits::quiet_NaN(); } else if (str_val == numeric_literals::infinity) { val = std::numeric_limits::infinity(); } else if (str_val == numeric_literals::infinity_negative) { val = -std::numeric_limits::infinity(); } else { throw std::invalid_argument("Unknown numeric literal"); } } } namespace detail { template struct has_utils_from_json { template static U& ref(); template static std::true_type check( decltype(utils::from_json(ref(), ref()))*); template static std::false_type check(...); static constexpr bool value = decltype(check>(nullptr))::value; }; template constexpr bool has_utils_from_json_v = has_utils_from_json::value; template struct has_utils_to_json { template static U& ref(); template static std::true_type check(decltype(utils::to_json(ref(), ref()))*); template static std::false_type check(...); static constexpr bool value = decltype(check>(nullptr))::value; }; template constexpr bool has_utils_to_json_v = has_utils_to_json::value; bool eq(const auto& a, const auto& b) { if constexpr (std::is_same, double>()) { if (std::isnan(a)) { return std::isnan(b); } } return a == b; } } // namespace detail template struct LabeledTuple; template struct LabeledTuple, Labels...> { static_assert(sizeof...(Args) == sizeof...(Labels)); using tuple_type = std::tuple; LabeledTuple() = default; LabeledTuple(const LabeledTuple&) = default; LabeledTuple(LabeledTuple&&) = default; explicit LabeledTuple(tuple_type v) : value(std::move(v)) {} LabeledTuple(Args... args) : value(std::move(args)...) {} LabeledTuple& operator=(const LabeledTuple&) = default; LabeledTuple& operator=(LabeledTuple&&) = default; nlohmann::json to_json() const { nlohmann::json j; to_json_all(j, std::make_index_sequence()); return j; } const tuple_type& to_tuple() const { return value; } void from_json(const nlohmann::json& j) { from_json_all(j, std::make_index_sequence()); } std::string dump() const { return to_json().dump(); } template const auto& at_index() const { return std::get(value); } template auto& at_index() { return std::get(value); } template const auto& at_label() const { return find_item<0, Label>(*this); } template auto& at_label() { return find_item<0, Label>(*this); } bool operator==(const LabeledTuple& other) const { return std::apply([&](auto&&... x) { return std::apply([&](auto&&... y) { return (true && ... && detail::eq(x, y)); }, value); }, other.value); } bool operator<(const LabeledTuple& other) const { return value < other.value; } private: template void to_json_all(nlohmann::json& j, std::index_sequence) const { (to_json_item(j), ...); } template void to_json_item(nlohmann::json& j) const { using Label = std::tuple_element_t>; using T = std::tuple_element_t; nlohmann::json& item = j[Label::str()]; if constexpr (detail::has_utils_to_json_v) { utils::to_json(item, std::get(value)); } else { item = std::get(value); } } template void from_json_all(const nlohmann::json& j, std::index_sequence) { (from_json_item(j), ...); } template void from_json_item(const nlohmann::json& j) { using Label = std::tuple_element_t>; using T = std::tuple_element_t; const nlohmann::json& item = j.at(Label::str()); if constexpr (detail::has_utils_from_json_v) { T& v = std::get(value); utils::from_json(item, v); } else { std::get(value) = item.get(); } } template static auto& find_item(Self& self) { if constexpr (std::is_same_v>>) { return std::get(self.value); } else { static_assert(Idx + 1 < sizeof...(Args), "Label not found in LabeledTuple"); return find_item(self); } } tuple_type value; }; template inline void to_json(nlohmann::json& json, const LabeledTuple, Labels...>& tuple) { json = tuple.to_json(); } template inline void from_json(const nlohmann::json& json, LabeledTuple, Labels...>& tuple) { tuple.from_json(json); } } // namespace utils