File indexing completed on 2026-07-20 08:33:04
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #ifndef FWCORE_FUNCTIONALUTILS_H
0020 #define FWCORE_FUNCTIONALUTILS_H
0021
0022 #include "Gaudi/Functional/details.h"
0023 #include "GaudiKernel/AnyDataWrapper.h"
0024 #include "GaudiKernel/DataObjID.h"
0025 #include "GaudiKernel/DataObjectHandle.h"
0026 #include "GaudiKernel/EventContext.h"
0027 #include "GaudiKernel/IDataProviderSvc.h"
0028 #include "GaudiKernel/ThreadLocalContext.h"
0029 #include <GaudiKernel/GaudiException.h>
0030
0031 #include "podio/CollectionBase.h"
0032
0033 #include "k4FWCore/DataWrapper.h"
0034
0035
0036
0037 #include <fmt/format.h>
0038
0039 #include <memory>
0040 #include <tuple>
0041 #include <type_traits>
0042
0043 namespace k4FWCore {
0044
0045 static const std::string frameLocation = "/_Frame";
0046
0047 namespace details {
0048
0049
0050 template <typename T>
0051 struct EventStoreType {
0052 using type = std::unique_ptr<podio::CollectionBase>;
0053 };
0054 using EventStoreType_t = typename EventStoreType<void>::type;
0055
0056
0057 template <typename T, typename P>
0058 requires(!std::is_same_v<P, podio::CollectionBase*>)
0059 const auto& maybeTransformToEDM4hep(const P& arg) {
0060 return arg;
0061 }
0062
0063
0064 template <typename T, typename P>
0065 requires std::same_as<P, podio::CollectionBase*>
0066 const auto& maybeTransformToEDM4hep(P&& arg) {
0067 return static_cast<const T&>(*arg);
0068 }
0069
0070
0071 template <typename T, typename P>
0072 requires std::same_as<P, podio::CollectionBase* const>
0073 const auto& maybeTransformToEDM4hep(P& arg) {
0074 return static_cast<const podio::CollectionBase&>(*arg);
0075 }
0076
0077
0078 template <typename T, typename P>
0079 requires(std::is_base_of_v<podio::CollectionBase, P> && !std::same_as<podio::CollectionBase, P>)
0080 const auto& maybeTransformToEDM4hep(P* arg) {
0081 return *arg;
0082 }
0083
0084 template <typename T, bool addPtr = std::is_base_of_v<podio::CollectionBase, T>>
0085 using addPtrIfColl = std::conditional_t<addPtr, std::add_pointer_t<T>, T>;
0086
0087
0088
0089
0090
0091 template <typename T>
0092 struct isVectorLike : std::false_type {};
0093
0094 template <typename Value>
0095 requires std::is_base_of_v<podio::CollectionBase, std::remove_cv_t<Value>> ||
0096 std::is_same_v<podio::CollectionBase*, std::remove_cv_t<Value>>
0097 struct isVectorLike<std::vector<Value*>> : std::true_type {};
0098
0099 template <typename Value>
0100 requires std::is_base_of_v<podio::CollectionBase, std::remove_cv_t<Value>>
0101 struct isVectorLike<std::vector<Value>> : std::true_type {};
0102
0103 template <class T>
0104 inline constexpr bool isVectorLike_v = isVectorLike<T>::value;
0105
0106 template <typename T>
0107 auto convertToUniquePtr(T&& arg) {
0108
0109
0110 if constexpr (std::same_as<T, podio::CollectionBase*>) {
0111 return std::unique_ptr<podio::CollectionBase>(std::forward<T>(arg));
0112 } else {
0113
0114
0115 return std::make_unique<T>(std::forward<T>(arg));
0116 }
0117 }
0118
0119 template <typename... In>
0120 struct filter_evtcontext {
0121 static_assert(!std::disjunction_v<std::is_same<EventContext, In>...>,
0122 "EventContext can only appear as first argument");
0123
0124 using type = std::tuple<In...>;
0125 static constexpr std::size_t size = std::tuple_size_v<type>;
0126
0127 template <typename Algorithm, typename Handles>
0128 static auto apply(const Algorithm& algo, Handles& handles) {
0129 return std::apply(
0130 [&](const auto&... handle) { return algo(get(handle, algo, Gaudi::Hive::currentContext())...); }, handles);
0131 }
0132 template <typename Algorithm, typename Handles>
0133 static auto apply(const Algorithm& algo, const EventContext&, Handles& handles) {
0134 auto inputTuple = std::tuple<addPtrIfColl<In>...>();
0135
0136
0137
0138 readVectorInputs<0, In...>(handles, &algo, inputTuple);
0139
0140 return std::apply([&](const auto&... input) { return algo(maybeTransformToEDM4hep<decltype(input)>(input)...); },
0141 inputTuple);
0142 }
0143 };
0144
0145 template <typename... In>
0146 struct filter_evtcontext<EventContext, In...> {
0147 static_assert(!std::disjunction_v<std::is_same<EventContext, In>...>,
0148 "EventContext can only appear as first argument");
0149
0150 using type = std::tuple<In...>;
0151 static constexpr std::size_t size = std::tuple_size_v<type>;
0152
0153 template <typename Algorithm, typename Handles>
0154 static auto apply(const Algorithm& algo, const EventContext& ctx, Handles& handles) {
0155 auto inputTuple = std::tuple<addPtrIfColl<In>...>();
0156
0157
0158
0159 readVectorInputs<0, In...>(handles, &algo, inputTuple);
0160
0161 return std::apply(
0162 [&](const auto&... input) { return algo(ctx, maybeTransformToEDM4hep<decltype(input)>(input)...); },
0163 inputTuple);
0164 }
0165 };
0166
0167 template <typename... In>
0168 using filter_evtcontext_t = typename filter_evtcontext<In...>::type;
0169
0170
0171
0172
0173 template <template <typename> class Handle, typename Tuple>
0174 struct tuple_of_handle_vec {
0175 using type = std::tuple<>;
0176 };
0177
0178 template <template <typename> class Handle, typename... Ts>
0179 struct tuple_of_handle_vec<Handle, std::tuple<Ts...>> {
0180 using type = std::tuple<std::vector<Handle<typename EventStoreType<Ts>::type>>...>;
0181 };
0182
0183 template <template <typename> class Handle, typename... Ts>
0184 using tuple_of_handle_vec_t = typename tuple_of_handle_vec<Handle, Ts...>::type;
0185
0186 template <size_t Index, typename... In, typename... Handles, typename InputTuple>
0187 void readVectorInputs(const std::tuple<Handles...>& handles, auto thisClass, InputTuple& inputTuple) {
0188 if constexpr (Index < sizeof...(Handles)) {
0189 using TupleType = std::tuple_element_t<Index, std::tuple<In...>>;
0190 if constexpr (isVectorLike_v<TupleType>) {
0191
0192 using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<typename TupleType::value_type>>;
0193 auto inputVector = std::vector<const EDM4hepType*>();
0194 inputVector.reserve(std::get<Index>(handles).size());
0195 for (const auto& handle : std::get<Index>(handles)) {
0196 podio::CollectionBase* collection = handle.get()->get();
0197 auto* typedCollection = dynamic_cast<const EDM4hepType*>(collection);
0198 if (typedCollection) {
0199 inputVector.push_back(typedCollection);
0200 } else {
0201 throw GaudiException(
0202 fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
0203 handle.objKey(), typeid(EDM4hepType).name(),
0204 collection ? collection->getTypeName() : "[undetermined]"),
0205 thisClass->name(), StatusCode::FAILURE);
0206 }
0207 }
0208 std::get<Index>(inputTuple) = std::move(inputVector);
0209 } else {
0210
0211 using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<TupleType>>;
0212 try {
0213 podio::CollectionBase* collection = std::get<Index>(handles)[0].get()->get();
0214 auto* typedCollection = dynamic_cast<EDM4hepType*>(collection);
0215 if (typedCollection) {
0216 std::get<Index>(inputTuple) = typedCollection;
0217 } else {
0218 throw GaudiException(
0219 fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
0220 std::get<Index>(handles)[0].objKey(), typeid(EDM4hepType).name(),
0221 collection ? collection->getTypeName() : "[undetermined]"),
0222 thisClass->name(), StatusCode::FAILURE);
0223 }
0224 } catch (GaudiException& e) {
0225
0226
0227
0228 if (e.message().find("different from") != std::string::npos) {
0229 thisClass->debug() << "Trying to cast the collection " << std::get<Index>(handles)[0].objKey()
0230 << " to the requested type didn't work " << endmsg;
0231 DataObject* dataObject;
0232 IDataProviderSvc* eventDataSvc = thisClass->evtSvc();
0233 eventDataSvc->retrieveObject("/Event/" + std::get<Index>(handles)[0].objKey(), dataObject).ignore();
0234
0235 const auto* wrapper = dynamic_cast<const DataWrapper<EDM4hepType>*>(dataObject);
0236
0237 const auto* marlinWrapper = dynamic_cast<const DataWrapper<podio::CollectionBase>*>(dataObject);
0238 if (!wrapper && !marlinWrapper) {
0239 throw GaudiException(fmt::format("Failed to cast collection {} to the required type {}",
0240 std::get<Index>(handles)[0].objKey(), typeid(EDM4hepType).name()),
0241 thisClass->name(), StatusCode::FAILURE);
0242 }
0243 if (wrapper) {
0244 std::get<Index>(inputTuple) = const_cast<EDM4hepType*>(wrapper->getData());
0245 } else {
0246 std::get<Index>(inputTuple) =
0247 dynamic_cast<EDM4hepType*>(const_cast<podio::CollectionBase*>(marlinWrapper->getData()));
0248 }
0249 } else {
0250 throw e;
0251 }
0252 }
0253 }
0254
0255
0256 readVectorInputs<Index + 1, In...>(handles, thisClass, inputTuple);
0257 }
0258 }
0259
0260 template <size_t Index, typename... Out, typename... Handles>
0261 void putVectorOutputs(std::tuple<Handles...>&& handles, const auto& outputs, auto thisClass) {
0262 if constexpr (Index < sizeof...(Handles)) {
0263 auto& outputHandles = std::get<Index>(handles);
0264 if constexpr (isVectorLike_v<std::tuple_element_t<Index, std::tuple<Out...>>>) {
0265 const auto& outputVector = std::get<Index>(outputs);
0266 if (outputHandles.size() != outputVector.size()) {
0267 throw GaudiException(fmt::format("Size of the output vector {} with type {} does not match the expected size "
0268 "from the steering file {}",
0269 outputHandles.size(), typeid(outputHandles).name(), outputVector.size()),
0270 thisClass->name(), StatusCode::FAILURE);
0271 }
0272 size_t index = 0;
0273 for (auto& value : outputHandles) {
0274 Gaudi::Functional::details::put(outputVector[index], convertToUniquePtr(std::move(value)));
0275 ++index;
0276 }
0277 } else {
0278 Gaudi::Functional::details::put(std::get<Index>(outputs)[0], convertToUniquePtr(std::move(outputHandles)));
0279 }
0280
0281
0282 putVectorOutputs<Index + 1, Out...>(std::move(handles), outputs, thisClass);
0283 }
0284 }
0285
0286 inline std::vector<DataObjID> to_DataObjID(const std::vector<std::string>& inputStrings) {
0287 std::vector<DataObjID> outputIds;
0288 outputIds.reserve(inputStrings.size());
0289 std::transform(inputStrings.begin(), inputStrings.end(), std::back_inserter(outputIds),
0290 [](const std::string& str) { return DataObjID{str}; });
0291 return outputIds;
0292 }
0293
0294 inline std::vector<DataObjID> to_DataObjID(const std::string& inputString) { return {DataObjID{inputString}}; }
0295
0296
0297
0298
0299 template <typename T>
0300 class FunctionalDataObjectReadHandle : public ::details::ReadHandle<T> {
0301 template <typename... Args, std::size_t... Is>
0302 FunctionalDataObjectReadHandle(std::tuple<Args...>&& args, std::index_sequence<Is...>)
0303 : FunctionalDataObjectReadHandle(std::get<Is>(std::move(args))...) {}
0304
0305 public:
0306
0307
0308 template <typename OWNER, typename K, typename = std::enable_if_t<std::is_base_of_v<IProperty, OWNER>>>
0309 FunctionalDataObjectReadHandle(OWNER* owner, std::string propertyName, K key = {}, std::string doc = "")
0310 : ::details::ReadHandle<T>(std::move(key), Gaudi::DataHandle::Reader, owner) {
0311 auto p = owner->declareProperty(std::move(propertyName), *this, std::move(doc));
0312 p->template setOwnerType<OWNER>();
0313 }
0314
0315 template <typename... Args>
0316 FunctionalDataObjectReadHandle(std::tuple<Args...>&& args)
0317 : FunctionalDataObjectReadHandle(std::move(args), std::index_sequence_for<Args...>{}) {}
0318
0319 const T& get() const;
0320 };
0321
0322 template <typename T>
0323 const T& FunctionalDataObjectReadHandle<T>::get() const {
0324 const auto dataObj = this->fetch();
0325 if (!dataObj) {
0326 throw GaudiException(fmt::format("Cannot retrieve '{}' from transient store [{}]", this->objKey(),
0327 this->m_owner ? this->owner()->name() : "no owner"),
0328 "FunctionalDataObjectReadHandle", StatusCode::FAILURE);
0329 }
0330 const auto ptr = dynamic_cast<AnyDataWrapper<std::unique_ptr<podio::CollectionBase>>*>(dataObj);
0331 return maybeTransformToEDM4hep<T>(ptr->getData().get());
0332 }
0333
0334 struct BaseClass_t {
0335 template <typename T>
0336 using InputHandle = FunctionalDataObjectReadHandle<T>;
0337
0338
0339 using BaseClass = Gaudi::Algorithm;
0340 };
0341
0342 template <typename InputHandle, typename KeyValue>
0343 Gaudi::Property<DataObjID> makeInputPropSingle(const auto& inp, auto& classInputs, auto* thisClass) {
0344 if (inp.index() == 0) {
0345 const auto& input = std::get<KeyValue>(inp);
0346 return {Gaudi::Property<DataObjID>(
0347 thisClass, input.first, to_DataObjID(input.second)[0],
0348 [thisClass, &classInputs](Gaudi::Details::PropertyBase& p) {
0349 std::vector<InputHandle> handles;
0350 auto handle = InputHandle(static_cast<Gaudi::Property<DataObjID>&>(p).value(), thisClass);
0351 handles.push_back(std::move(handle));
0352 classInputs = std::move(handles);
0353 },
0354 Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0355 } else {
0356 return {};
0357 }
0358 }
0359 template <typename InputHandle, typename KeyValues>
0360 Gaudi::Property<std::vector<DataObjID>> makeInputPropVector(const auto& inp, auto& classInputs, auto* thisClass) {
0361 if (inp.index() == 1) {
0362 const auto& input = std::get<KeyValues>(inp);
0363 return {Gaudi::Property<std::vector<DataObjID>>(
0364 thisClass, input.first, to_DataObjID(input.second),
0365 [thisClass, &classInputs](Gaudi::Details::PropertyBase& p) {
0366 const auto& tmpprop = static_cast<Gaudi::Property<std::vector<DataObjID>>&>(p);
0367 const auto& tmpval = tmpprop.value();
0368 std::vector<InputHandle> handles;
0369 handles.reserve(tmpval.size());
0370 for (const auto& value : tmpval) {
0371 auto handle = InputHandle(value, thisClass);
0372 handles.push_back(std::move(handle));
0373 }
0374 classInputs = std::move(handles);
0375 },
0376 Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0377 } else {
0378 return {};
0379 }
0380 }
0381
0382 template <typename OutputHandle, typename KeyValue>
0383 Gaudi::Property<DataObjID> makeOutputPropSingle(const auto& out, auto& classOutputs, auto* thisClass) {
0384 if (out.index() == 0) {
0385 const auto& output = std::get<KeyValue>(out);
0386 return {Gaudi::Property<DataObjID>(
0387 thisClass, output.first, to_DataObjID(output.second)[0],
0388 [thisClass, &classOutputs](Gaudi::Details::PropertyBase& p) {
0389 std::vector<OutputHandle> handles;
0390 auto handle = OutputHandle(static_cast<Gaudi::Property<DataObjID>&>(p).value(), thisClass);
0391 handles.push_back(std::move(handle));
0392 classOutputs = std::move(handles);
0393 },
0394 Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0395 } else {
0396 return {};
0397 }
0398 }
0399 template <typename OutputHandle, typename KeyValues>
0400 Gaudi::Property<std::vector<DataObjID>> makeOutputPropVector(const auto& out, auto& classOutputs, auto* thisClass) {
0401 if (out.index() == 1) {
0402 const auto& output = std::get<KeyValues>(out);
0403 return {Gaudi::Property<std::vector<DataObjID>>(
0404 thisClass, output.first, to_DataObjID(output.second),
0405 [thisClass, &classOutputs](Gaudi::Details::PropertyBase& p) {
0406 std::vector<OutputHandle> handles;
0407 const auto& tmpprop = static_cast<Gaudi::Property<std::vector<DataObjID>>&>(p);
0408 const auto& tmpval = tmpprop.value();
0409 for (const auto& value : tmpval) {
0410 if (value.key().empty()) {
0411 continue;
0412 }
0413 auto handle = OutputHandle(value, thisClass);
0414 handles.push_back(std::move(handle));
0415 }
0416 classOutputs = std::move(handles);
0417 },
0418 Gaudi::Details::Property::ImmediatelyInvokeHandler{true})};
0419 } else {
0420 return {};
0421 }
0422 }
0423
0424 }
0425 }
0426
0427 #endif