-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
[RFC] Add tests and rudimentary protections for default-constructed PortableCollections #44844
Changes from all commits
c30702c
7f2113b
b6a555e
e4d2b53
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,70 @@ | ||
#define CATCH_CONFIG_MAIN | ||
#include "catch.hpp" | ||
|
||
#include "DataFormats/Common/interface/DeviceProduct.h" | ||
|
||
namespace { | ||
class Metadata { | ||
public: | ||
Metadata(int v = 0) : value_(v) {} | ||
void synchronize(int& ret) const { ret = value_; } | ||
|
||
private: | ||
int const value_; | ||
}; | ||
|
||
class Product { | ||
public: | ||
Product() = default; | ||
Product(int v) : value_(v) {} | ||
|
||
int value() const { return value_; } | ||
|
||
private: | ||
int value_ = 0; | ||
}; | ||
|
||
class ProductNoDefault { | ||
public: | ||
ProductNoDefault(int v) : value_(v) {} | ||
|
||
int value() const { return value_; } | ||
|
||
private: | ||
int value_; | ||
}; | ||
|
||
} // namespace | ||
|
||
TEST_CASE("DeviceProduct", "[DeviceProduct]") { | ||
SECTION("Default constructor") { [[maybe_unused]] edm::DeviceProduct<int> prod; } | ||
|
||
SECTION("Default-constructible data product") { | ||
SECTION("Default constructed") { | ||
edm::DeviceProduct<Product> prod(std::make_shared<Metadata>(3)); | ||
|
||
int md = 0; | ||
auto const& data = prod.getSynchronized<Metadata>(md); | ||
REQUIRE(md == 3); | ||
REQUIRE(data.value() == 0); | ||
} | ||
|
||
SECTION("Explicitly constructed") { | ||
edm::DeviceProduct<Product> prod(std::make_shared<Metadata>(4), 42); | ||
|
||
int md = 0; | ||
auto const& data = prod.getSynchronized<Metadata>(md); | ||
REQUIRE(md == 4); | ||
REQUIRE(data.value() == 42); | ||
} | ||
} | ||
|
||
SECTION("Non-default-constructible data product") { | ||
edm::DeviceProduct<ProductNoDefault> prod(std::make_shared<Metadata>(5), 314); | ||
|
||
int md = 0; | ||
auto const& data = prod.getSynchronized<Metadata>(md); | ||
REQUIRE(md == 5); | ||
REQUIRE(data.value() == 314); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -12,6 +12,9 @@ | |||||||||||||||||||||
#include "DataFormats/Portable/interface/PortableCollectionCommon.h" | ||||||||||||||||||||||
|
||||||||||||||||||||||
// generic SoA-based product in host memory | ||||||||||||||||||||||
// | ||||||||||||||||||||||
// Moving from PortableHostCollection leaves the source object in a | ||||||||||||||||||||||
// "not isValid()" state that has 0 size | ||||||||||||||||||||||
template <typename T> | ||||||||||||||||||||||
class PortableHostCollection { | ||||||||||||||||||||||
public: | ||||||||||||||||||||||
|
@@ -47,27 +50,86 @@ class PortableHostCollection { | |||||||||||||||||||||
PortableHostCollection& operator=(PortableHostCollection const&) = delete; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// movable | ||||||||||||||||||||||
PortableHostCollection(PortableHostCollection&&) = default; | ||||||||||||||||||||||
PortableHostCollection& operator=(PortableHostCollection&&) = default; | ||||||||||||||||||||||
PortableHostCollection(PortableHostCollection&& other): | ||||||||||||||||||||||
buffer_(std::move(other.buffer_)), | ||||||||||||||||||||||
layout_(std::move(other.layout_)), | ||||||||||||||||||||||
view_(std::move(other.view_)) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
other.buffer_.reset(); | ||||||||||||||||||||||
other.view_ = View(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
PortableHostCollection& operator=(PortableHostCollection&& other) { | ||||||||||||||||||||||
// Protection for self-assignment without if-statements, following | ||||||||||||||||||||||
// what was suggested in the C++ core guidelines | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
auto tmp = std::move(other.buffer_); | ||||||||||||||||||||||
other.buffer_.reset(); | ||||||||||||||||||||||
buffer_.reset(); | ||||||||||||||||||||||
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. Is this 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. Maybe ? 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'd think (now) it would not be necessary, and I was just following the pattern in an example https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-move-self (that uses raw pointers where the buffer_ = std::move(tmp); to work equally well in both cases of self-assignment and assignment from a different object. Any existing value in |
||||||||||||||||||||||
buffer_ = std::move(tmp); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
auto tmp = std::move(other.view_); | ||||||||||||||||||||||
other.view_ = View(); | ||||||||||||||||||||||
view_ = View(); | ||||||||||||||||||||||
view_ = std::move(tmp); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+70
to
+75
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. it should (to be verified) safe to do just
Suggested change
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, what about the 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.
This is because
"Whoops", I guess, thanks for catching. Then I need to think a test that would demonstrate it failing. |
||||||||||||||||||||||
return *this; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// default destructor | ||||||||||||||||||||||
~PortableHostCollection() = default; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// has a valid buffer? | ||||||||||||||||||||||
bool isValid() const { return buffer_.has_value(); } | ||||||||||||||||||||||
|
||||||||||||||||||||||
// a way to access the number of elements without the buffer validity assertion | ||||||||||||||||||||||
auto size() const { return view_.metadata().size(); } | ||||||||||||||||||||||
|
||||||||||||||||||||||
// access the View | ||||||||||||||||||||||
View& view() { return view_; } | ||||||||||||||||||||||
ConstView const& view() const { return view_; } | ||||||||||||||||||||||
ConstView const& const_view() const { return view_; } | ||||||||||||||||||||||
View& view() { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
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. Alternatively the View accessors could be left unchecked, and require the users to explicitly check (we could also throw an exception instead of |
||||||||||||||||||||||
ConstView const& view() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
ConstView const& const_view() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
View& operator*() { return view_; } | ||||||||||||||||||||||
ConstView const& operator*() const { return view_; } | ||||||||||||||||||||||
View& operator*() { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
ConstView const& operator*() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
View* operator->() { return &view_; } | ||||||||||||||||||||||
ConstView const* operator->() const { return &view_; } | ||||||||||||||||||||||
View* operator->() { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return &view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
ConstView const* operator->() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return &view_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// access the Buffer | ||||||||||||||||||||||
Buffer buffer() { return *buffer_; } | ||||||||||||||||||||||
ConstBuffer buffer() const { return *buffer_; } | ||||||||||||||||||||||
ConstBuffer const_buffer() const { return *buffer_; } | ||||||||||||||||||||||
Buffer buffer() { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return *buffer_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
ConstBuffer buffer() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return *buffer_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
ConstBuffer const_buffer() const { | ||||||||||||||||||||||
assert(isValid()); | ||||||||||||||||||||||
return *buffer_; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// part of the ROOT read streamer | ||||||||||||||||||||||
static void ROOTReadStreamer(PortableHostCollection* newObj, Layout& layout) { | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#define CATCH_CONFIG_MAIN | ||
#include <catch.hpp> |
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.
... and I just learned something new 🤷🏻
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.
Just curious, what was that?