From 79688b8cb4af7e24fadae1752b364346cef6388b Mon Sep 17 00:00:00 2001 From: jmcarcell Date: Fri, 11 Oct 2024 10:58:12 +0200 Subject: [PATCH] Use std::unique_ptr to save collections in the store Also fix a leak in the Writer that without this change would have been difficult to fix. --- k4FWCore/components/CollectionMerger.cpp | 32 ++++----- k4FWCore/components/IIOSvc.h | 2 +- k4FWCore/components/IOSvc.cpp | 9 ++- k4FWCore/components/IOSvc.h | 2 +- k4FWCore/components/Reader.cpp | 21 +++--- k4FWCore/components/Writer.cpp | 22 +++--- k4FWCore/include/k4FWCore/Consumer.h | 15 ++-- k4FWCore/include/k4FWCore/DataHandle.h | 6 +- k4FWCore/include/k4FWCore/DataWrapper.h | 2 +- k4FWCore/include/k4FWCore/FunctionalUtils.h | 76 ++++++++------------- k4FWCore/include/k4FWCore/Transformer.h | 39 ++++++----- 11 files changed, 100 insertions(+), 126 deletions(-) diff --git a/k4FWCore/components/CollectionMerger.cpp b/k4FWCore/components/CollectionMerger.cpp index af4da585..89845de2 100644 --- a/k4FWCore/components/CollectionMerger.cpp +++ b/k4FWCore/components/CollectionMerger.cpp @@ -38,12 +38,11 @@ #include "k4FWCore/Transformer.h" #include -#include #include #include -struct CollectionMerger final : k4FWCore::Transformer( - const std::vector*>&)> { +struct CollectionMerger final + : k4FWCore::Transformer&)> { CollectionMerger(const std::string& name, ISvcLocator* svcLoc) : Transformer(name, svcLoc, {KeyValues("InputCollections", {"MCParticles"})}, {KeyValues("OutputCollection", {"NewMCParticles"})}) { @@ -91,43 +90,38 @@ struct CollectionMerger final : k4FWCore::Transformer; } - std::shared_ptr operator()( - const std::vector*>& input) const override { - std::shared_ptr ret; + podio::CollectionBase* operator()(const std::vector& input) const override { + podio::CollectionBase* ret = nullptr; debug() << "Merging " << input.size() << " collections" << endmsg; std::string_view type = ""; for (const auto& coll : input) { - debug() << "Merging collection of type " << (*coll)->getTypeName() << " with " << (*coll)->size() << " elements" + debug() << "Merging collection of type " << coll->getTypeName() << " with " << coll->size() << " elements" << endmsg; if (type.empty()) { - type = (*coll)->getTypeName(); - } else if (type != (*coll)->getTypeName()) { + type = coll->getTypeName(); + } else if (type != coll->getTypeName()) { throw std::runtime_error("Different collection types are not supported"); - return ret; } - (this->*m_map.at((*coll)->getTypeName()))(*coll, ret); + (this->*m_map.at(coll->getTypeName()))(coll, ret); } return ret; } private: - using MergeType = void (CollectionMerger::*)(const std::shared_ptr&, - std::shared_ptr&) const; + using MergeType = void (CollectionMerger::*)(const podio::CollectionBase*, podio::CollectionBase*&) const; std::map m_map; Gaudi::Property m_copy{this, "Copy", false, "Copy the elements of the collections instead of creating a subset collection"}; - template - void mergeCollections(const std::shared_ptr& source, - std::shared_ptr& ret) const { + template void mergeCollections(const podio::CollectionBase* source, podio::CollectionBase*& ret) const { if (!ret) { - ret = std::make_shared(); + ret = new T(); if (!m_copy) { ret->setSubsetCollection(); } } - const auto ptr = std::static_pointer_cast(ret); - const auto sourceColl = std::static_pointer_cast(source); + const auto ptr = static_cast(ret); + const auto sourceColl = static_cast(source); if (m_copy) { for (const auto& elem : *sourceColl) { ptr->push_back(elem.clone()); diff --git a/k4FWCore/components/IIOSvc.h b/k4FWCore/components/IIOSvc.h index 288164ef..df07eff8 100644 --- a/k4FWCore/components/IIOSvc.h +++ b/k4FWCore/components/IIOSvc.h @@ -39,7 +39,7 @@ class IIOSvc : virtual public IInterface { * @brief Read the next event from the input file * @return A tuple containing the collections read, the collection names and the frame that owns the collections */ - virtual std::tuple>, std::vector, podio::Frame> + virtual std::tuple, std::vector, podio::Frame> next() = 0; virtual std::shared_ptr> getCollectionNames() const = 0; diff --git a/k4FWCore/components/IOSvc.cpp b/k4FWCore/components/IOSvc.cpp index ec7f7acf..b8f6d217 100644 --- a/k4FWCore/components/IOSvc.cpp +++ b/k4FWCore/components/IOSvc.cpp @@ -109,15 +109,14 @@ StatusCode IOSvc::initialize() { StatusCode IOSvc::finalize() { return Service::finalize(); } -std::tuple>, std::vector, podio::Frame> IOSvc::next() { +std::tuple, std::vector, podio::Frame> IOSvc::next() { podio::Frame frame; { std::scoped_lock lock(m_changeBufferLock); if (m_nextEntry < m_entries) { frame = podio::Frame(m_reader->readEvent(m_nextEntry)); } else { - return std::make_tuple(std::vector>(), std::vector(), - std::move(frame)); + return std::make_tuple(std::vector(), std::vector(), std::move(frame)); } m_nextEntry++; if (m_collectionNames.empty()) { @@ -134,11 +133,11 @@ std::tuple>, std::vector> collections; + std::vector collections; for (const auto& name : m_collectionNames) { auto ptr = const_cast(frame.get(name)); - collections.push_back(std::shared_ptr(ptr)); + collections.push_back(ptr); } return std::make_tuple(collections, m_collectionNames, std::move(frame)); diff --git a/k4FWCore/components/IOSvc.h b/k4FWCore/components/IOSvc.h index 2d1957d3..c34e42a2 100644 --- a/k4FWCore/components/IOSvc.h +++ b/k4FWCore/components/IOSvc.h @@ -43,7 +43,7 @@ class IOSvc : public extends { StatusCode initialize() override; StatusCode finalize() override; - std::tuple>, std::vector, podio::Frame> next() + std::tuple, std::vector, podio::Frame> next() override; std::shared_ptr> getCollectionNames() const override { diff --git a/k4FWCore/components/Reader.cpp b/k4FWCore/components/Reader.cpp index 6e5511a6..f78628ca 100644 --- a/k4FWCore/components/Reader.cpp +++ b/k4FWCore/components/Reader.cpp @@ -16,12 +16,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include #include "Gaudi/Functional/details.h" #include "Gaudi/Functional/utilities.h" #include "GaudiKernel/AnyDataWrapper.h" #include "GaudiKernel/FunctionalFilterDecision.h" #include "GaudiKernel/IDataProviderSvc.h" +#include "GaudiKernel/SmartIF.h" #include "GaudiKernel/StatusCode.h" #include "podio/CollectionBase.h" @@ -32,18 +32,16 @@ #include -template using vector_of_ = std::vector; - class CollectionPusher : public Gaudi::Functional::details::BaseClass_t { using Traits_ = Gaudi::Functional::Traits::useDefaults; - using Out = std::shared_ptr; + using Out = std::unique_ptr; using base_class = Gaudi::Functional::details::BaseClass_t; static_assert(std::is_base_of_v, "BaseClass must inherit from Algorithm"); template using OutputHandle_t = Gaudi::Functional::details::OutputHandle_t>; - std::vector>> m_outputs; - Gaudi::Property> m_inputCollections{ + std::vector> m_outputs; + Gaudi::Property> m_inputCollections{ this, "InputCollections", {"First collection"}, "List of input collections"}; // Gaudi::Property m_input{this, "Input", "Event", "Input file"}; @@ -73,7 +71,7 @@ class CollectionPusher : public Gaudi::Functional::details::BaseClass_t>>(out); + auto outColls = std::get>(out); auto outputLocations = std::get>(out); // if (out.size() != m_outputs.size()) { @@ -82,7 +80,7 @@ class CollectionPusher : public Gaudi::Functional::details::BaseClass_tname(), StatusCode::FAILURE); // } for (size_t i = 0; i != outColls.size(); ++i) { - m_outputs[i].put(std::move(outColls[i])); + m_outputs[i].put(std::unique_ptr(outColls[i])); } return Gaudi::Functional::FilterDecision::PASSED; } catch (GaudiException& e) { @@ -91,7 +89,7 @@ class CollectionPusher : public Gaudi::Functional::details::BaseClass_t, std::vector> operator()() const = 0; + virtual std::tuple, std::vector> operator()() const = 0; private: ServiceHandle m_dataSvc{this, "EventDataSvc", "EventDataSvc"}; @@ -108,7 +106,7 @@ class Reader final : public CollectionPusher { // Gaudi doesn't run the destructor of the Services so we have to // manually ask for the reader to be deleted so it will call finish() // See https://gitlab.cern.ch/gaudi/Gaudi/-/issues/169 - ~Reader() { iosvc->deleteReader(); } + ~Reader() override { iosvc->deleteReader(); } ServiceHandle iosvc{this, "IOSvc", "IOSvc"}; @@ -131,8 +129,7 @@ class Reader final : public CollectionPusher { // The IOSvc takes care of reading and passing the data // By convention the Frame is pushed to the store // so that it's deleted at the right time - std::tuple>, std::vector> operator()() - const override { + std::tuple, std::vector> operator()() const override { auto val = iosvc->next(); auto eds = eventSvc().as(); diff --git a/k4FWCore/components/Writer.cpp b/k4FWCore/components/Writer.cpp index 25c9874c..83b6d57e 100644 --- a/k4FWCore/components/Writer.cpp +++ b/k4FWCore/components/Writer.cpp @@ -185,18 +185,22 @@ class Writer final : public Gaudi::Functional::ConsumerretrieveObject("/Event" + k4FWCore::frameLocation, p); - AnyDataWrapper* ptr; + DataObject* p; + StatusCode code = m_dataSvc->retrieveObject("/Event" + k4FWCore::frameLocation, p); + std::unique_ptr> ptr; // This is the case when we are reading from a file + // Since we unregistered the object, we need to delete it if (code.isSuccess()) { - m_dataSvc->unregisterObject(p).ignore(); - ptr = dynamic_cast*>(p); + auto sc = m_dataSvc->unregisterObject(p); + if (!sc.isSuccess()) { + error() << "Failed to unregister object" << endmsg; + return; + } + ptr = std::unique_ptr>(dynamic_cast*>(p)); } // This is the case when no reading is being done - // Will be deleted by the store else { - ptr = new AnyDataWrapper(podio::Frame()); + ptr = std::make_unique>(podio::Frame()); } const auto& frameCollections = ptr->getData().getAvailableCollections(); @@ -242,7 +246,7 @@ class Writer final : public Gaudi::Functional::Consumer>*>(storeCollection); + const auto collection = dynamic_cast>*>(storeCollection); if (!collection) { // Check the case when the data has been produced using the old DataHandle const auto old_collection = dynamic_cast(storeCollection); @@ -256,7 +260,7 @@ class Writer final : public Gaudi::Functional::Consumer uptr(collection->getData().get()); + std::unique_ptr uptr(std::move(collection->getData())); ptr->getData().put(std::move(uptr), coll); } } diff --git a/k4FWCore/include/k4FWCore/Consumer.h b/k4FWCore/include/k4FWCore/Consumer.h index 8077645e..4744a322 100644 --- a/k4FWCore/include/k4FWCore/Consumer.h +++ b/k4FWCore/include/k4FWCore/Consumer.h @@ -19,9 +19,9 @@ #ifndef FWCORE_CONSUMER_H #define FWCORE_CONSUMER_H -#include #include "Gaudi/Functional/details.h" #include "Gaudi/Functional/utilities.h" +#include "GaudiKernel/FunctionalFilterDecision.h" // #include "GaudiKernel/CommonMessaging.h" @@ -43,14 +43,13 @@ namespace k4FWCore { : Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_> { using Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>::DataHandleMixin; - static_assert(((std::is_base_of_v || isVectorLike_v)&&...), + static_assert(((std::is_base_of_v || isVectorLike_v) && ...), "Consumer input types must be EDM4hep collections or vectors of collection pointers"); - template - using InputHandle_t = Gaudi::Functional::details::InputHandle_t>; + template using InputHandle_t = Gaudi::Functional::details::InputHandle_t; - std::tuple::type>>...> m_inputs; - std::array>, sizeof...(In)> m_inputLocations{}; + std::tuple::type>>...> m_inputs; + std::array>, sizeof...(In)> m_inputLocations{}; using base_class = Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>; @@ -66,9 +65,9 @@ namespace k4FWCore { m_inputLocations{Gaudi::Property>{ this, std::get(inputs).first, to_DataObjID(std::get(inputs).second), [this](Gaudi::Details::PropertyBase&) { - std::vector::type>> handles; + std::vector> handles; for (auto& value : this->m_inputLocations[I].value()) { - auto handle = InputHandle_t::type>(value, this); + auto handle = InputHandle_t(value, this); handles.push_back(std::move(handle)); } std::get(m_inputs) = std::move(handles); diff --git a/k4FWCore/include/k4FWCore/DataHandle.h b/k4FWCore/include/k4FWCore/DataHandle.h index 8d5e8689..92b48c4f 100644 --- a/k4FWCore/include/k4FWCore/DataHandle.h +++ b/k4FWCore/include/k4FWCore/DataHandle.h @@ -27,8 +27,6 @@ #include "edm4hep/Constants.h" -#include "TTree.h" - #include #include @@ -133,9 +131,9 @@ template const T* DataHandle::get() { return reinterpret_cast(tmp->collectionBase()); } else { // When a functional has pushed a std::shared_ptr into the store - auto ptr = static_cast>*>(dataObjectp)->getData(); + auto ptr = static_cast>*>(dataObjectp); if (ptr) { - return reinterpret_cast(ptr.get()); + return static_cast(ptr->getData().get()); } std::string errorMsg("The type provided for " + DataObjectHandle>::pythonRepr() + " is different from the one of the object in the store."); diff --git a/k4FWCore/include/k4FWCore/DataWrapper.h b/k4FWCore/include/k4FWCore/DataWrapper.h index 5bfc22f9..005f411d 100644 --- a/k4FWCore/include/k4FWCore/DataWrapper.h +++ b/k4FWCore/include/k4FWCore/DataWrapper.h @@ -41,7 +41,7 @@ template class GAUDI_API DataWrapper : public DataWrapperBase { template friend class DataHandle; public: - DataWrapper() : m_data(nullptr){}; + DataWrapper() : m_data(nullptr) {}; DataWrapper(T&& coll) { m_data = new T(std::move(coll)); is_owner = true; diff --git a/k4FWCore/include/k4FWCore/FunctionalUtils.h b/k4FWCore/include/k4FWCore/FunctionalUtils.h index 003aad28..c795d6c7 100644 --- a/k4FWCore/include/k4FWCore/FunctionalUtils.h +++ b/k4FWCore/include/k4FWCore/FunctionalUtils.h @@ -42,38 +42,34 @@ namespace k4FWCore { namespace details { - // This function will be used to modify std::shared_ptr to the actual collection type - template - requires std::same_as>> - auto maybeTransformToEDM4hep(P& arg) { - return arg; - } + // It doesn't need to be a template but this allows parameter pack expansion + template struct EventStoreType { + using type = std::unique_ptr; + }; + using EventStoreType_t = typename EventStoreType::type; + template - requires(!std::is_same_v>) + requires(!std::is_same_v) const auto& maybeTransformToEDM4hep(const P& arg) { return arg; } template - requires std::is_base_of_v + requires(std::is_base_of_v && !std::same_as) const auto& maybeTransformToEDM4hep(P* arg) { return *arg; } template - requires std::same_as> - const auto& maybeTransformToEDM4hep(P arg) { + requires std::same_as + const auto& maybeTransformToEDM4hep(P&& arg) { return static_cast(*arg); } template > using addPtrIfColl = std::conditional_t, T>; - template const auto& transformtoEDM4hep(const std::shared_ptr& arg) { - return static_cast(*arg); - } - // Check if the type is a vector like type, where vector is the special // type to have an arbitrary number of collections as input or output: // std::vector where Coll is the collection type for output @@ -82,7 +78,7 @@ namespace k4FWCore { template requires std::is_base_of_v> || - std::is_same_v, std::remove_cvref_t> + std::is_same_v> struct isVectorLike> : std::true_type {}; template @@ -91,28 +87,16 @@ namespace k4FWCore { template inline constexpr bool isVectorLike_v = isVectorLike::value; - // transformType function to transform the types from the ones that the user wants - // like edm4hep::MCParticleCollection, to the ones that are actually stored in the - // event store, like std::shared_ptr - // For std::map, th - template struct transformType { - using type = T; - }; - - template - requires std::is_base_of_v || isVectorLike_v - struct transformType { - using type = std::shared_ptr; - }; - - template auto convertToSharedPtr(T&& arg) { - return std::shared_ptr(std::make_shared(std::move(arg))); - } - - template - requires std::is_same_v> - auto convertToSharedPtr(T&& arg) { - return std::move(arg); + template auto convertToUniquePtr(T&& arg) { + // This is the case for CollectionMerger.cpp, where a raw pointer is + // returned from the algorithm + if constexpr (std::same_as) { + return std::unique_ptr(std::forward(arg)); + // Most common case, when an algorithm returns a collection and + // we want to store a unique_ptr + } else { + return std::make_unique(std::forward(arg)); + } } template struct filter_evtcontext_tt { @@ -146,18 +130,18 @@ namespace k4FWCore { std::remove_pointer_t>::value_type>; auto inputMap = std::vector(); for (auto& handle : std::get(handles)) { - if constexpr (std::is_same_v>) { + if constexpr (std::is_same_v) { inputMap.push_back(&get(handle, thisClass, Gaudi::Hive::currentContext())); } else { - auto in = get(handle, thisClass, Gaudi::Hive::currentContext()); - inputMap.push_back(static_cast(in.get())); + podio::CollectionBase* in = handle.get()->get(); + inputMap.push_back(static_cast(in)); } } std::get(inputTuple) = std::move(inputMap); } else { try { - auto in = get(std::get(handles)[0], thisClass, Gaudi::Hive::currentContext()); - std::get(inputTuple) = static_cast>*>(in.get()); + podio::CollectionBase* in = std::get(handles)[0].get()->get(); + std::get(inputTuple) = static_cast>*>(in); } catch (GaudiException& e) { // When the type of the collection is different from the one requested, this can happen because // 1. a mistake was made in the input types of a functional algorithm @@ -201,12 +185,12 @@ namespace k4FWCore { throw GaudiException(thisClass->name(), msg, StatusCode::FAILURE); } for (auto& val : std::get(handles)) { - Gaudi::Functional::details::put(std::get(m_outputs)[i], convertToSharedPtr(std::move(val))); + Gaudi::Functional::details::put(std::get(m_outputs)[i], convertToUniquePtr(std::move(val))); i++; } } else { Gaudi::Functional::details::put(std::get(m_outputs)[0], - convertToSharedPtr(std::move(std::get(handles)))); + convertToUniquePtr(std::move(std::get(handles)))); } // Recursive call for the next index @@ -248,8 +232,8 @@ namespace k4FWCore { throw GaudiException("Cannot retrieve \'" + this->objKey() + "\' from transient store.", this->m_owner ? this->owner()->name() : "no owner", StatusCode::FAILURE); } - auto ptr = dynamic_cast>*>(dataObj); - return maybeTransformToEDM4hep(ptr->getData()); + auto ptr = dynamic_cast>*>(dataObj); + return maybeTransformToEDM4hep(ptr->getData().get()); } struct BaseClass_t { diff --git a/k4FWCore/include/k4FWCore/Transformer.h b/k4FWCore/include/k4FWCore/Transformer.h index 2bbde54c..bd61e2a3 100644 --- a/k4FWCore/include/k4FWCore/Transformer.h +++ b/k4FWCore/include/k4FWCore/Transformer.h @@ -43,10 +43,10 @@ namespace k4FWCore { : Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_> { using Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>::DataHandleMixin; - static_assert(((std::is_base_of_v || isVectorLike_v)&&...), + static_assert(((std::is_base_of_v || isVectorLike_v) && ...), "Transformer and Producer input types must be EDM4hep collections or vectors of collections"); static_assert((std::is_base_of_v || isVectorLike_v || - std::is_same_v, Out>), + std::is_same_v), "Transformer and Producer output types must be EDM4hep collections or vectors of collections"); template @@ -54,10 +54,10 @@ namespace k4FWCore { template using OutputHandle_t = Gaudi::Functional::details::OutputHandle_t>; - std::tuple::type>>...> m_inputs; - std::tuple::type>>> m_outputs; - std::array>, sizeof...(In)> m_inputLocations{}; - Gaudi::Property> m_outputLocations{}; + std::tuple::type>>...> m_inputs; + std::tuple>> m_outputs; + std::array>, sizeof...(In)> m_inputLocations{}; + Gaudi::Property> m_outputLocations{}; using base_class = Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>; @@ -75,9 +75,9 @@ namespace k4FWCore { m_inputLocations{Gaudi::Property>{ this, std::get(inputs).first, to_DataObjID(std::get(inputs).second), [this](Gaudi::Details::PropertyBase&) { - std::vector::type>> h; + std::vector> h; for (auto& value : this->m_inputLocations[I].value()) { - auto handle = InputHandle_t::type>(value, this); + auto handle = InputHandle_t(value, this); h.push_back(std::move(handle)); } std::get(m_inputs) = std::move(h); @@ -87,12 +87,12 @@ namespace k4FWCore { m_outputLocations{Gaudi::Property>{ this, std::get(outputs).first, to_DataObjID(std::get(outputs).second), [this](Gaudi::Details::PropertyBase&) { - std::vector::type>> h; + std::vector> h; for (auto& inpID : this->m_outputLocations.value()) { if (inpID.key().empty()) { continue; } - auto handle = OutputHandle_t::type>(inpID, this); + auto handle = OutputHandle_t::type>(inpID, this); h.push_back(std::move(handle)); } std::get<0>(m_outputs) = std::move(h); @@ -117,7 +117,7 @@ namespace k4FWCore { } else { Gaudi::Functional::details::put( std::get<0>(this->m_outputs)[0], - convertToSharedPtr(std::move(filter_evtcontext_tt::apply(*this, ctx, this->m_inputs)))); + convertToUniquePtr(std::move(filter_evtcontext_tt::apply(*this, ctx, this->m_inputs)))); } return Gaudi::Functional::FilterDecision::PASSED; } catch (GaudiException& e) { @@ -153,7 +153,6 @@ namespace k4FWCore { /** * @brief Get the output locations for a given output index - * @param i The index of the output * @return A range of the output locations */ auto outputLocations() const { @@ -193,10 +192,10 @@ namespace k4FWCore { template using OutputHandle_t = Gaudi::Functional::details::OutputHandle_t>; - std::tuple::type>>...> m_inputs; - std::tuple::type>>...> m_outputs; - std::array>, sizeof...(In)> m_inputLocations{}; - std::array>, sizeof...(Out)> m_outputLocations{}; + std::tuple::type>>...> m_inputs; + std::tuple::type>>...> m_outputs; + std::array>, sizeof...(In)> m_inputLocations{}; + std::array>, sizeof...(Out)> m_outputLocations{}; using base_class = Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>; @@ -209,9 +208,9 @@ namespace k4FWCore { m_inputLocations{Gaudi::Property>{ this, std::get(inputs).first, to_DataObjID(std::get(inputs).second), [this](Gaudi::Details::PropertyBase&) { - std::vector::type>> h; + std::vector> h; for (auto& value : this->m_inputLocations[I].value()) { - auto handle = InputHandle_t::type>(value, this); + auto handle = InputHandle_t(value, this); h.push_back(std::move(handle)); } std::get(m_inputs) = std::move(h); @@ -220,7 +219,7 @@ namespace k4FWCore { m_outputLocations{Gaudi::Property>{ this, std::get(outputs).first, to_DataObjID(std::get(outputs).second), [this](Gaudi::Details::PropertyBase&) { - std::vector::type>> h; + std::vector::type>> h; // Is this needed? // std::sort(this->m_outputLocations[J].value().begin(), this->m_outputLocations[J].value().end(), // [](const DataObjID& a, const DataObjID& b) { return a.key() < b.key(); }); @@ -228,7 +227,7 @@ namespace k4FWCore { if (inpID.key().empty()) { continue; } - auto handle = OutputHandle_t::type>(inpID, this); + auto handle = OutputHandle_t::type>(inpID, this); h.push_back(std::move(handle)); } std::get(m_outputs) = std::move(h);