@@ -34,23 +34,45 @@ template <typename Metric>
3434concept ResettableMetric = requires (Metric& m) { ResetMetric (m); };
3535
3636template <typename Field>
37- concept StringViewCompatibleField =
38- std::constructible_from<Field, std::string_view> && std::constructible_from<std::string_view, const Field&>;
37+ std::string_view FieldToStringView (const Field& field) {
38+ if constexpr (std::constructible_from<std::string_view, const Field&>) {
39+ return std::string_view{field};
40+ } else {
41+ // ADL lookup.
42+ return ToStringView (field);
43+ }
44+ }
45+
46+ template <typename Field>
47+ concept FieldConvertibleToStringView =
48+ std::constructible_from<std::string_view, const Field&> || requires (const Field& f) {
49+ {
50+ ToStringView (f)
51+ } -> std::same_as<std::string_view>;
52+ };
53+
54+ template <typename Field>
55+ concept FieldConstructibleFromStringView = std::constructible_from<Field, std::string_view>;
56+
57+ template <typename Labels>
58+ concept AllFieldsConvertibleToStringView = []<std::size_t ... Is>(std::index_sequence<Is...>) {
59+ return (FieldConvertibleToStringView<boost::pfr::tuple_element_t <Is, Labels>> && ...);
60+ }(std::make_index_sequence<boost::pfr::tuple_size_v<Labels>>{});
3961
4062template <typename Labels>
41- concept AllFieldsAreStringViewCompatible = []<std::size_t ... Is>(std::index_sequence<Is...>) {
42- return (StringViewCompatibleField <boost::pfr::tuple_element_t <Is, Labels>> && ...);
63+ concept AllFieldsConstructibleFromStringView = []<std::size_t ... Is>(std::index_sequence<Is...>) {
64+ return (FieldConstructibleFromStringView <boost::pfr::tuple_element_t <Is, Labels>> && ...);
4365}(std::make_index_sequence<boost::pfr::tuple_size_v<Labels>>{});
4466
4567template <typename Labels>
46- concept LabelsAggregate = std::is_aggregate_v<Labels> && AllFieldsAreStringViewCompatible <Labels>;
68+ concept LabelsAggregate = std::is_aggregate_v<Labels> && AllFieldsConvertibleToStringView <Labels>;
4769
4870template <typename Labels>
4971auto LabelsStructToViewArray (const Labels& labels) {
5072 constexpr std::size_t kN = boost::pfr::tuple_size_v<Labels>;
5173 std::array<std::string_view, kN > result{};
5274 boost::pfr::for_each_field (labels, [&result](const auto & field, std::size_t i) {
53- result[i] = std::string_view{ field} ;
75+ result[i] = FieldToStringView ( field) ;
5476 });
5577 return result;
5678}
@@ -157,10 +179,13 @@ struct ByLabelEntryEqual {
157179// /
158180// / See @ref scripts/docs/en/userver/metrics.md .
159181// /
160- // / `Labels` must be an aggregate type where all fields are interconvertible
161- // / with `std::string_view`, i.e. constructible from `std::string_view` and
162- // / convertible to `std::string_view`. This includes `std::string_view` itself,
163- // / `utils::Required<std::string_view>`, `std::string`, @ref utils::StrongTypedef, etc.
182+ // / `Labels` must be an aggregate type where all fields are convertible to `std::string_view`, by at least one of:
183+ // /
184+ // / * a (possibly `explicit`) conversion operator to `std::string_view`;
185+ // / * an ADL-found `std::string_view ToStringView(const Field&)` function.
186+ // /
187+ // / This includes `std::string_view` itself, `utils::Required<std::string_view>`,
188+ // / `std::string`, @ref utils::StrongTypedef, code-generated enums, etc.
164189// /
165190// / Label names are taken from `Labels` field names.
166191// /
@@ -203,7 +228,19 @@ struct ByLabelEntryEqual {
203228// / `MonotonicByLabelStorage` is also composable the other way around, it can be included in larger metric structures
204229// / as a field.
205230// /
206- // / @tparam Labels An aggregate type with `std::string_view` fields.
231+ // / ## Usage of MonotonicByLabelStorage with enum labels
232+ // /
233+ // / An enum can be used as a label as long as it has `ToStringView` defined. Usage example:
234+ // /
235+ // / @snippet core/src/utils/statistics/by_label_storage_test.cpp enum label ToStringView
236+ // /
237+ // / @snippet core/src/utils/statistics/by_label_storage_test.cpp enum label labels
238+ // /
239+ // / @snippet core/src/utils/statistics/by_label_storage_test.cpp enum label metric tag
240+ // /
241+ // / @snippet core/src/utils/statistics/by_label_storage_test.cpp enum label usage
242+ // /
243+ // / @tparam Labels An aggregate type whose fields are convertible to `std::string_view`.
207244// / @tparam Metric The metric type. Must support `DumpMetric(Writer&, const Metric&)`.
208245template <typename Labels, typename Metric>
209246requires impl::LabelsAggregate<Labels> && impl::DumpableMetric<Metric>
@@ -249,8 +286,13 @@ class MonotonicByLabelStorage final {
249286 }
250287
251288 // / @brief Visit all stored metrics.
289+ // /
290+ // / Requires that all fields of `Labels` are constructible from `std::string_view`.
291+ // /
252292 // / @param func Callable accepting `(const Labels&, const Metric&)`.
253- void VisitAll (std::invocable<const Labels&, const Metric&> auto func) const {
293+ void VisitAll (std::invocable<const Labels&, const Metric&> auto func) const
294+ requires impl::AllFieldsConstructibleFromStringView<Labels>
295+ {
254296 set_.Visit ([&func](const Entry& entry) { func (impl::LabelsArrayToStruct<Labels>(entry.labels ), entry.metric ); }
255297 );
256298 }
0 commit comments