-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Implement a GenericCloner test module based on an edmplugin::PluginFactory
#49152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
8b9e447
787a7c7
2c22926
d232b74
79955cd
94d5ad8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #ifndef DataFormats_Common_interface_AnyBuffer_h | ||
| #define DataFormats_Common_interface_AnyBuffer_h | ||
|
|
||
| #include <type_traits> | ||
| #include <typeinfo> | ||
| #include <cstddef> | ||
|
|
||
| #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 |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please recover the previous formatting in the long comment near the beginning of the file. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| #ifndef Dataformats_Common_interface_TrivialCopyTraits_h | ||
| #define Dataformats_Common_interface_TrivialCopyTraits_h | ||
|
|
||
| #include <cassert> | ||
| #include <span> | ||
| #include <type_traits> | ||
| #include <vector> | ||
| #include <string> | ||
|
|
||
| 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 method properties(), which returns the | ||
| // properties of an existing object, which can be used to initialize a newly | ||
| // allocated copy of the object via the initialize() method. | ||
| // | ||
| // using Properties = ...; | ||
| // static Properties properties(T const& object); | ||
| // | ||
| // If properties() is not implemented, the initialize() method takes a single | ||
| // argument: | ||
| // | ||
| // static void initialize(T& object); | ||
| // | ||
| // If properties() is implemented, the initialize() method should take as a | ||
| // second parameter a const reference to a Properties object: | ||
| // | ||
| // static void initialize(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; | ||
|
|
||
| // Checks if the properties method is defined | ||
| template <typename T> | ||
| concept HasTrivialCopyProperties = requires(T const& object) { TrivialCopyTraits<T>::properties(object); }; | ||
|
|
||
| // Get the return type of properties(...), if it exists. | ||
| template <typename T> | ||
| requires HasTrivialCopyProperties<T> | ||
| using TrivialCopyProperties = decltype(TrivialCopyTraits<T>::properties(std::declval<T const&>())); | ||
|
|
||
| // Checks if the declaration of initialize(...) is consistent with the presence or absence of properties. | ||
| template <typename T> | ||
| concept HasValidInitialize = | ||
| // does not have properties(...) and initialize(object) takes a single argument | ||
| (not HasTrivialCopyProperties<T> && requires(T& object) { TrivialCopyTraits<T>::initialize(object); }) || | ||
| // or does have properties(...) and initialize(object, props) takes two arguments | ||
| (HasTrivialCopyProperties<T> && | ||
| requires(T& object, TrivialCopyProperties<T> props) { TrivialCopyTraits<T>::initialize(object, props); }); | ||
|
|
||
| // Checks for const and non const memory regions | ||
| template <typename T> | ||
| concept HasRegions = requires(T& object, T const& const_object) { | ||
| TrivialCopyTraits<T>::regions(object); | ||
| TrivialCopyTraits<T>::regions(const_object); | ||
| }; | ||
|
|
||
| // Checks if there is a valid specialisation of TrivialCopyTraits for a type T | ||
| template <typename T> | ||
| concept HasTrivialCopyTraits = | ||
| // Has memory regions declared | ||
| (HasRegions<T>) && | ||
| // and has either no initialize(...) or a valid one | ||
| (not requires { &TrivialCopyTraits<T>::initialize; } || HasValidInitialize<T>); | ||
|
|
||
| // Checks if finalize(...) is defined | ||
| template <typename T> | ||
| concept HasTrivialCopyFinalize = requires(T& object) { edm::TrivialCopyTraits<T>::finalize(object); }; | ||
|
|
||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
| // Specialisations for various types | ||
|
|
||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if If you find this concept confusing we can eliminate it, as we don't strictly need it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, in if constexpr (not requires { typename edm::TrivialCopyTraits<T>::Properties; }) {
// if edm::TrivialCopyTraits<T>::Properties is not defined, do not call properties()
return {};The idea of defining this concept was that those checks could be simplified as if constexpr (not HasTrivialCopyProperties<T>) {
// if edm::TrivialCopyTraits<T>::Properties is not defined, do not call properties()
return {};However, I agree with
How about
? This way people won't need to define
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, we also need a way to enforce that
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 |
||
| // Specialisation for arithmetic types | ||
| template <typename T> | ||
| requires std::is_arithmetic_v<T> | ||
| struct TrivialCopyTraits<T> { | ||
| static std::vector<std::span<std::byte>> regions(T& object) { | ||
| return {{reinterpret_cast<std::byte*>(&object), sizeof(T)}}; | ||
| } | ||
|
|
||
| static std::vector<std::span<const std::byte>> regions(T const& object) { | ||
| return {{reinterpret_cast<std::byte const*>(&object), sizeof(T)}}; | ||
| } | ||
| }; | ||
|
|
||
| // Specialisation for std::string | ||
| template <> | ||
| struct TrivialCopyTraits<std::string> { | ||
| using Properties = std::string::size_type; | ||
|
|
||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will remove |
||
| static Properties properties(std::string const& object) { return object.size(); } | ||
| static void initialize(std::string& object, Properties const& size) { object.resize(size); } | ||
|
|
||
| static std::vector<std::span<std::byte>> regions(std::string& object) { | ||
| return {{reinterpret_cast<std::byte*>(object.data()), object.size() * sizeof(char)}}; | ||
| } | ||
|
|
||
| static std::vector<std::span<const std::byte>> regions(std::string const& object) { | ||
| return {{reinterpret_cast<std::byte const*>(object.data()), object.size() * sizeof(char)}}; | ||
| } | ||
| }; | ||
|
|
||
| // 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 Properties = std::vector<T>::size_type; | ||
|
|
||
| static Properties properties(std::vector<T> const& object) { return object.size(); } | ||
| static void initialize(std::vector<T>& object, Properties const& size) { object.resize(size); } | ||
|
|
||
| static std::vector<std::span<std::byte>> regions(std::vector<T>& object) { | ||
| return {{reinterpret_cast<std::byte*>(object.data()), object.size() * sizeof(T)}}; | ||
| } | ||
|
|
||
| static std::vector<std::span<const std::byte>> regions(std::vector<T> const& object) { | ||
| return {{reinterpret_cast<std::byte const*>(object.data()), object.size() * sizeof(T)}}; | ||
| } | ||
| }; | ||
| } // namespace edm | ||
|
|
||
| #endif // Dataformats_Common_interface_TrivialCopyTraits_h | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this file should go under
DataFormats/Common?The interface described by the trait can also be used independently from the serialisation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some unit tests for the traits ?
For example, check that some types (e.g.
intorstd::vector<float>) are properly supported, that some other types (e.g.std::map) are not supported, and that you can write a simple type and specialise the traits for it.