diff --git a/FWCore/Framework/interface/WrapperBaseHandle.h b/FWCore/Framework/interface/WrapperBaseHandle.h new file mode 100644 index 0000000000000..2aca52048b7e4 --- /dev/null +++ b/FWCore/Framework/interface/WrapperBaseHandle.h @@ -0,0 +1,119 @@ +#ifndef FWCore_Framework_interface_WrapperBaseHandle_h +#define FWCore_Framework_interface_WrapperBaseHandle_h +/* + Description: Allows interaction with data in the Event without actually using the C++ class + + Usage: + The Handle allows one to get data back from the edm::Event as an edm::Wrapper + via a polymorphic pointer of type edm::WrapperBase, instead of as the actual C++ class type. + + // make a handle to hold an instance of MyClass + edm::Handle handle(typeid(MyClass)); + event.getByToken(token, handle); + + // handle.product() returns a polymorphic pointer of type edm::WrapperBase to the underlying + // edm::Wrapper + assert(handle.product()->dynamicTypeInfo() == typeid(MyClass)); + edm::Wrapper const* wrapper = dynamic_cast const*>(handle.product()); +*/ + +// c++ include files +#include +#include + +// CMSSW include files +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/Common/interface/WrapperBase.h" +#include "DataFormats/Provenance/interface/ProductID.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Utilities/interface/EDMException.h" +#include "FWCore/Utilities/interface/TypeID.h" + +// forward declarations +namespace edm { + + class Provenance; + + template <> + class Handle { + public: + explicit Handle(std::type_info const& type) : type_(type), product_(nullptr), prov_(nullptr) {} + + explicit Handle(TypeID type) : type_(type), product_(nullptr), prov_(nullptr) { + if (not type_) { + throw Exception(errors::NotFound, "Handle given an invalid type."); + } + } + + Handle(WrapperBase const* product, Provenance const* prov) + : type_(product->dynamicTypeInfo()), product_(product), prov_(prov) { + assert(product_); + assert(prov_); + } + + // Reimplement the interface of HandleBase + + void clear() { + product_ = nullptr; + prov_ = nullptr; + whyFailedFactory_ = nullptr; + } + + bool isValid() const { return nullptr != product_ and nullptr != prov_; } + + bool failedToGet() const { return bool(whyFailedFactory_); } + + Provenance const* provenance() const { return prov_; } + + ProductID id() const { return prov_->productID(); } + + std::shared_ptr whyFailed() const { + if (whyFailedFactory_.get()) { + return whyFailedFactory_->make(); + } + return std::shared_ptr(); + } + + std::shared_ptr const& whyFailedFactory() const { return whyFailedFactory_; } + + explicit operator bool() const { return isValid(); } + + bool operator!() const { return not isValid(); } + + // Reimplement the interface of Handle + + WrapperBase const* product() const { + if (this->failedToGet()) { + whyFailedFactory_->make()->raise(); + } + return product_; + } + + WrapperBase const* operator->() const { return this->product(); } + WrapperBase const& operator*() const { return *(this->product()); } + + // Additional methods + + TypeID const& type() const { return type_; } + + void setWhyFailedFactory(std::shared_ptr const& iWhyFailed) { + whyFailedFactory_ = iWhyFailed; + } + + private: + TypeID type_; + WrapperBase const* product_; + Provenance const* prov_; + std::shared_ptr whyFailedFactory_; + }; + + // Specialize convert_handle for Handle + void convert_handle(BasicHandle&& orig, Handle& result); + + // Specialize the Event's getByToken method to work with a Handle + template <> + bool Event::getByToken(EDGetToken token, Handle& result) const; + +} // namespace edm + +#endif // FWCore_Framework_interface_WrapperBaseHandle_h diff --git a/FWCore/Framework/interface/WrapperBaseOrphanHandle.h b/FWCore/Framework/interface/WrapperBaseOrphanHandle.h new file mode 100644 index 0000000000000..45e4f1930169f --- /dev/null +++ b/FWCore/Framework/interface/WrapperBaseOrphanHandle.h @@ -0,0 +1,86 @@ +#ifndef FWCore_Framework_interface_WrapperBaseOrphanHandle_h +#define FWCore_Framework_interface_WrapperBaseOrphanHandle_h + +// c++ include files +#include +#include + +// CMSSW include files +#include "DataFormats/Common/interface/OrphanHandle.h" +#include "DataFormats/Common/interface/WrapperBase.h" +#include "DataFormats/Provenance/interface/ProductID.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Utilities/interface/TypeID.h" + +// forward declarations +namespace edm { + + template <> + class OrphanHandle { + public: + OrphanHandle() : product_(nullptr), id_() {} + + OrphanHandle(WrapperBase const* prod, ProductID const& id) : product_(prod), id_(id) { assert(product_); } + + // Reimplement the interface of OrphanHandleBase + + void clear() { product_ = nullptr; } + + bool isValid() const { return nullptr != product_; } + + ProductID id() const { return id_; } + + // Reimplement the interface of OrphanHandle + + WrapperBase const* product() const { return product_; } + + WrapperBase const* operator->() const { return this->product(); } + WrapperBase const& operator*() const { return *(this->product()); } + + private: + WrapperBase const* product_; + ProductID id_; + }; + + // specialise Event::putImpl for WrapperBase + template <> + inline OrphanHandle Event::putImpl(EDPutToken::value_type index, std::unique_ptr product) { + // Event::putImpl calls detail::do_post_insert_if_available(prod), + // but that requires access to the concrete type of PROD, which is not + // available here. Eventually the call to post_insert() should be moved to + // be part of the Wrapper constructor. + // For now the call to post_insert() is the responsibility of the caller. + + assert(index < putProducts().size()); + + // move the wrapped product into the event + putProducts()[index] = std::move(product); + + // construct and return a handle to the product + WrapperBase const* prod = putProducts()[index].get(); + ProductID const& prodID = provRecorder_.getProductID(index); + return OrphanHandle(prod, prodID); + } + + // specialise Event::put for WrapperBase + template <> + inline OrphanHandle Event::put(EDPutToken token, std::unique_ptr product) { + if (UNLIKELY(product.get() == nullptr)) { // null pointer is illegal + TypeID typeID(typeid(WrapperBase)); + principal_get_adapter_detail::throwOnPutOfNullProduct("Event", typeID, provRecorder_.productInstanceLabel(token)); + } + std::type_info const& type = product->dynamicTypeInfo(); + if (UNLIKELY(token.isUninitialized())) { + principal_get_adapter_detail::throwOnPutOfUninitializedToken("Event", type); + } + TypeID const& expected = provRecorder_.getTypeIDForPutTokenIndex(token.index()); + if (UNLIKELY(expected != TypeID{type})) { + principal_get_adapter_detail::throwOnPutOfWrongType(type, expected); + } + + return putImpl(token.index(), std::move(product)); + } + +} // namespace edm + +#endif // FWCore_Framework_interface_WrapperBaseOrphanHandle_h diff --git a/FWCore/Framework/src/WrapperBaseHandle.cc b/FWCore/Framework/src/WrapperBaseHandle.cc new file mode 100644 index 0000000000000..70fc247723a27 --- /dev/null +++ b/FWCore/Framework/src/WrapperBaseHandle.cc @@ -0,0 +1,47 @@ +// CMSSW include files +#include "DataFormats/Common/interface/Handle.h" +#include "DataFormats/Common/interface/WrapperBase.h" +#include "DataFormats/Provenance/interface/Provenance.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/WrapperBaseHandle.h" +#include "FWCore/Utilities/interface/EDMException.h" +#include "FWCore/Utilities/interface/TypeDemangler.h" +#include "FWCore/Utilities/interface/TypeID.h" + +namespace edm { + + // Specialize the convert_handle free function for Handle + void convert_handle(BasicHandle&& handle, Handle& result) { + if (handle.failedToGet()) { + result.setWhyFailedFactory(handle.whyFailedFactory()); + return; + } + + WrapperBase const* wrapper = handle.wrapper(); + if (wrapper == nullptr) { + throw Exception(errors::InvalidReference, "NullPointer") << "edm::BasicHandle has null pointer to Wrapper"; + } + + if (TypeID(wrapper->dynamicTypeInfo()) != result.type()) { + throw Exception(errors::LogicError) << "WrapperBase asked for " << typeDemangle(result.type().name()) + << " but was given a " << typeDemangle(wrapper->dynamicTypeInfo().name()); + } + + // Move the handle into result + result = Handle(wrapper, handle.provenance()); + } + + // Specialize the Event::getByToken method for Handle + template <> + bool Event::getByToken(EDGetToken token, Handle& result) const { + result.clear(); + BasicHandle bh = provRecorder_.getByToken_(result.type(), PRODUCT_TYPE, token, moduleCallingContext_); + convert_handle(std::move(bh), result); // throws on conversion error + if (UNLIKELY(result.failedToGet())) { + return false; + } + addToGotBranchIDs(*result.provenance()); + return true; + } + +} // namespace edm diff --git a/FWCore/Integration/plugins/BuildFile.xml b/FWCore/Integration/plugins/BuildFile.xml index 84e979b5a4583..48568c811bb3c 100644 --- a/FWCore/Integration/plugins/BuildFile.xml +++ b/FWCore/Integration/plugins/BuildFile.xml @@ -33,6 +33,15 @@ + + + + + + + + + diff --git a/FWCore/Integration/plugins/WrapperBaseProducer.cc b/FWCore/Integration/plugins/WrapperBaseProducer.cc new file mode 100644 index 0000000000000..7f9ac32e8e7ad --- /dev/null +++ b/FWCore/Integration/plugins/WrapperBaseProducer.cc @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include + +#include "DataFormats/Common/interface/WrapperBase.h" +#include "DataFormats/TestObjects/interface/Thing.h" +#include "DataFormats/TestObjects/interface/ThingWithDoNotSort.h" +#include "DataFormats/TestObjects/interface/ThingWithPostInsert.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/WrapperBaseOrphanHandle.h" +#include "FWCore/Framework/interface/global/EDProducer.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/Utilities/interface/EDPutToken.h" + +// FIXME for do_post_insert_if_available, remove when no longer needed +#include "FWCore/Framework/interface/PrincipalGetAdapter.h" + +namespace edmtest { + + class WrapperBaseProducer : public edm::global::EDProducer<> { + public: + explicit WrapperBaseProducer(edm::ParameterSet const& ps); + + void produce(edm::StreamID sid, edm::Event& event, edm::EventSetup const& es) const override; + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + + private: + edm::EDPutToken intToken_; + edm::EDPutToken floatToken_; + edm::EDPutToken stringToken_; + edm::EDPutToken vectorToken_; + edm::EDPutToken thingToken_; + edm::EDPutToken thingWithPostInsertToken_; + edm::EDPutToken thingWithDoNotSortToken_; + edm::EDPutToken badToken_; + }; + + template + static T& unwrap_as(edm::WrapperBase& wrapper) { + // throws std::bad_cast on conversion error + edm::Wrapper& w = dynamic_cast&>(wrapper); + + // throws an execption if the wrapper is empty + if (w.product() == nullptr) { + throw cms::Exception("LogicError") << "Empty or invalid wrapper"; + } + + // return the content of the wrapper + return w.bareProduct(); + } + + template + static T const& unwrap_as(edm::WrapperBase const& wrapper) { + // throws std::bad_cast on conversion error + edm::Wrapper const& w = dynamic_cast const&>(wrapper); + + // throws an execption if the wrapper is empty + if (w.product() == nullptr) { + throw cms::Exception("LogicError") << "Empty or invalid wrapper"; + } + + // return the content of the wrapper + return *w.product(); + } + + WrapperBaseProducer::WrapperBaseProducer(edm::ParameterSet const& iConfig) + : intToken_(produces(edm::TypeID(typeid(int)))), + floatToken_(produces(edm::TypeID(typeid(float)))), + stringToken_(produces(edm::TypeID(typeid(std::string)))), + vectorToken_(produces(edm::TypeID(typeid(std::vector)))), + thingToken_(produces(edm::TypeID(typeid(Thing)))), + thingWithPostInsertToken_(produces(edm::TypeID(typeid(ThingWithPostInsert)))), + thingWithDoNotSortToken_(produces(edm::TypeID(typeid(ThingWithDoNotSort)))), + badToken_(produces(edm::TypeID(typeid(int)), "bad")) {} + + void WrapperBaseProducer::produce(edm::StreamID sid, edm::Event& event, edm::EventSetup const& es) const { + { + // Check that an int can be put into the event via a WrapperBase. + int value = 42; + // Copy the value into the wrapper + std::unique_ptr product( + std::make_unique>(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product) == value); + // Move the wrapper into the event + auto handle = event.put(intToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle) == value); + } + + { + // Check that a float can be put into the event via a WrapperBase. + float value = 3.14159f; + // Copy the value into the wrapper + std::unique_ptr product(new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product) == value); + // Move the wrapper into the event + auto handle = event.put(floatToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle) == value); + } + + { + // Check that a string can be put into the event via a WrapperBase. + // With small string optimisation, the underlying buffer should be inside the string object itself. + std::string value = "42"; + // Copy the value into the wrapper + std::unique_ptr product(new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product) == value); + // Move the wrapper into the event + auto handle = event.put(stringToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle) == value); + } + + { + // Check that a vector can be put into the event via a WrapperBase. + // The underlying buffer is outside the vector object itself. + using Vector = std::vector; + Vector value = {1., 1., 2., 3., 5., 8., 11., 19., 30.}; + // Copy the value into the wrapper + std::unique_ptr product(new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product) == value); + // Move the wrapper into the event + auto handle = event.put(vectorToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle) == value); + } + + { + // Check that a Thing can be put into the event via a WrapperBase. + cms_int32_t value = 99; + // Copy the value into the wrapper + std::unique_ptr product(new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product).a == value); + // Move the wrapper into the event + auto handle = event.put(thingToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle).a == value); + } + + { + // Check that a ThingWithPostInsert can be put into the event via a WrapperBase, + // and that ThingWithPostInsert::post_insert() is called by Wrapper. + int32_t value = 2147483647; + // Copy the value into the wrapper + std::unique_ptr product( + new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + assert(not unwrap_as(*product).valid()); + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + // end-of-FIXME + assert(unwrap_as(*product).value() == value); + assert(unwrap_as(*product).valid()); + // Move the wrapper into the event + auto handle = event.put(thingWithPostInsertToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle).value() == value); + assert(unwrap_as(*handle).valid()); + } + + { + // Check that a ThingWithDoNotSort can be put into the event via a WrapperBase, + // and that ThingWithDoNotSort::post_insert() is *not* called by Wrapper. + int32_t value = 2147483647; + // Copy the value into the wrapper + std::unique_ptr product( + new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product).value() == value); + // Move the wrapper into the event + auto handle = event.put(thingWithDoNotSortToken_, std::move(product)); + assert(not product); + assert(handle.isValid()); + assert(unwrap_as(*handle).value() == value); + } + + { + // Check that mismatches in the type of the token and of the underlying object are caught + float value = 3.14159f; + // Copy the value into the wrapper + std::unique_ptr product(new edm::Wrapper(edm::WrapperBase::Emplace{}, value)); + // FIXME Wrapper should call post_insert (if available), but that is not implemented yet + edm::detail::do_post_insert_if_available(unwrap_as(*product)); + assert(unwrap_as(*product) == value); + bool failed = false; + try { + event.put(badToken_, std::move(product)); + } catch (edm::Exception const& e) { + assert(e.categoryCode() == edm::errors::LogicError); + failed = true; + } + assert(failed); + } + } + + void WrapperBaseProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + descriptions.addWithDefaultLabel(desc); + } + +} // namespace edmtest + +#include "FWCore/Framework/interface/MakerMacros.h" +using edmtest::WrapperBaseProducer; +DEFINE_FWK_MODULE(WrapperBaseProducer); diff --git a/FWCore/Integration/test/BuildFile.xml b/FWCore/Integration/test/BuildFile.xml index db95dfadd88f2..083dd3eafc40d 100644 --- a/FWCore/Integration/test/BuildFile.xml +++ b/FWCore/Integration/test/BuildFile.xml @@ -31,6 +31,7 @@ + diff --git a/FWCore/Integration/test/testWrapperBaseProducer_cfg.py b/FWCore/Integration/test/testWrapperBaseProducer_cfg.py new file mode 100644 index 0000000000000..643a3f90452ff --- /dev/null +++ b/FWCore/Integration/test/testWrapperBaseProducer_cfg.py @@ -0,0 +1,39 @@ +import FWCore.ParameterSet.Config as cms + +process = cms.Process("TEST") + +process.source = cms.Source("EmptySource") + +process.maxEvents = cms.untracked.PSet( + input = cms.untracked.int32(10) +) + +process.producer = cms.EDProducer("WrapperBaseProducer") + +process.validateInt = cms.EDAnalyzer("edmtest::GlobalIntAnalyzer", + source = cms.InputTag("producer"), + expected = cms.int32(42) # hard-coded in WrapperBaseProducer +) + +process.validateFloat = cms.EDAnalyzer("edmtest::GlobalFloatAnalyzer", + source = cms.InputTag("producer"), + expected = cms.double(3.14159) # hard-coded in WrapperBaseProducer +) + +process.validateString = cms.EDAnalyzer("edmtest::GlobalStringAnalyzer", + source = cms.InputTag("producer"), + expected = cms.string("42") # hard-coded in WrapperBaseProducer +) + +process.validateVector = cms.EDAnalyzer("edmtest::GlobalVectorAnalyzer", + source = cms.InputTag("producer"), + expected = cms.vdouble(1., 1., 2., 3., 5., 8., 11., 19., 30.) # hard-coded in WrapperBaseProducer +) + +process.path = cms.Path( + process.producer + + process.validateInt + + process.validateFloat + + process.validateString + + process.validateVector +)