Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions DataFormats/Common/interface/AnyBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#ifndef DataFormats_Common_interface_AnyBuffer_h
#define DataFormats_Common_interface_AnyBuffer_h

#include <type_traits>
#include <typeinfo>

#include <boost/container/small_vector.hpp>

#include "FWCore/Utilities/interface/EDMException.h"
#include "FWCore/Utilities/interface/TypeDemangler.h"

namespace edm {

class AnyBuffer {
public:
AnyBuffer() = default;

template <typename T>
AnyBuffer(T const& t)
requires(std::is_trivially_copyable_v<T>)
: storage_(reinterpret_cast<std::byte const*>(&t), reinterpret_cast<std::byte const*>(&t) + sizeof(T)),
typeid_(&typeid(std::remove_cv_t<T>)) {}

template <typename T>
T& cast_to()
requires(std::is_trivially_copyable_v<T>)
{
if (empty()) {
throw edm::Exception(edm::errors::LogicError)
<< "Attempt to read an object of type " << edm::typeDemangle(typeid(T).name())
<< " from an empty AnyBuffer";
}
if (typeid(std::remove_cv_t<T>) != *typeid_) {
throw edm::Exception(edm::errors::LogicError)
<< "Attempt to read an object of type " << edm::typeDemangle(typeid(T).name())
<< " from an AnyBuffer holding an object of type " << edm::typeDemangle(typeid_->name());
}
return *reinterpret_cast<T*>(storage_.data());
}

template <typename T>
T const& cast_to() const
requires(std::is_trivially_copyable_v<T>)
{
if (empty()) {
throw edm::Exception(edm::errors::LogicError)
<< "Attempt to read an object of type " << edm::typeDemangle(typeid(T).name())
<< " from an empty AnyBuffer";
}
if (typeid(std::remove_cv_t<T>) != *typeid_) {
throw edm::Exception(edm::errors::LogicError)
<< "Attempt to read an object of type " << edm::typeDemangle(typeid(T).name())
<< " from an AnyBuffer holding an object of type " << edm::typeDemangle(typeid_->name());
}
return *reinterpret_cast<T const*>(storage_.data());
}

bool empty() const { return typeid_ == nullptr; }

std::byte* data() { return storage_.data(); }

std::byte const* data() const { return storage_.data(); }

size_t size_bytes() const { return storage_.size(); }

private:
boost::container::small_vector<std::byte, 32> storage_; // arbitrary small vector size to fit AnyBuffer in 64 bytes
std::type_info const* typeid_ = nullptr;
};

} // namespace edm

#endif // DataFormats_Common_interface_AnyBuffer_h
92 changes: 92 additions & 0 deletions DataFormats/Common/interface/TrivialCopyTraits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef DataFormats_Common_interface_TrivialCopyTraits_h
#define DataFormats_Common_interface_TrivialCopyTraits_h

#include <cassert>
#include <span>
#include <type_traits>
#include <utility>
#include <vector>

namespace edm {

// This struct should be specialised for each type that can be safely memcpy'ed.
//
// The specialisation shall have two static methods
//
// static std::vector<std::span<std::byte>> regions(T& object);
// static std::vector<std::span<const std::byte>> regions(T const& object);
//
// that return a vector of address, size pairs.
// A type that supports this interface can be copied by doing a memcpy of all
// the address, size pairs from a source object to a destination object.
//
//
// A specialisation may implement the type alias Properties to describe the
// properties of an object that can be queried from an existing object via the
// properties() method, and used to initialise a newly allocated copy of the
// object via the initialize() method.
//
// If Properties is void, the properties() method should not be implemented,
// and the initialize() method takes a single argument:
//
// using Properties = void;
// static void initialise(T& object);
//
// If Properties is a concrete type, the properties() method should return an
// instance of Properties, and the initialise() method should take as a second
// parameter a const reference to a Properties object:
//
// using Properties = ...;
// static Properties properties(T const& object);
// static void initialise(T& object, Properties const& args);
//
//
// A specialisation can optionally provide a static method
//
// static void finalize(T& object);
//
// If present, it should be called to restore the object invariants after a
// memcpy operation.

template <typename T>
struct TrivialCopyTraits;

// Specialisation for arithmetic types
template <typename T>
requires std::is_arithmetic_v<T>
struct TrivialCopyTraits<T> {
using value_type = T;

static std::vector<std::span<std::byte>> regions(value_type& object) {
return {{reinterpret_cast<std::byte*>(&object), sizeof(value_type)}};
}

static std::vector<std::span<const std::byte>> regions(value_type const& object) {
return {{reinterpret_cast<std::byte const*>(&object), sizeof(value_type)}};
}
};

// Specialisation for vectors of arithmetic types
template <typename T>
requires(std::is_arithmetic_v<T> and not std::is_same_v<T, bool>)
struct TrivialCopyTraits<std::vector<T>> {
using value_type = std::vector<T>;

using Properties = std::vector<T>::size_type;

static Properties properties(value_type const& object) { return object.size(); }

static void initialize(value_type& object, Properties const& size) { object.resize(size); }

static std::vector<std::span<std::byte>> regions(value_type& object) {
return {{reinterpret_cast<std::byte*>(object.data()), object.size() * sizeof(T)}};
}

static std::vector<std::span<const std::byte>> regions(value_type const& object) {
return {{reinterpret_cast<std::byte const*>(object.data()), object.size() * sizeof(T)}};
}
};

} // namespace edm

#endif // DataFormats_Common_interface_TrivialCopyTraits_h
114 changes: 112 additions & 2 deletions DataFormats/Common/interface/Wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ Wrapper: A template wrapper around EDProducts to hold the product ID.

----------------------------------------------------------------------*/

#include "DataFormats/Common/interface/Uninitialized.h"
#include "DataFormats/Common/interface/CMS_CLASS_VERSION.h"
#include "DataFormats/Common/interface/TrivialCopyTraits.h"
#include "DataFormats/Common/interface/Uninitialized.h"
#include "DataFormats/Common/interface/WrapperBase.h"
#include "DataFormats/Common/interface/WrapperDetail.h"
#include "DataFormats/Provenance/interface/ProductID.h"
#include "FWCore/Utilities/interface/EDMException.h"
#include "FWCore/Utilities/interface/TypeDemangler.h"
#include "FWCore/Utilities/interface/Visibility.h"

#include <algorithm>
#include <cassert>
#include <memory>
#include <span>
#include <string>
#include <typeinfo>
#include <vector>

namespace edm {
template <typename T>
Expand Down Expand Up @@ -59,6 +64,7 @@ namespace edm {
}

bool isPresent_() const override { return present; }
void markAsPresent_() override { present = true; }
std::type_info const& dynamicTypeInfo_() const override { return typeid(T); }
std::type_info const& wrappedTypeInfo_() const override { return typeid(Wrapper<T>); }

Expand All @@ -81,6 +87,14 @@ namespace edm {

std::shared_ptr<soa::TableExaminerBase> tableExaminer_() const override;

bool hasTrivialCopyTraits_() const override;
bool hasTrivialCopyProperties_() const override;
void trivialCopyInitialize_(edm::AnyBuffer const& args) override;
edm::AnyBuffer trivialCopyParameters_() const override;
std::vector<std::span<const std::byte>> trivialCopyRegions_() const override;
std::vector<std::span<std::byte>> trivialCopyRegions_() override;
void trivialCopyFinalize_() override;

private:
T obj;
bool present;
Expand Down Expand Up @@ -176,13 +190,109 @@ namespace edm {
}
};
} // namespace soa

template <typename T>
inline std::shared_ptr<edm::soa::TableExaminerBase> Wrapper<T>::tableExaminer_() const {
return soa::MakeTableExaminer<T>::make(&obj);
}

template <typename T>
inline bool Wrapper<T>::hasTrivialCopyTraits_() const {
if constexpr (requires(T& t) { edm::TrivialCopyTraits<T>::regions(t); }) {
return true;
}
return false;
}

template <typename T>
inline bool Wrapper<T>::hasTrivialCopyProperties_() const {
if constexpr (requires { typename edm::TrivialCopyTraits<T>::Properties; }) {
return true;
}
return false;
}

template <typename T>
void Wrapper<T>::trivialCopyInitialize_([[maybe_unused]] edm::AnyBuffer const& args) {
if (not present) {
throw edm::Exception(edm::errors::LogicError) << "Attempt to access an empty Wrapper";
}
// if edm::TrivialCopyTraits<T>::Properties is not defined, do not call initialize()
if constexpr (not requires { typename edm::TrivialCopyTraits<T>::Properties; }) {
return;
} else
// if edm::TrivialCopyTraits<T>::Properties is void, call initialize() without any additional arguments
if constexpr (std::is_same_v<typename edm::TrivialCopyTraits<T>::Properties, void>) {
edm::TrivialCopyTraits<T>::initialize(obj);
} else
// if edm::TrivialCopyTraits<T>::Properties is not void, cast args to Properties and pass it as an additional argument to initialize()
{
edm::TrivialCopyTraits<T>::initialize(obj, args.cast_to<typename edm::TrivialCopyTraits<T>::Properties>());
}
}

template <typename T>
inline edm::AnyBuffer Wrapper<T>::trivialCopyParameters_() const {
if (not present) {
throw edm::Exception(edm::errors::LogicError) << "Attempt to access an empty Wrapper";
}
// if edm::TrivialCopyTraits<T>::Properties is not defined, do not call properties()
if constexpr (not requires { typename edm::TrivialCopyTraits<T>::Properties; }) {
return {};
} else
// if edm::TrivialCopyTraits<T>::Properties is void, do not call properties()
if constexpr (std::is_same_v<typename edm::TrivialCopyTraits<T>::Properties, void>) {
return {};
} else
// if edm::TrivialCopyTraits<T>::Properties is not void, call properties() and wrap the result in an edm::AnyBuffer
{
typename edm::TrivialCopyTraits<T>::Properties p = edm::TrivialCopyTraits<T>::properties(obj);
return edm::AnyBuffer(p);
}
}

template <typename T>
inline std::vector<std::span<const std::byte>> Wrapper<T>::trivialCopyRegions_() const {
if (not present) {
throw edm::Exception(edm::errors::LogicError) << "Attempt to access an empty Wrapper";
}
if constexpr (requires(T const& t) { edm::TrivialCopyTraits<T>::regions(t); }) {
return edm::TrivialCopyTraits<T>::regions(obj);
} else {
throw edm::Exception(edm::errors::LogicError)
<< "edm::TrivialCopyTraits<T>::regions(const T&) is not defined for type "
<< edm::typeDemangle(typeid(T).name());
return {};
}
}

template <typename T>
inline std::vector<std::span<std::byte>> Wrapper<T>::trivialCopyRegions_() {
if (not present) {
throw edm::Exception(edm::errors::LogicError) << "Attempt to access an empty Wrapper";
}
if constexpr (requires(T& t) { edm::TrivialCopyTraits<T>::regions(t); }) {
return edm::TrivialCopyTraits<T>::regions(obj);
} else {
throw edm::Exception(edm::errors::LogicError)
<< "edm::TrivialCopyTraits<T>::regions(const T&) is not defined for type "
<< edm::typeDemangle(typeid(T).name());
return {};
}
}

template <typename T>
inline void Wrapper<T>::trivialCopyFinalize_() {
if (not present) {
throw edm::Exception(edm::errors::LogicError) << "Attempt to access an empty Wrapper";
}
if constexpr (requires(T& t) { edm::TrivialCopyTraits<T>::finalize(t); }) {
edm::TrivialCopyTraits<T>::finalize(obj);
}
}

} // namespace edm

#include "DataFormats/Common/interface/WrapperView.icc"

#endif
#endif // DataFormats_Common_Wrapper_h
25 changes: 24 additions & 1 deletion DataFormats/Common/interface/WrapperBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ WrapperBase: The base class of all things that will be inserted into the Event.

----------------------------------------------------------------------*/

#include "DataFormats/Common/interface/AnyBuffer.h"
#include "DataFormats/Common/interface/EDProductfwd.h"
#include "DataFormats/Common/interface/FillViewHelperVector.h"
#include "DataFormats/Provenance/interface/ViewTypeChecker.h"

#include <span>
#include <typeinfo>
#include <vector>
#include <memory>
Expand All @@ -28,6 +30,7 @@ namespace edm {
WrapperBase();
~WrapperBase() override;
bool isPresent() const { return isPresent_(); }
void markAsPresent() { markAsPresent_(); }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used by GenericCloner::produce() to mark a newly constructed Wrapper<T> as holding a valid object, before filling that object with memcpy.
Do you see any reasons not to have this method ? Currently I cannot think of any way to avoid it, though.


// We have to use vector<void*> to keep the type information out
// of the WrapperBase class.
Expand All @@ -54,6 +57,15 @@ namespace edm {

std::shared_ptr<soa::TableExaminerBase> tableExaminer() const { return tableExaminer_(); }

bool hasTrivialCopyTraits() const { return hasTrivialCopyTraits_(); }
bool hasTrivialCopyProperties() const { return hasTrivialCopyProperties_(); }

void trivialCopyInitialize(edm::AnyBuffer const& args) { trivialCopyInitialize_(args); }
edm::AnyBuffer trivialCopyParameters() const { return trivialCopyParameters_(); }
std::vector<std::span<const std::byte>> trivialCopyRegions() const { return trivialCopyRegions_(); }
std::vector<std::span<std::byte>> trivialCopyRegions() { return trivialCopyRegions_(); }
void trivialCopyFinalize() { trivialCopyFinalize_(); }

private:
virtual std::type_info const& dynamicTypeInfo_() const = 0;

Expand All @@ -63,6 +75,7 @@ namespace edm {
// For technical ROOT related reasons, we cannot
// declare it = 0.
virtual bool isPresent_() const { return true; }
virtual void markAsPresent_() = 0;

virtual bool isMergeable_() const = 0;
virtual bool mergeProduct_(WrapperBase const* newProduct) = 0;
Expand All @@ -81,6 +94,16 @@ namespace edm {
std::vector<void const*>& oPtr) const = 0;

virtual std::shared_ptr<soa::TableExaminerBase> tableExaminer_() const = 0;

virtual bool hasTrivialCopyTraits_() const = 0;
virtual bool hasTrivialCopyProperties_() const = 0;
virtual void trivialCopyInitialize_(edm::AnyBuffer const& args) = 0;
virtual edm::AnyBuffer trivialCopyParameters_() const = 0;
virtual std::vector<std::span<const std::byte>> trivialCopyRegions_() const = 0;
virtual std::vector<std::span<std::byte>> trivialCopyRegions_() = 0;
virtual void trivialCopyFinalize_() = 0;
};

} // namespace edm
#endif

#endif // DataFormats_Common_WrapperBase_h
1 change: 1 addition & 0 deletions DataFormats/Portable/BuildFile.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<use name="alpaka"/>
<use name="rootcling"/>
<use name="DataFormats/Common"/>
<use name="FWCore/Utilities/" source_only="1"/>
<use name="HeterogeneousCore/AlpakaInterface"/>
Loading