From 2c4d26b6d51d2097b6fd203400cd15167f68a9de Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:50:47 +0200 Subject: [PATCH] DPL: use C++26 extension to expand packs where available O(1) in both CPU and time rather than O(N) when compiling the pack expansion. Removes the limit to 100 elements in a struct. Requires XCode 26.4 --- .../Core/include/Framework/AnalysisTask.h | 4 +- .../Core/include/Framework/TableBuilder.h | 19 +++ .../include/Framework/StructToTuple.h | 140 +++++++++--------- 3 files changed, 93 insertions(+), 70 deletions(-) diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 1e483d017cc88..aa86525df1721 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -317,7 +317,7 @@ struct AnalysisDataProcessorBuilder { groupingTable.setPointerReconstructor(pointerReconstructor); } #endif - constexpr const int numElements = nested_brace_constructible_size>() / 10; + constexpr const int numElements = homogeneous_apply_refs_size>(); // set filtered tables for partitions with grouping homogeneous_apply_refs_sized([&groupingTable](auto& element) { @@ -546,7 +546,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) newOrigin.runtimeInit(newOriginStr.c_str(), std::min(newOriginStr.size(), 4UL)); } - constexpr const int numElements = nested_brace_constructible_size>() / 10; + constexpr const int numElements = homogeneous_apply_refs_size>(); /// make sure options and configurables are set before expression infos are created homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); diff --git a/Framework/Core/include/Framework/TableBuilder.h b/Framework/Core/include/Framework/TableBuilder.h index 41f6d4ea5dc86..3b2d81bc04b00 100644 --- a/Framework/Core/include/Framework/TableBuilder.h +++ b/Framework/Core/include/Framework/TableBuilder.h @@ -558,6 +558,23 @@ constexpr auto tuple_to_pack(std::tuple&&) /// Helper function to convert a brace-initialisable struct to /// a tuple. +#ifdef DPL_STRUCTURED_BINDING_PACKS +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++26-extensions" +#endif +template +auto constexpr to_tuple(T&& object) noexcept +{ + auto&& [... members] = object; + return std::make_tuple(members...); +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#else // DPL_STRUCTURED_BINDING_PACKS + template auto constexpr to_tuple(T&& object) noexcept { @@ -579,6 +596,8 @@ auto constexpr to_tuple(T&& object) noexcept } } +#endif // DPL_STRUCTURED_BINDING_PACKS + template constexpr auto makeHolderTypes() { diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 1c7aa62260bd3..e06df1bab984a 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -14,6 +14,14 @@ #include #include +// Structured binding packs (P1061) are C++26, but clang implements them as an +// extension in every language mode and advertises them via the feature test +// macro, so we can use them while still compiling as C++20. +#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 202411L +#define DPL_STRUCTURED_BINDING_PACKS 1 +#endif + +#ifndef DPL_STRUCTURED_BINDING_PACKS namespace { template @@ -24,9 +32,11 @@ template std::false_type brace_test(...); } // namespace +#endif namespace o2::framework { +#ifndef DPL_STRUCTURED_BINDING_PACKS struct any_type { template constexpr operator T(); // non explicit @@ -35,19 +45,67 @@ struct any_type { template struct is_braces_constructible : decltype(brace_test(0)) { }; +#endif -#define DPL_REPEAT_0(x) -#define DPL_REPEAT_1(x) x -#define DPL_REPEAT_2(x) x, x -#define DPL_REPEAT_3(x) x, x, x -#define DPL_REPEAT_4(x) x, x, x, x -#define DPL_REPEAT_5(x) x, x, x, x, x -#define DPL_REPEAT_6(x) x, x, x, x, x, x -#define DPL_REPEAT_7(x) x, x, x, x, x, x, x -#define DPL_REPEAT_8(x) x, x, x, x, x, x, x, x -#define DPL_REPEAT_9(x) x, x, x, x, x, x, x, x, x -#define DPL_REPEAT_10(x) x, x, x, x, x, x, x, x, x, x -#define DPL_REPEAT(x, d, u) DPL_REPEAT_##d(DPL_REPEAT_10(x)), DPL_REPEAT_##u(x) +struct UniversalType { + template + operator T() + { + } +}; + +template +consteval auto brace_constructible_size(auto... Members) +{ + if constexpr (requires { T{Members...}; } == false) { + static_assert(sizeof...(Members) != 0, "You need to make sure that you have implicit constructors or that you call the explicit constructor correctly."); + return sizeof...(Members) - 1; + } else { + return brace_constructible_size(Members..., UniversalType{}); + } +} + +template +consteval int nested_brace_constructible_size() +{ + using type = std::decay_t; + constexpr int nesting = B ? 1 : 0; + return brace_constructible_size() - nesting; +} + +/// The size to be passed to homogeneous_apply_refs_sized for T. Structured +/// binding packs do not need to know how many members T has, so we can skip +/// counting them altogether. +template +consteval int homogeneous_apply_refs_size() +{ +#ifdef DPL_STRUCTURED_BINDING_PACKS + return 0; +#else + return nested_brace_constructible_size() / 10; +#endif +} + +#ifdef DPL_STRUCTURED_BINDING_PACKS +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++26-extensions" +#endif +template +constexpr auto homogeneous_apply_refs(L l, T&& object) +{ + auto&& [... members] = object; + if constexpr (sizeof...(members) == 0) { + return std::array(); + } else { + return std::array{l(members)...}; + } +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#else // DPL_STRUCTURED_BINDING_PACKS #define DPL_ENUM_0(pre, post) #define DPL_ENUM_1(pre, post) pre##0##post @@ -97,54 +155,6 @@ struct is_braces_constructible : decltype(brace_test(0)) { #define DPL_FENUM(f, pre, post, d, u) DPL_FENUM_##d##0(f, pre, post), DPL_FENUM_##u(f, pre##d, post) -#define DPL_10_As DPL_REPEAT_10(A) -#define DPL_20_As DPL_10_As, DPL_10_As -#define DPL_30_As DPL_20_As, DPL_10_As -#define DPL_40_As DPL_30_As, DPL_10_As -#define DPL_50_As DPL_40_As, DPL_10_As -#define DPL_60_As DPL_50_As, DPL_10_As -#define DPL_70_As DPL_60_As, DPL_10_As -#define DPL_80_As DPL_70_As, DPL_10_As -#define DPL_90_As DPL_80_As, DPL_10_As -#define DPL_100_As DPL_90_As, DPL_10_As - -#define DPL_0_9(pre, po) pre##0##po, pre##1##po, pre##2##po, pre##3##po, pre##4##po, pre##5##po, pre##6##po, pre##7##po, pre##8##po, pre##9##po - -#define BRACE_CONSTRUCTIBLE_ENTRY_LOW(u) \ - constexpr(is_braces_constructible{}) \ - { \ - return u; \ - } -#define BRACE_CONSTRUCTIBLE_ENTRY(d, u) \ - constexpr(is_braces_constructible{}) \ - { \ - return d##u; \ - } - -#define BRACE_CONSTRUCTIBLE_ENTRY_TENS(d) \ - constexpr(is_braces_constructible{}) \ - { \ - return d##0; \ - } - -struct UniversalType { - template - operator T() - { - } -}; - -template -consteval auto brace_constructible_size(auto... Members) -{ - if constexpr (requires { T{Members...}; } == false) { - static_assert(sizeof...(Members) != 0, "You need to make sure that you have implicit constructors or that you call the explicit constructor correctly."); - return sizeof...(Members) - 1; - } else { - return brace_constructible_size(Members..., UniversalType{}); - } -} - #define DPL_HOMOGENEOUS_APPLY_ENTRY_LOW(u) \ constexpr(numElements == u) \ { \ @@ -166,14 +176,6 @@ consteval auto brace_constructible_size(auto... Members) return std::array{DPL_FENUM_##d##0(l, p, )}; \ } -template -consteval int nested_brace_constructible_size() -{ - using type = std::decay_t; - constexpr int nesting = B ? 1 : 0; - return brace_constructible_size() - nesting; -} - template () / 10, typename L> requires(D == 9) constexpr auto homogeneous_apply_refs(L l, T&& object) @@ -373,6 +375,8 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } +#endif // DPL_STRUCTURED_BINDING_PACKS + template constexpr auto homogeneous_apply_refs_sized(L l, T&& object) {