diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index cf0bba20e74ea..5d18a31dcb955 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -59,7 +59,7 @@ template static constexpr bool is_enumeration_v> = true; template -concept is_enumeration = is_enumeration_v; +concept is_enumeration = is_enumeration_v>; // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... @@ -140,48 +140,84 @@ struct AnalysisDataProcessorBuilder { DataSpecUtils::updateInputList(inputs, InputSpec{o2::aod::label(), o2::aod::origin(), aod::description(o2::aod::signature()), R.version, Lifetime::Timeframe, inputMetadata}); } - template - static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos, std::vector& bk, std::vector& bku) requires(std::is_lvalue_reference_v&&...) + /// helpers to append expression information for a single argument + template + requires(!soa::is_filtered_table>) + static void addExpression(int, uint32_t, std::vector&) { - // update grouping cache - if constexpr (soa::is_iterator>>>) { - addGroupingCandidates(bk, bku); - } + } + + template + static void addExpression(int ai, uint32_t hash, std::vector& eInfos) + { + auto fields = soa::createFieldsFromColumns(typename std::decay_t::persistent_columns_t{}); + eInfos.emplace_back(ai, hash, std::decay_t::hashes(), std::make_shared(fields)); + } + + template + static void addExpression(int ai, uint32_t hash, std::vector& eInfos) + { + addExpression::parent_t>(ai, hash, eInfos); + } + + /// helpers to append InputSpec for a single argument + template + static void addInput(const char* name, bool value, std::vector& inputs) + { + [&name, &value, &inputs] refs, size_t... Is>(std::index_sequence) mutable { + (addOriginalRef(name, value, inputs), ...); + }.template operator()::originals>(std::make_index_sequence::originals.size()>()); + } + + template + static void addInput(const char* name, bool value, std::vector& inputs) + { + addInput::parent_t>(name, value, inputs); + } - // populate input list and expression infos + /// helper to append the inputs and expression information for normalized arguments + template + static void addInputsAndExpressions(uint32_t hash, const char* name, bool value, std::vector& inputs, std::vector& eInfos) + { int ai = -1; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); - ([&name, &value, &eInfos, &inputs, &hash, &ai]() mutable { + ([&ai, &hash, &eInfos, &name, &value, &inputs]() mutable { ++ai; - using T = std::decay_t; - if constexpr (is_enumeration) { - std::vector inputMetadata; - // FIXME: for the moment we do not support begin, end and step. - DataSpecUtils::updateInputList(inputs, InputSpec{"enumeration", "DPL", "ENUM", 0, Lifetime::Enumeration, inputMetadata}); - } else { - // populate expression infos - if constexpr (soa::is_filtered_table) { - auto fields = soa::createFieldsFromColumns(typename T::persistent_columns_t{}); - eInfos.emplace_back(ai, hash, T::hashes(), std::make_shared(fields)); - } else if constexpr (soa::is_filtered_iterator) { - auto fields = soa::createFieldsFromColumns(typename T::parent_t::persistent_columns_t{}); - eInfos.emplace_back(ai, hash, T::parent_t::hashes(), std::make_shared(fields)); - } - // add inputs from the originals - auto adder = [&name, &value, &inputs] refs, size_t... Is>(std::index_sequence) mutable { - (addOriginalRef(name, value, inputs), ...); - }; - if constexpr (soa::is_table || soa::is_filtered_table) { - adder.template operator()(std::make_index_sequence()); - } else if constexpr (soa::is_iterator || soa::is_filtered_iterator) { - adder.template operator()(std::make_index_sequence()); - } - } - return true; - }() && + using T = std::decay_t; + addExpression(ai, hash, eInfos); + addInput(name, value, inputs); + }(), ...); } + /// helper to parse the process arguments + /// 1. enumeration (must be the only argument) + template + static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&, std::vector&, std::vector&) + { + std::vector inputMetadata; + // FIXME: for the moment we do not support begin, end and step. + DataSpecUtils::updateInputList(inputs, InputSpec{"enumeration", "DPL", "ENUM", 0, Lifetime::Enumeration, inputMetadata}); + } + + /// 2. grouping case - 1st argument is an iterator + template + static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos, std::vector& bk, std::vector& bku) + requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) + { + addGroupingCandidates(bk, bku); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); + } + + /// 3. generic case + template + static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos, std::vector&, std::vector&) + requires(std::is_lvalue_reference_v && ...) + { + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + addInputsAndExpressions(hash, name, value, inputs, eInfos); + } + template static auto extractTableFromRecord(InputRecord& record) { @@ -498,19 +534,19 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) homogeneous_apply_refs([&inputs](auto& x) { return ConditionManager>::appendCondition(inputs, x); }, *task.get()); /// parse process functions defined by corresponding configurables - if constexpr (requires { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos, bindingsKeys, bindingsKeysUnsorted); }) { + if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos, bindingsKeys, bindingsKeysUnsorted); } homogeneous_apply_refs( - [name = name_str, &expressionInfos, &inputs, &bindingsKeys, &bindingsKeysUnsorted](auto& x) { - using D = std::decay_t; - if constexpr (base_of_template) { + overloaded{ + [name = name_str, &expressionInfos, &inputs, &bindingsKeys, &bindingsKeysUnsorted](framework::is_process_configurable auto& x) mutable { // this pushes (argumentIndex,processHash,schemaPtr,nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators AnalysisDataProcessorBuilder::inputsFromArgs(x.process, (name + "/" + x.name).c_str(), x.value, inputs, expressionInfos, bindingsKeys, bindingsKeysUnsorted); return true; - } - return false; - }, + }, + [](auto&) { + return false; + }}, *task.get()); // add preslice declarations to slicing cache definition diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index f1167adb5ebdd..88e50cf3c7c26 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -11,6 +11,7 @@ #ifndef O2_FRAMEWORK_CONFIGURABLE_H_ #define O2_FRAMEWORK_CONFIGURABLE_H_ #include "Framework/ConfigurableKinds.h" +#include "Framework/Traits.h" #include #include namespace o2::framework @@ -95,6 +96,9 @@ struct ProcessConfigurable : Configurable { (As...); }; +template +concept is_process_configurable = base_of_template; + #define PROCESS_SWITCH(_Class_, _Name_, _Help_, _Default_) \ decltype(ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}) do##_Name_ = ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}; #define PROCESS_SWITCH_FULL(_Class_, _Method_, _Name_, _Help_, _Default_) \