From 8bb87900630cbdda04a1d2ab74842cae681480e4 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 29 Jun 2026 15:41:59 +0200 Subject: [PATCH 1/7] initial --- Framework/Core/include/Framework/ASoA.h | 100 ++++++-------------- Framework/Core/include/Framework/Concepts.h | 98 +++++++++++++++++++ 2 files changed, 128 insertions(+), 70 deletions(-) create mode 100644 Framework/Core/include/Framework/Concepts.h diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index a21532e42e692..9cde267252011 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -15,7 +15,7 @@ #if defined(__CLING__) #error "Please do not include this file in ROOT dictionary generation" #endif - +#include "Framework/Concepts.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/Pack.h" // IWYU pragma: export #include "Framework/FunctionalHelpers.h" // IWYU pragma: export @@ -189,30 +189,13 @@ consteval auto intersectOriginals() namespace o2::soa { -struct Binding; - -template -concept not_void = requires { !std::same_as; }; - -/// column identification concepts -template -concept is_persistent_column = requires(C c) { c.mColumnIterator; }; - +/// column identification template constexpr bool is_persistent_v = is_persistent_column; template using is_persistent_column_t = std::conditional_t, std::true_type, std::false_type>; -template -concept is_self_index_column = not_void && std::same_as; - -template -concept is_index_column = !is_self_index_column && requires(C c, o2::soa::Binding b) { - { c.setCurrentRaw(b) } -> std::same_as; - requires std::same_as; -}; - template using is_external_index_t = typename std::conditional_t, std::true_type, std::false_type>; @@ -239,6 +222,8 @@ static consteval int getIndexPosToKey_impl() /// Base type for table metadata template struct TableMetadata { + static constexpr void isTableMetadata() + {}; using columns = framework::pack; using persistent_columns_t = framework::selected_pack; using external_index_columns_t = framework::selected_pack; @@ -270,6 +255,8 @@ struct TableMetadata { template struct MetadataTrait { + static constexpr void isMetadataTrait() + {}; using metadata = void; }; @@ -277,6 +264,8 @@ struct MetadataTrait { /// type signature template struct Hash { + static constexpr void isHash() + {}; static constexpr uint32_t hash = H; static constexpr char const* const str{""}; }; @@ -298,6 +287,8 @@ consteval auto filterForKey() #define O2HASH(_Str_) \ template <> \ struct Hash<_Str_ ""_h> { \ + static constexpr void isHash() \ + {}; \ static constexpr uint32_t hash = _Str_ ""_h; \ static constexpr char const* const str{_Str_}; \ }; @@ -306,6 +297,10 @@ consteval auto filterForKey() #define O2ORIGIN(_Str_) \ template <> \ struct Hash<_Str_ ""_h> { \ + static constexpr void isHash() \ + {}; \ + static constexpr void isOriginHash() \ + {}; \ static constexpr header::DataOrigin origin{_Str_}; \ static constexpr uint32_t hash = _Str_ ""_h; \ static constexpr char const* const str{_Str_}; \ @@ -386,13 +381,6 @@ constexpr framework::ConcreteDataMatcher matcher() return {origin(), description(signature()), R.version}; } -/// hash identification concepts -template -concept is_aod_hash = requires(T t) { t.hash; t.str; }; - -template -concept is_origin_hash = is_aod_hash && requires(T t) { t.origin; }; - /// convert TableRef to a DPL source specification template static constexpr auto sourceSpec() @@ -446,27 +434,6 @@ struct Binding { using SelectionVector = std::vector; -template -concept has_parent_t = not_void; - -template -concept is_metadata = framework::base_of_template; - -template -concept is_metadata_trait = framework::specialization_of_template; - -template -concept has_metadata = is_metadata_trait && not_void; - -template -concept has_extension = is_metadata && not_void; - -template -concept has_configurable_extension = has_extension && requires(T t) { typename T::configurable_t; requires std::same_as; }; - -template -concept is_spawnable_column = std::same_as; - template struct EquivalentIndex { constexpr static bool value = false; @@ -696,6 +663,9 @@ class ColumnIterator : ChunkingPolicy template struct Column { + static constexpr void isIteratableColumn() + {}; + using inherited_t = INHERIT; Column(ColumnIterator const& it) : mColumnIterator{it} @@ -730,6 +700,8 @@ struct Column { /// method call. template struct DynamicColumn { + static constexpr void isDynamicColumn() + {}; using inherited_t = INHERIT; static constexpr const char* const& columnLabel() { return INHERIT::mLabel; } @@ -737,6 +709,8 @@ struct DynamicColumn { template struct IndexColumn { + static constexpr void isEnumeratingColumn() + {}; using inherited_t = INHERIT; static constexpr const uint32_t hash = 0; @@ -745,6 +719,8 @@ struct IndexColumn { template struct MarkerColumn { + static constexpr void isMarkingColumn() + {}; using inherited_t = INHERIT; static constexpr const uint32_t hash = 0; @@ -846,29 +822,6 @@ struct Index : o2::soa::IndexColumn> { std::tuple rowOffsets; }; -template -concept is_indexing_column = requires(C& c) { - c.rowIndices; - c.rowOffsets; -}; - -template -concept is_dynamic_column = requires(C& c) { - c.boundIterators; -}; - -template -concept is_marker_column = requires { &C::mark; }; - -template -using is_dynamic_t = std::conditional_t, std::true_type, std::false_type>; - -template -concept is_column = is_persistent_column || is_dynamic_column || is_indexing_column || is_marker_column; - -template -using is_indexing_t = std::conditional_t, std::true_type, std::false_type>; - struct IndexPolicyBase { /// Position inside the current table int64_t mRowIndex = 0; @@ -877,10 +830,13 @@ struct IndexPolicyBase { }; struct RowViewSentinel { + static constexpr void isRowViewSentinel() + {}; int64_t const index; }; struct FilteredIndexPolicy : IndexPolicyBase { + static constexpr void isFilteredIndexPolicy(); // We use -1 in the IndexPolicyBase to indicate that the index is // invalid. What will validate the index is the this->setCursor() // which happens below which will properly setup the first index @@ -986,6 +942,8 @@ struct FilteredIndexPolicy : IndexPolicyBase { }; struct DefaultIndexPolicy : IndexPolicyBase { + static constexpr void isDefaultIndexPolicy() + {}; /// Needed to be able to copy the policy DefaultIndexPolicy() = default; DefaultIndexPolicy(DefaultIndexPolicy&&) = default; @@ -1088,6 +1046,8 @@ concept has_index = (is_indexing_column || ...); template struct TableIterator : IP, C... { public: + static constexpr void isTableIterator() + {}; using self_t = TableIterator; using policy_t = IP; using all_columns = framework::pack; diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h new file mode 100644 index 0000000000000..715081b33b410 --- /dev/null +++ b/Framework/Core/include/Framework/Concepts.h @@ -0,0 +1,98 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FRAMEWORK_CONCEPTS_H +#define O2_FRAMEWORK_CONCEPTS_H + +#include + +namespace o2::aod +{ +/// hash +template +concept is_aod_hash = requires(T t) { &T::isHash; }; + +template +concept is_origin_hash = requires(T t) { &T::isOriginHash; }; +} // namespace o2::aod + +namespace o2::soa +{ +/// general +template +concept not_void = requires { requires !std::same_as; }; + +/// columns +template +concept is_persistent_column = requires(C c) { &C::isIteratableColumn; }; + +template +concept is_self_index_column = not_void && std::same_as; + +/// FIXME: this should really rely on the struct's content instead +struct Binding; +template +concept is_index_column = !is_self_index_column && requires(C c, o2::soa::Binding b) { + { c.setCurrentRaw(b) } -> std::same_as; + requires std::same_as; +}; + +template +concept is_spawnable_column = std::same_as; + +template +concept is_indexing_column = requires(C c) { &C::isEnumeratingColumn; }; + +template +concept is_dynamic_column = requires(C c) { &C::isDynamicColumn; }; + +template +concept is_marker_column = requires { &C::isMarkingColumn; }; + +template +concept is_column = is_persistent_column || is_dynamic_column || is_indexing_column || is_marker_column; + +/// pack filtering helpers +template +using is_dynamic_t = std::conditional_t, std::true_type, std::false_type>; + +template +using is_indexing_t = std::conditional_t, std::true_type, std::false_type>; + +/// tables, iterators and metadata +template +concept has_parent_t = not_void; + +/// FIXME: this should really rely on the struct's content instead +template +concept is_metadata_trait = requires(T t) { &T::isMetadataTrait; }; + +/// FIXME: this should really rely on the struct's content instead +template +concept is_metadata = requires(T t) { &T::isTableMetadata; }; + +template +concept has_metadata = is_metadata_trait && not_void; + +template +concept has_extension = is_metadata && not_void; + +template +concept has_configurable_extension = has_extension && requires(T t) { typename T::configurable_t; requires std::same_as; }; + +} // namespace o2::soa + +namespace o2::framework +{ + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_CONCEPTS_H From 2cfa9f2bfa48d6cdc2bb3319c57f7deb3d976b98 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 30 Jun 2026 13:53:51 +0200 Subject: [PATCH 2/7] move concepts out of ASoA.h --- Framework/Core/include/Framework/ASoA.h | 118 +++-------------- Framework/Core/include/Framework/Concepts.h | 139 +++++++++++++++++++- 2 files changed, 153 insertions(+), 104 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 9cde267252011..871d4cebd21be 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1018,15 +1018,6 @@ struct DefaultIndexPolicy : IndexPolicyBase { int64_t mMaxRow = 0; }; -// template -// class Table; - -template -class Table; - -template -concept is_table = framework::specialization_of_template || framework::base_of_template; - /// Similar to a pair but not a pair, to avoid /// exposing the second type everywhere. template @@ -1035,14 +1026,6 @@ struct ColumnDataHolder { arrow::ChunkedArray* second; }; -template -concept can_bind = requires(T&& t) { - { t.B::mColumnIterator }; -}; - -template -concept has_index = (is_indexing_column || ...); - template struct TableIterator : IP, C... { public: @@ -1272,48 +1255,6 @@ struct ArrowHelpers { static std::shared_ptr concatTables(std::vector>&& tables); }; -//! Helper to check if a type T is an iterator -template -concept is_iterator = framework::base_of_template || framework::specialization_of_template; - -template -concept is_table_or_iterator = is_table || is_iterator; - -template -concept with_originals = requires { - T::originals.size(); -}; - -template -concept with_sources = requires { - T::sources.size(); -}; - -template -concept with_sources_generator = requires(T t) { - t.template generateSources>(); -}; - -template -concept with_ccdb_urls = requires { - T::ccdb_urls.size(); -}; - -template -concept with_base_table = requires { - typename aod::MetadataTrait>::metadata::base_table_t; -}; - -template -concept with_expression_pack = requires { - typename T::expression_pack_t{}; -}; - -template -concept with_index_pack = requires { - typename T::index_pack_t{}; -}; - template os1, size_t N2, std::array os2> consteval bool is_compatible() { @@ -1336,12 +1277,6 @@ consteval bool is_binding_compatible_v() template using is_binding_compatible = std::conditional_t(), std::true_type, std::false_type>; -template -struct IndexTable; - -template -concept is_index_table = framework::specialization_of_template; - template static constexpr std::string getLabelForTable() { @@ -1469,6 +1404,8 @@ namespace o2::framework { /// tracks origin in bindingKey matcher to handle the correct arguments struct PreslicePolicyBase { + static constexpr void isPreslicePolicy() + {}; const std::string binding; Entry bindingKey; @@ -1495,11 +1432,10 @@ struct PreslicePolicyGeneral : public PreslicePolicyBase { std::span getSliceFor(int value) const; }; -template -concept is_preslice_policy = std::derived_from; - template struct PresliceBase : public Policy { + static constexpr void isPresliceContainer() + {}; constexpr static bool optional = OPT; using target_t = T; using policy_t = Policy; @@ -1540,13 +1476,6 @@ using Preslice = PresliceBase; template using PresliceOptional = PresliceBase; -template -concept is_preslice = std::derived_from&& - requires(T) -{ - T::optional; -}; - /// Can be user to group together a number of Preslice declaration /// to avoid the limit of 100 data members per task /// @@ -1560,11 +1489,9 @@ concept is_preslice = std::derived_from&& /// /// preslices.perCol; struct PresliceGroup { + static constexpr void isPresliceGroup() + {}; }; - -template -concept is_preslice_group = std::derived_from; - } // namespace o2::framework namespace o2::soa @@ -1574,25 +1501,10 @@ class FilteredBase; template class Filtered; -template -concept has_filtered_policy = not_void && std::same_as; - -template -concept is_filtered_iterator = is_iterator && has_filtered_policy; - -template -concept is_filtered_table = framework::base_of_template; - // FIXME: compatbility declaration to be removed template constexpr bool is_soa_filtered_v = is_filtered_table; -template -concept is_filtered = is_filtered_table || is_filtered_iterator; - -template -concept is_not_filtered_table = is_table && !is_filtered_table; - /// Helper function to extract bound indices template static consteval auto extractBindings(framework::pack) @@ -1804,6 +1716,8 @@ template ; using table_t = self_t; @@ -2297,7 +2211,7 @@ concept dynamic_with_common_getter = is_dynamic_column && }; template -concept persistent_with_common_getter = is_persistent_v && requires(T t) { +concept persistent_with_common_getter = is_persistent_column && requires(T t) { { t.get() } -> std::convertible_to; }; @@ -3409,6 +3323,8 @@ namespace o2::soa { template struct Join : Table, o2::aod::Hash<"JOIN/0"_h>, o2::aod::Hash<"JOIN"_h>, Ts...> { + static constexpr void isJoin() + {}; using base = Table, o2::aod::Hash<"JOIN/0"_h>, o2::aod::Hash<"JOIN"_h>, Ts...>; Join(std::shared_ptr&& table, uint64_t offset = 0) @@ -3517,9 +3433,6 @@ constexpr auto join(Ts const&... t) return Join(ArrowHelpers::joinTables({t.asArrowTable()...}, std::span{Join::base::originalLabels})); } -template -concept is_join = framework::specialization_of_template; - template constexpr bool is_soa_join_v = is_join; @@ -3565,6 +3478,8 @@ template class FilteredBase : public T { public: + static constexpr void isFilteredBase() + {}; using self_t = FilteredBase; using table_t = typename T::table_t; using T::originals; @@ -4221,6 +4136,8 @@ struct IndexTable : Table { template struct SmallGroupsBase : public Filtered { + static constexpr void isSmallGroups() + {}; static constexpr bool applyFilters = APPLY; SmallGroupsBase(std::vector>&& tables, gandiva::Selection const& selection, uint64_t offset = 0) : Filtered(std::move(tables), selection, offset) {} @@ -4237,11 +4154,6 @@ using SmallGroups = SmallGroupsBase; template using SmallGroupsUnfiltered = SmallGroupsBase; - -template -concept is_smallgroups = requires { - [](SmallGroupsBase*) {}(std::declval*>()); -}; } // namespace o2::soa #endif // O2_FRAMEWORK_ASOA_H_ diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h index 715081b33b410..b512a087b7a01 100644 --- a/Framework/Core/include/Framework/Concepts.h +++ b/Framework/Core/include/Framework/Concepts.h @@ -12,31 +12,42 @@ #ifndef O2_FRAMEWORK_CONCEPTS_H #define O2_FRAMEWORK_CONCEPTS_H +#include #include namespace o2::aod { +template +struct Hash; /// hash +/// 1. require aod::Hash template concept is_aod_hash = requires(T t) { &T::isHash; }; - +/// 2. requires aod::Hash with header::DataOrigin template concept is_origin_hash = requires(T t) { &T::isOriginHash; }; + +template +struct MetadataTrait; } // namespace o2::aod namespace o2::soa { /// general +/// require a type to be not void template concept not_void = requires { requires !std::same_as; }; /// columns +/// 1. require a storage-backed column template concept is_persistent_column = requires(C c) { &C::isIteratableColumn; }; +/// 2. require self-index column template concept is_self_index_column = not_void && std::same_as; +/// 3. require bidable index column /// FIXME: this should really rely on the struct's content instead struct Binding; template @@ -45,21 +56,36 @@ concept is_index_column = !is_self_index_column && requires(C c, o2::soa::Bin requires std::same_as; }; +/// 4. require a column that can be created from an expression template concept is_spawnable_column = std::same_as; +/// 5. require an enumerating column, like soa::Index template concept is_indexing_column = requires(C c) { &C::isEnumeratingColumn; }; +/// 6. require a dynamic column template concept is_dynamic_column = requires(C c) { &C::isDynamicColumn; }; +/// 7. require a marking column template concept is_marker_column = requires { &C::isMarkingColumn; }; +/// 8. require any supported column template concept is_column = is_persistent_column || is_dynamic_column || is_indexing_column || is_marker_column; +/// 9. require a type that can be bound as a column of type B +template +concept can_bind = requires(T&& t) { + { t.B::mColumnIterator }; +}; + +/// 10. require at least one column in an exploded pack to be an indexing column +template +concept has_index = (is_indexing_column || ...); + /// pack filtering helpers template using is_dynamic_t = std::conditional_t, std::true_type, std::false_type>; @@ -68,31 +94,142 @@ template using is_indexing_t = std::conditional_t, std::true_type, std::false_type>; /// tables, iterators and metadata +/// 1. require a type with parent_t dependent type template concept has_parent_t = not_void; +/// 2. require a MetadataTrait specialization/descendant /// FIXME: this should really rely on the struct's content instead template concept is_metadata_trait = requires(T t) { &T::isMetadataTrait; }; +/// 3. require a TableMetadata depcialization/descendant /// FIXME: this should really rely on the struct's content instead template concept is_metadata = requires(T t) { &T::isTableMetadata; }; +/// 4. require a type with non-void metadata dependent type template concept has_metadata = is_metadata_trait && not_void; +/// 5. require a type with non-void extension_table_t dependent type template concept has_extension = is_metadata && not_void; +/// 6. require a type with non-void configurable_t dependent type, that is same as true_type template concept has_configurable_extension = has_extension && requires(T t) { typename T::configurable_t; requires std::same_as; }; +/// 7. require an soa::Table +template +concept is_table = requires(T t) { &T::isSOATable(); }; + +/// 8. require a specialization/descendant of a TableIterator +template +concept is_iterator = requires (T t) { &T::isTableIterator(); }; + +/// 9. require a table or iterator +template +concept is_table_or_iterator = is_table || is_iterator; + +/// 10. require soa::IndexTable +template +struct IndexTable; + +template +concept is_index_table = framework::specialization_of_template; + +/// 11. require a type with a filtered policy +template +concept has_filtered_policy = not_void && requires{T::policy_t::isFilteredIndexPolicy();}; + +/// 12. require a filtered table iterator +template +concept is_filtered_iterator = is_iterator && has_filtered_policy; + +/// 13. require a filtered table +template +concept is_filtered_table = requires(T t) { &T::isFilteredBase; }; + +/// 14. require a filtered table or iterator +template +concept is_filtered = is_filtered_table || is_filtered_iterator; + +/// 15. require not filtered table +template +concept is_not_filtered_table = is_table && !is_filtered_table; + +/// 16. require a join +template +concept is_join = requires(T t) { &T::isJoin; }; + +/// misc +/// 1. require a type with originals container +template +concept with_originals = requires { + T::originals.size(); +}; + +/// 2. require a type with sources container +template +concept with_sources = requires { + T::sources.size(); +}; + +/// 3. require a type with sources generator method +/// FIXME: this should really rely on the struct's content instead +template +concept with_sources_generator = requires(T t) { + t.template generateSources>(); +}; + +/// 4. require a type with ccd_urls container +template +concept with_ccdb_urls = requires { + T::ccdb_urls.size(); +}; + +/// 5. require a type, whos metadata has base_table_t dependant type +/// FIXME: this should really rely on the struct's content instead +template +concept with_base_table = with_originals && has_metadata>> && requires { + typename aod::MetadataTrait>::metadata::base_table_t; +}; + +template +concept with_base_table_ng = not_void; // redicrection should be done at the check site + +/// 6. require a type with expression_pack_t dependant type +template +concept with_expression_pack = requires { + typename T::expression_pack_t{}; +}; + +/// 7. require a type with index_pack_t dependant type +template +concept with_index_pack = requires { + typename T::index_pack_t{}; +}; + +/// 8. require SmallGroups +template +concept is_smallgroups = requires(T t) { &T::isSmallGroups; }; } // namespace o2::soa namespace o2::framework { +/// preslice +/// 1. require a preslice policy +template +concept is_preslice_policy = requires(T t) { &T::isPreslicePolicy; }; +/// 2. require a preslice container +template +concept is_preslice = requires(T t) { &T::isPresliceContainer; }; + +/// 3. reqiures a preslice group +template +concept is_preslice_group = requires(T t) { &T::isPresliceGroup; }; } // namespace o2::framework #endif // O2_FRAMEWORK_CONCEPTS_H From 1223002519f74d9f0bac0e8eb870215b6b3d4915 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Sat, 4 Jul 2026 18:08:26 +0200 Subject: [PATCH 3/7] fixup! move concepts out of ASoA.h --- Framework/Core/include/Framework/ASoA.h | 6 +++ .../Core/include/Framework/AnalysisHelpers.h | 2 +- Framework/Core/include/Framework/Concepts.h | 54 +++++++++---------- Framework/Core/test/test_Concepts.cxx | 4 ++ 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 871d4cebd21be..5ec52a0ddc246 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -3115,6 +3115,7 @@ consteval auto getIndexTargets() #define DECLARE_SOA_TABLE_METADATA_TRAIT(_Name_, _Desc_, _Version_) \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##Metadata; \ }; @@ -3125,6 +3126,7 @@ consteval auto getIndexTargets() using _Name_ = _Name_##From>; \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##Metadata; \ }; @@ -3184,6 +3186,7 @@ consteval auto getIndexTargets() }; \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##ExtensionMetadata; \ }; \ template \ @@ -3218,6 +3221,7 @@ consteval auto getIndexTargets() }; \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##CfgExtensionMetadata; \ }; \ template \ @@ -3253,6 +3257,7 @@ consteval auto getIndexTargets() }; \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##Metadata; \ }; \ template \ @@ -3307,6 +3312,7 @@ consteval auto getIndexTargets() }; \ template <> \ struct MetadataTrait> { \ + static constexpr void isMetadataTrait() {}; \ using metadata = _Name_##TimestampMetadata; \ }; \ template \ diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index c7d567c9eb810..41bddd7500aad 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -493,7 +493,7 @@ class TableConsumer; /// a table. The provided template arguments are if type Column and /// therefore refer only to the persisted columns. template -concept is_producable = soa::has_metadata> || soa::has_metadata>; +concept is_producable = soa::has_metadata>> || soa::has_metadata>>; template concept is_enumerated_iterator = requires(T t) { t.globalIndex(); }; diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h index b512a087b7a01..09cc3ba2687ca 100644 --- a/Framework/Core/include/Framework/Concepts.h +++ b/Framework/Core/include/Framework/Concepts.h @@ -22,12 +22,12 @@ struct Hash; /// hash /// 1. require aod::Hash template -concept is_aod_hash = requires(T t) { &T::isHash; }; +concept is_aod_hash = requires(T t) { t.isHash(); }; /// 2. requires aod::Hash with header::DataOrigin template -concept is_origin_hash = requires(T t) { &T::isOriginHash; }; +concept is_origin_hash = requires(T t) { t.isOriginHash(); }; -template +template struct MetadataTrait; } // namespace o2::aod @@ -41,11 +41,11 @@ concept not_void = requires { requires !std::same_as; }; /// columns /// 1. require a storage-backed column template -concept is_persistent_column = requires(C c) { &C::isIteratableColumn; }; +concept is_persistent_column = requires(C c) { c.isIteratableColumn(); }; /// 2. require self-index column template -concept is_self_index_column = not_void && std::same_as; +concept is_self_index_column = not_void::self_index_t> && std::same_as::self_index_t, std::true_type>; /// 3. require bidable index column /// FIXME: this should really rely on the struct's content instead @@ -58,19 +58,19 @@ concept is_index_column = !is_self_index_column && requires(C c, o2::soa::Bin /// 4. require a column that can be created from an expression template -concept is_spawnable_column = std::same_as; +concept is_spawnable_column = std::same_as::spawnable_t, std::true_type>; /// 5. require an enumerating column, like soa::Index template -concept is_indexing_column = requires(C c) { &C::isEnumeratingColumn; }; +concept is_indexing_column = requires(C c) { c.isEnumeratingColumn(); }; /// 6. require a dynamic column template -concept is_dynamic_column = requires(C c) { &C::isDynamicColumn; }; +concept is_dynamic_column = requires(C c) { c.isDynamicColumn(); }; /// 7. require a marking column template -concept is_marker_column = requires { &C::isMarkingColumn; }; +concept is_marker_column = requires(C c) { c.isMarkingColumn(); }; /// 8. require any supported column template @@ -101,38 +101,39 @@ concept has_parent_t = not_void; /// 2. require a MetadataTrait specialization/descendant /// FIXME: this should really rely on the struct's content instead template -concept is_metadata_trait = requires(T t) { &T::isMetadataTrait; }; +concept is_metadata_trait = requires(T t) { t.isMetadataTrait(); }; /// 3. require a TableMetadata depcialization/descendant /// FIXME: this should really rely on the struct's content instead template -concept is_metadata = requires(T t) { &T::isTableMetadata; }; +concept is_metadata = requires(T t) { t.isTableMetadata(); }; /// 4. require a type with non-void metadata dependent type template -concept has_metadata = is_metadata_trait && not_void; +concept has_metadata = is_metadata::metadata>; /// 5. require a type with non-void extension_table_t dependent type template -concept has_extension = is_metadata && not_void; +concept has_extension = is_metadata && not_void::extension_table_t>; /// 6. require a type with non-void configurable_t dependent type, that is same as true_type template -concept has_configurable_extension = has_extension && requires(T t) { typename T::configurable_t; requires std::same_as; }; +concept has_configurable_extension = has_extension && requires(T t) { typename std::decay_t::configurable_t; requires std::same_as::configurable_t>; }; /// 7. require an soa::Table template -concept is_table = requires(T t) { &T::isSOATable(); }; +concept is_table = requires(T t) { t.isSOATable(); }; /// 8. require a specialization/descendant of a TableIterator template -concept is_iterator = requires (T t) { &T::isTableIterator(); }; +concept is_iterator = requires(T t) { t.isTableIterator(); }; /// 9. require a table or iterator template concept is_table_or_iterator = is_table || is_iterator; /// 10. require soa::IndexTable +/// FIXME: this should really rely on the struct's content instead template struct IndexTable; @@ -141,7 +142,7 @@ concept is_index_table = framework::specialization_of_template -concept has_filtered_policy = not_void && requires{T::policy_t::isFilteredIndexPolicy();}; +concept has_filtered_policy = not_void::policy_t> && requires { std::decay_t::policy_t::isFilteredIndexPolicy(); }; /// 12. require a filtered table iterator template @@ -149,7 +150,7 @@ concept is_filtered_iterator = is_iterator && has_filtered_policy; /// 13. require a filtered table template -concept is_filtered_table = requires(T t) { &T::isFilteredBase; }; +concept is_filtered_table = requires(T t) { t.isFilteredBase(); }; /// 14. require a filtered table or iterator template @@ -161,7 +162,7 @@ concept is_not_filtered_table = is_table && !is_filtered_table; /// 16. require a join template -concept is_join = requires(T t) { &T::isJoin; }; +concept is_join = requires(T t) { t.isJoin(); }; /// misc /// 1. require a type with originals container @@ -177,16 +178,15 @@ concept with_sources = requires { }; /// 3. require a type with sources generator method -/// FIXME: this should really rely on the struct's content instead template concept with_sources_generator = requires(T t) { - t.template generateSources>(); + t.generateSources(); }; /// 4. require a type with ccd_urls container template -concept with_ccdb_urls = requires { - T::ccdb_urls.size(); +concept with_ccdb_urls = requires(T t) { + t.ccdb_urls.size(); }; /// 5. require a type, whos metadata has base_table_t dependant type @@ -213,7 +213,7 @@ concept with_index_pack = requires { /// 8. require SmallGroups template -concept is_smallgroups = requires(T t) { &T::isSmallGroups; }; +concept is_smallgroups = requires(T t) { t.isSmallGroups(); }; } // namespace o2::soa namespace o2::framework @@ -221,15 +221,15 @@ namespace o2::framework /// preslice /// 1. require a preslice policy template -concept is_preslice_policy = requires(T t) { &T::isPreslicePolicy; }; +concept is_preslice_policy = requires(T t) { t.isPreslicePolicy(); }; /// 2. require a preslice container template -concept is_preslice = requires(T t) { &T::isPresliceContainer; }; +concept is_preslice = requires(T t) { t.isPresliceContainer(); }; /// 3. reqiures a preslice group template -concept is_preslice_group = requires(T t) { &T::isPresliceGroup; }; +concept is_preslice_group = requires(T t) { t.isPresliceGroup(); }; } // namespace o2::framework #endif // O2_FRAMEWORK_CONCEPTS_H diff --git a/Framework/Core/test/test_Concepts.cxx b/Framework/Core/test/test_Concepts.cxx index 375e537cfaec0..ff5e0fa6200db 100644 --- a/Framework/Core/test/test_Concepts.cxx +++ b/Framework/Core/test/test_Concepts.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Framework/Concepts.h" #include #include "Framework/ASoA.h" #include "Framework/AnalysisDataModel.h" @@ -87,6 +88,9 @@ TEST_CASE("IdentificationConcepts") REQUIRE(with_originals); + REQUIRE(o2::soa::is_metadata_trait>>); + REQUIRE(o2::soa::has_metadata>>); + REQUIRE(o2::soa::is_metadata>::metadata>); REQUIRE(with_sources_generator>::metadata>); REQUIRE(with_base_table); From 62d5f408ad365b61a21cb67d74a728071638725e Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 6 Jul 2026 09:47:49 +0200 Subject: [PATCH 4/7] move concepts out of AnalysisHelpers.h --- .../Core/include/Framework/AnalysisHelpers.h | 72 ++--------------- Framework/Core/include/Framework/Concepts.h | 81 ++++++++++++++++++- 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 41bddd7500aad..77e9ed760b1e0 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -492,12 +492,6 @@ class TableConsumer; /// Helper class actually implementing the cursor which can write to /// a table. The provided template arguments are if type Column and /// therefore refer only to the persisted columns. -template -concept is_producable = soa::has_metadata>> || soa::has_metadata>>; - -template -concept is_enumerated_iterator = requires(T t) { t.globalIndex(); }; - template struct WritingCursor { public: @@ -579,13 +573,13 @@ struct WritingCursor { decltype(FFL(std::declval())) cursor; private: - static decltype(auto) extract(is_enumerated_iterator auto const& arg) + static decltype(auto) extract(soa::is_enumerated_iterator auto const& arg) { return arg.globalIndex(); } template - requires(!is_enumerated_iterator) + requires(!soa::is_enumerated_iterator) static decltype(auto) extract(A&& arg) { return arg; @@ -642,9 +636,6 @@ template struct Produces : WritingCursor { }; -template -concept is_produces = requires(T t) { typename T::cursor_t; typename T::persistent_table_t; &T::cursor; }; - /// Use this to group together produces. Useful to separate them logically /// or simply to stay within the 100 elements per Task limit. /// Use as: @@ -654,11 +645,10 @@ concept is_produces = requires(T t) { typename T::cursor_t; typename T::persiste /// /// Notice the label MySetOfProduces is just a mnemonic and can be omitted. struct ProducesGroup { + static constexpr void isProducesGroup() + {}; }; -template -concept is_produces_group = std::derived_from; - /// Helper template for table transformations template struct TableTransform { @@ -682,12 +672,6 @@ struct TableTransform { /// This helper struct allows you to declare extended tables which should be /// created by the task (as opposed to those pre-defined by data model) -template -concept is_spawnable = soa::has_metadata>> && soa::has_extension>::metadata>; - -template -concept is_dynamically_spawnable = soa::has_metadata>> && soa::has_configurable_extension>::metadata>; - template consteval auto transformBase() { @@ -736,13 +720,6 @@ struct Spawns : decltype(transformBase()) { }(); }; -template -concept is_spawns = requires(T t) { - typename T::metadata; - typename T::expression_pack_t; - requires std::same_as>; -}; - /// This helper struct allows you to declare extended tables with dynamically-supplied /// expressions to be created by the task /// The actual expressions have to be set in init() for the configurable expression @@ -792,15 +769,6 @@ struct Defines : decltype(transformBase()) { template using DefinesDelayed = Defines; -template -concept is_defines = requires(T t) { - typename T::metadata; - typename T::placeholders_pack_t; - requires std::same_as>; - requires std::same_as; - &T::recompile; -}; - /// Policy to control index building /// Exclusive index: each entry in a row has a valid index /// Sparse index: values in a row can be (-1), index table is isomorphic (joinable) to T1 @@ -859,13 +827,6 @@ struct Builds : decltype(transformBase()) { } }; -template -concept is_builds = requires(T t) { - typename T::metadata; - typename T::Key; - requires std::same_as>; -}; - /// a task with rewritten origin, if running together with a task with the default, will /// have a different name and thus its output would be routed separately @@ -877,6 +838,7 @@ concept is_builds = requires(T t) { /// to determine the target file, e.g. analysis result, QA or control histogram, /// etc. template + requires(std::derived_from) struct OutputObj { using obj_t = T; @@ -964,15 +926,6 @@ struct OutputObj { uint32_t mTaskHash; }; -template -concept is_outputobj = requires(T t) { - &T::setHash; - &T::spec; - &T::ref; - requires std::same_as()), typename T::obj_t*>; - requires std::same_as>; -}; - /// This helper allows you to fetch a Sevice from the context or /// by using some singleton. This hopefully will hide the Singleton and /// We will be able to retrieve it in a more thread safe manner later on. @@ -991,12 +944,6 @@ struct Service { } }; -template -concept is_service = requires(T t) { - requires std::same_as; - &T::operator->; -}; - auto getTableFromFilter(soa::is_filtered_table auto const& table, soa::SelectionVector&& selection) { return std::make_unique>>(std::vector{table}, std::forward(selection)); @@ -1016,7 +963,7 @@ void initializePartitionCaches(std::set const& hashes, std::shared_ptr /// the real reason is to provide grouped parts for the process functions that request it /// better solution would be to "slice" the selection, as is already done in GroupSlicer /// for the same purpose, instead of reapplying the filtering -template +template struct Partition { using content_t = T; Partition(expressions::Node&& filter_) : filter{std::forward(filter_)} @@ -1128,13 +1075,6 @@ struct Partition { return mFiltered->size(); } }; - -template -concept is_partition = requires(T t) { - &T::updatePlaceholders; - requires std::same_as; - requires std::same_as>>; -}; } // namespace o2::framework namespace o2::soa diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h index 09cc3ba2687ca..7d6a990e1c762 100644 --- a/Framework/Core/include/Framework/Concepts.h +++ b/Framework/Core/include/Framework/Concepts.h @@ -164,6 +164,10 @@ concept is_not_filtered_table = is_table && !is_filtered_table; template concept is_join = requires(T t) { t.isJoin(); }; +/// 17. require an enumerated iterator +template +concept is_enumerated_iterator = requires(T t) { t.globalIndex(); }; + /// misc /// 1. require a type with originals container template @@ -227,9 +231,84 @@ concept is_preslice_policy = requires(T t) { t.isPreslicePolicy(); }; template concept is_preslice = requires(T t) { t.isPresliceContainer(); }; -/// 3. reqiures a preslice group +/// 3. reqiure a preslice group template concept is_preslice_group = requires(T t) { t.isPresliceGroup(); }; + +/// 4. require a producable entity +template +concept is_producable = soa::has_metadata>> || soa::has_metadata>>; + +/// 5. require produces declaration +template +concept is_produces = requires(T t) { typename T::cursor_t; typename T::persistent_table_t; &T::cursor; }; + +/// 6. require produces group +template +concept is_produces_group = requires(T t) { t.isProducesGroup(); }; + +/// 7. require spawnable entity +template +concept is_spawnable = soa::has_metadata>> && soa::has_extension>::metadata>; + +/// 8. require dynamically spawnable entity +template +concept is_dynamically_spawnable = soa::has_metadata>> && soa::has_configurable_extension>::metadata>; + +/// 9. require spawns declaration +template +concept is_spawns = requires(T t) { + typename T::metadata; + typename T::expression_pack_t; + t.projector.get(); +}; + +/// 10. require defines declaration +template +concept is_defines = requires(T t) { + typename T::metadata; + typename T::placeholders_pack_t; + t.projector.get(); + requires std::same_as; + t.recompile(); +}; + +/// 11. require builds declaration +template +concept is_builds = requires(T t) { + typename T::metadata; + typename T::Key; + t.map.size(); +}; + +/// 12. require outputobj declaration +template +concept is_outputobj = requires(T t) { + &T::setHash; + &T::spec; + &T::ref; + requires std::same_as()), typename T::obj_t*>; + requires std::same_as; +}; + +/// 13. require service declaration +template +concept is_service = requires(T t) { + requires std::same_as; + &T::operator->; +}; + +/// 14. require partition declaration +template +concept is_partition = requires(T t) { + &T::updatePlaceholders; + t.mFiltered.get(); + &T::operator->; + requires std::same_as; + t.begin(); + t.end(); + t.size(); +}; } // namespace o2::framework #endif // O2_FRAMEWORK_CONCEPTS_H From f2979f537fc50323b3c8f095b3a604ccb0f6039d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 9 Jul 2026 11:58:33 +0200 Subject: [PATCH 5/7] address FIXMEs --- Framework/Core/include/Framework/ASoA.h | 2 ++ Framework/Core/include/Framework/Concepts.h | 31 +++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 5ec52a0ddc246..f0d29b3e44eb3 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -4097,6 +4097,8 @@ class Filtered> : public FilteredBase /// First index will be used by process() as the grouping template struct IndexTable : Table { + static constexpr void isIndexTable() + {}; using self_t = IndexTable; using base_t = Table; using table_t = base_t; diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h index 7d6a990e1c762..b96d531a69e56 100644 --- a/Framework/Core/include/Framework/Concepts.h +++ b/Framework/Core/include/Framework/Concepts.h @@ -45,15 +45,20 @@ concept is_persistent_column = requires(C c) { c.isIteratableColumn(); }; /// 2. require self-index column template -concept is_self_index_column = not_void::self_index_t> && std::same_as::self_index_t, std::true_type>; +concept is_self_index_column = requires(C c) +{ + typename C::compatible_signature; + //requires aod::is_aod_hash; + typename C::self_index_t; + requires std::same_as; +}; -/// 3. require bidable index column -/// FIXME: this should really rely on the struct's content instead -struct Binding; +/// 3. require bindable index column template -concept is_index_column = !is_self_index_column && requires(C c, o2::soa::Binding b) { - { c.setCurrentRaw(b) } -> std::same_as; - requires std::same_as; +concept is_index_column = requires(C c) +{ + typename C::binding_t; + requires not_void; }; /// 4. require a column that can be created from an expression @@ -99,12 +104,10 @@ template concept has_parent_t = not_void; /// 2. require a MetadataTrait specialization/descendant -/// FIXME: this should really rely on the struct's content instead template concept is_metadata_trait = requires(T t) { t.isMetadataTrait(); }; /// 3. require a TableMetadata depcialization/descendant -/// FIXME: this should really rely on the struct's content instead template concept is_metadata = requires(T t) { t.isTableMetadata(); }; @@ -133,12 +136,11 @@ template concept is_table_or_iterator = is_table || is_iterator; /// 10. require soa::IndexTable -/// FIXME: this should really rely on the struct's content instead -template -struct IndexTable; - template -concept is_index_table = framework::specialization_of_template; +concept is_index_table = requires(T t) +{ + t.isIndexTable(); +}; /// 11. require a type with a filtered policy template @@ -194,7 +196,6 @@ concept with_ccdb_urls = requires(T t) { }; /// 5. require a type, whos metadata has base_table_t dependant type -/// FIXME: this should really rely on the struct's content instead template concept with_base_table = with_originals && has_metadata>> && requires { typename aod::MetadataTrait>::metadata::base_table_t; From 398799257591d36b44267334aead519435ceb23c Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 9 Jul 2026 12:06:05 +0200 Subject: [PATCH 6/7] fixup! address FIXMEs --- Framework/Core/include/Framework/AnalysisHelpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 77e9ed760b1e0..5600e201feb8f 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -838,7 +838,7 @@ struct Builds : decltype(transformBase()) { /// to determine the target file, e.g. analysis result, QA or control histogram, /// etc. template - requires(std::derived_from) + requires(std::derived_from) struct OutputObj { using obj_t = T; From 483c7af2eea639a22e69780f2415c6e867f64a56 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 9 Jul 2026 16:04:35 +0200 Subject: [PATCH 7/7] include cstdint for uint32_t Co-authored-by: Anton Alkin --- Framework/Core/include/Framework/Concepts.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/Core/include/Framework/Concepts.h b/Framework/Core/include/Framework/Concepts.h index b96d531a69e56..e868b439bb286 100644 --- a/Framework/Core/include/Framework/Concepts.h +++ b/Framework/Core/include/Framework/Concepts.h @@ -13,6 +13,7 @@ #define O2_FRAMEWORK_CONCEPTS_H #include +#include #include namespace o2::aod