From 889b9d44930717437609f2c438600bb294ffa274 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer <weiglhofer@fias.uni-frankfurt.de> Date: Tue, 6 Jun 2023 12:34:09 +0000 Subject: [PATCH] algo: Rework SerializableEnum.h - Renamed header to EnumDict.h - Implementation detail are now hidden with `CBM_ENUM_DICT`, this also simplifies enum declarations - Use std::vector underneath instead of std::set --- .clang-format | 2 + algo/base/RecoParams.h | 31 +++----- algo/base/config/Yaml.h | 7 +- algo/base/util/EnumDict.h | 116 ++++++++++++++++++++++++++++++ algo/base/util/SerializableEnum.h | 111 ---------------------------- 5 files changed, 131 insertions(+), 136 deletions(-) create mode 100644 algo/base/util/EnumDict.h delete mode 100644 algo/base/util/SerializableEnum.h diff --git a/.clang-format b/.clang-format index 4e17fee5b3..c7526acf06 100644 --- a/.clang-format +++ b/.clang-format @@ -142,6 +142,8 @@ SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false +WhitespaceSensitiveMacros: ['CBM_ENUM_DICT'] + Standard: c++11 TabWidth: 8 diff --git a/algo/base/RecoParams.h b/algo/base/RecoParams.h index 184e78a1e5..b1f08da263 100644 --- a/algo/base/RecoParams.h +++ b/algo/base/RecoParams.h @@ -8,7 +8,7 @@ #include "Prelude.h" #include "config/Property.h" -#include "util/SerializableEnum.h" +#include "util/EnumDict.h" namespace cbm::algo { @@ -97,27 +97,16 @@ namespace cbm::algo static constexpr auto Properties = std::make_tuple(config::Property(&RecoParams::sts, "sts", "STS reco settings")); }; - template<> - struct EnumIsSerializable<RecoParams::SortMode> : std::true_type { - }; - - template<> - inline const EnumDict_t<RecoParams::SortMode>& EnumDict<RecoParams::SortMode> = { - {"BlockSort", RecoParams::SortMode::BlockSort}, - {"CUBSegmentedSort", RecoParams::SortMode::CUBSegmentedSort}, - }; - - template<> - struct EnumIsSerializable<RecoParams::UnpackMode> : std::true_type { - }; - - template<> - inline const EnumDict_t<RecoParams::UnpackMode>& EnumDict<RecoParams::UnpackMode> = { - {"CPU", RecoParams::UnpackMode::CPU}, - {"XPU", RecoParams::UnpackMode::XPU}, - }; +}; // namespace cbm::algo +CBM_ENUM_DICT(cbm::algo::RecoParams::SortMode, + {"BlockSort", RecoParams::SortMode::BlockSort}, + {"CUBSegmentedSort", RecoParams::SortMode::CUBSegmentedSort} +); -}; // namespace cbm::algo +CBM_ENUM_DICT(cbm::algo::RecoParams::UnpackMode, + {"CPU", RecoParams::UnpackMode::CPU}, + {"XPU", RecoParams::UnpackMode::XPU} +); #endif // CBM_ALGO_BASE_RECOPARAMS_H diff --git a/algo/base/config/Yaml.h b/algo/base/config/Yaml.h index cbdd7a2f9a..cb836b0465 100644 --- a/algo/base/config/Yaml.h +++ b/algo/base/config/Yaml.h @@ -13,7 +13,7 @@ #include "BaseTypes.h" #include "Prelude.h" #include "Property.h" -#include "util/SerializableEnum.h" +#include "util/EnumDict.h" namespace cbm::algo::config { @@ -29,8 +29,7 @@ namespace cbm::algo::config { using Type = std::remove_cv_t<std::remove_reference_t<T>>; - static_assert(!IsEnum<T> || EnumIsSerializable<Type>::value, "Enum must be serializable"); - + static_assert(!IsEnum<T> || detail::EnumHasDict_v<T>, "Enum must have a dictionary to be deserializable"); // TODO: error handling if constexpr (IsBaseType<Type>) { return node.as<Type>(); } @@ -123,7 +122,7 @@ namespace cbm::algo::config template<typename T> void DoDump(const T& object, YAML::Emitter& ss, std::optional<YAML::EMITTER_MANIP> formatEntries = {}) { - static_assert(!IsEnum<T> || EnumIsSerializable<T>::value, "Enum must be serializable"); + static_assert(!IsEnum<T> || detail::EnumHasDict_v<T>, "Enum must have a dictionary"); if constexpr (IsBaseType<T>) { ss << object; } else if constexpr (IsEnum<T>) { diff --git a/algo/base/util/EnumDict.h b/algo/base/util/EnumDict.h new file mode 100644 index 0000000000..6dc3341bcd --- /dev/null +++ b/algo/base/util/EnumDict.h @@ -0,0 +1,116 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H +#define CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H + +#include <boost/algorithm/string/predicate.hpp> + +#include <algorithm> +#include <iostream> +#include <optional> +#include <stdexcept> +#include <string_view> +#include <vector> + +#include <fmt/format.h> + +namespace cbm::algo +{ + namespace detail + { + template<typename T> + using EnumDict_t = std::vector<std::pair<std::string_view, T>>; + + template<typename T> + inline const EnumDict_t<T> EnumDict; + + template<typename T> + struct EnumHasDict : std::false_type { + }; + + template<typename T> + inline constexpr bool EnumHasDict_v = EnumHasDict<T>::value; + } // namespace detail + + template<typename T, typename = std::enable_if_t<detail::EnumHasDict_v<T>>> + std::optional<T> FromString(std::string_view str, bool caseSensitive = false) + { + const auto& dict = detail::EnumDict<T>; + auto it = std::find_if(dict.begin(), dict.end(), [&](const auto& pair) { + if (caseSensitive) return pair.first == str; + else + return boost::iequals(pair.first, str); + }); + if (it == dict.end()) return std::nullopt; + return it->second; + } + + template<typename T, typename = std::enable_if_t<detail::EnumHasDict_v<T>>> + std::string_view ToString(T t) + { + const auto& dict = detail::EnumDict<T>; + auto it = std::find_if(dict.begin(), dict.end(), [t](const auto& pair) { return pair.second == t; }); + if (it == dict.end()) throw std::runtime_error(fmt::format("Entry {} for enum missing!", static_cast<int>(t))); + return it->first; + } +} // namespace cbm::algo + +/** + * @brief Convert enums to strings and back. + * + * @param type The enum type. + * + * Example: + * @code{.cpp} + * enum class Detector { + * STS, + * TOF, + * }; + * + * CBM_ENUM_DICT(Detector, + * {"sts", Detector::STS}, + * {"tof", Detector::TOF} + * ); + * + * // Use it like this: + * L_(info) << ToString(Detector::STS); // Prints "sts" + * + * std::optional<Detector> d = FromString<Detector>("tof"); // *d == Detector::TOF + * std::optional<Detector> d2 = FromString<Detector>("invalid"); // d2 == std::nullopt + * @endcode + */ +#define CBM_ENUM_DICT(type, ...) \ + template<> \ + inline const cbm::algo::detail::EnumDict_t<type> cbm::algo::detail::EnumDict<type> = {__VA_ARGS__}; \ + template<> \ + struct cbm::algo::detail::EnumHasDict<type> : std::true_type { \ + } + + +// Stream operators for enums +// Placed in global namespace to be found by ADL e.g. for std::ostream_iterator +namespace std +{ + template<typename T, typename = std::enable_if_t<cbm::algo::detail::EnumHasDict_v<T>>> + std::ostream& operator<<(std::ostream& os, T t) + { + os << cbm::algo::ToString(t); + return os; + } + + template<typename T, typename = std::enable_if_t<cbm::algo::detail::EnumHasDict_v<T>>> + std::istream& operator>>(std::istream& is, T& t) + { + std::string str; + is >> str; + auto maybet = cbm::algo::FromString<T>(str); + + if (!maybet) throw std::invalid_argument("Could not parse " + str + " as Enum"); + t = *maybet; + + return is; + } +} // namespace std + +#endif //CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H diff --git a/algo/base/util/SerializableEnum.h b/algo/base/util/SerializableEnum.h deleted file mode 100644 index 84ffd80482..0000000000 --- a/algo/base/util/SerializableEnum.h +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main - SPDX-License-Identifier: GPL-3.0-only - Authors: Felix Weiglhofer [committer] */ -#ifndef CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H -#define CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H - -#include <boost/algorithm/string/predicate.hpp> - -#include <algorithm> -#include <iostream> -#include <optional> -#include <set> -#include <stdexcept> -#include <string_view> - -#include <fmt/format.h> - -namespace cbm::algo -{ - /** - * @brief Helper class to serialize enums to strings and back. - * - * @tparam T The enum type. - * - * To use this class, you need to specialize EnumIsSerializable<T> and EnumStringMap<T>. - * Used to indicate that an enum can be converted to / from a string. - * - * Example: - * @code{.cpp} - * enum class Detector { - * STS, - * TOF, - * }; - * - * // Set to true to indicate that the enum is serializable. - * template<> - * struct EnumIsSerializable<Detector> : std::true_type {}; - * - * // Create dictionary to convert enum to string and back. - * template<> - * const EnumDict_t<Detector> EnumDict<Detector> = { - * {"sts", Detector::STS}, - * {"tof", Detector::TOF}, - * }; - * - * // Use it like this: - * L_(info) << ToString(Detector::STS); // Prints "sts" - * - * std::optional<Detector> d = FromString<Detector>("tof"); // *d == Detector::TOF - * std::optional<Detector> d2 = FromString<Detector>("invalid"); // d2 == std::nullopt - * @endcode - */ - template<typename T> - struct EnumIsSerializable : std::false_type { - }; - - template<typename T> - using EnumDict_t = std::set<std::pair<std::string_view, T>>; - - template<typename T> - inline const EnumDict_t<T> EnumDict; - - template<typename T, typename = std::enable_if_t<EnumIsSerializable<T>::value>> - std::optional<T> FromString(std::string_view str, bool caseSensitive = false) - { - const auto& set = EnumDict<T>; - auto it = std::find_if(set.begin(), set.end(), [&](const auto& pair) { - if (caseSensitive) return pair.first == str; - else - return boost::iequals(pair.first, str); - }); - if (it == set.end()) return std::nullopt; - return it->second; - } - - template<typename T, typename = std::enable_if_t<EnumIsSerializable<T>::value>> - std::string_view ToString(T t) - { - const auto& set = EnumDict<T>; - auto it = std::find_if(set.begin(), set.end(), [t](const auto& pair) { return pair.second == t; }); - if (it == set.end()) throw std::runtime_error(fmt::format("Entry {} for enum missing!", static_cast<int>(t))); - return it->first; - } -} // namespace cbm::algo - -// Stream operators for enums -// Placed in global namespace to be found by ADL e.g. for std::ostream_iterator -namespace std -{ - template<typename T, typename = std::enable_if_t<cbm::algo::EnumIsSerializable<T>::value>> - std::ostream& operator<<(std::ostream& os, T t) - { - os << cbm::algo::ToString(t); - return os; - } - - template<typename T, typename = std::enable_if_t<cbm::algo::EnumIsSerializable<T>::value>> - std::istream& operator>>(std::istream& is, T& t) - { - std::string str; - is >> str; - auto maybet = cbm::algo::FromString<T>(str); - - if (!maybet) throw std::invalid_argument("Could not parse " + str + " as Enum"); - t = *maybet; - - return is; - } -} // namespace std - -#endif //CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H -- GitLab