-
Notifications
You must be signed in to change notification settings - Fork 11
Ensure implementation is up to spec #55
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
Conversation
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…for [container.rev.reqmts]
|
The only thing left should be [sequence.reqmts] |
jbab
left a comment
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.
Some remarks on constructor.test.cpp. I still need to go through the other files.
| if (std::is_same_v<T, NonTriviallyDefaultConstructible> || | ||
| std::is_same_v<T, NonTrivial>) { | ||
|
|
||
| IV mid_correct; | ||
| for (auto i = 0ul; i < mid_size; ++i) | ||
| mid_correct.emplace_back(); | ||
|
|
||
| EXPECT_EQ(mid, mid_correct); | ||
| } |
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.
When I first read this I had to think a bit what this is for. It is for the the effect "... with n default-inserted elements". This means that the values are value-initialized (see https://en.cppreference.com/w/cpp/named_req/DefaultInsertable). Therefore there is no need to restrict the test to non trivially default constructible types. It should work with all default constructible types (all of the test types are). The test is that each element is equal to T{}.
| if (std::is_same_v<T, NonTriviallyDefaultConstructible> || | |
| std::is_same_v<T, NonTrivial>) { | |
| IV mid_correct; | |
| for (auto i = 0ul; i < mid_size; ++i) | |
| mid_correct.emplace_back(); | |
| EXPECT_EQ(mid, mid_correct); | |
| } | |
| // All elements of mid are T{} | |
| EXPECT_EQ(std::count(mid.begin(), mid.end(), T{}), mid.size()); |
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.
We have this project now set to c++23 -- I'd suggest we simplify this code using ranges algo
EXPECT_EQ(std::ranges::count(mid, T{}), mid.size());
Alternatively std::ranges::all_of would work as well. Comment applies below as well
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.
The problem here is that types other than this two does not have defined value:
e.g.:
// A trivial type
struct Trivial {
int value;
friend constexpr bool operator==(Trivial x, Trivial y) = default;
};
The value is not defined for implicit default constructor. Equality check here is invalid and undefined.
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.
Only NonTriviallyDefaultConstructible and NonTrivial have their member variables defined.
// A type which is not trivially default constructible (and thus not trivial),
// and all other SMFs are trivial.
struct NonTriviallyDefaultConstructible {
int value = 0;
friend constexpr bool operator==(NonTriviallyDefaultConstructible x, NonTriviallyDefaultConstructible y) = default;
};
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.
The problem here is that types other than this two does not have defined value: e.g.:
// A trivial type struct Trivial { int value; friend constexpr bool operator==(Trivial x, Trivial y) = default; };The value is not defined for implicit default constructor. Equality check here is invalid and undefined.
Make it defined is easy and this should be trivial?
struct Trivial {
int value = 0;
bool operator==( const Trivial& ) const = default;
};
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.
Sure, so basically there's no testing this because anything trivial can't do any initialization and so any comparison will be UB -- no matter the technique used to compare.
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.
Exactly
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.
@JeffGarland, @wusatosi you are right, I forgot about my own implementation of the test types, of course we need a condition here.
What bothers me a bit about the current condition
if (std::is_same_v<T, NonTriviallyDefaultConstructible> ||
std::is_same_v<T, NonTrivial>)
is that it mentions the test types directly. We may add to them or remove some of them in the future or rename them, so this becomes brittle.
So I'm suggesting to focus on the type properties which are relevant here. This is obviously
!std::is_trivially_default_constructible_v<T>
But there are also trivially default constructible types which can be value-initialised to a defined value (all zeros). These are the scalar types. So or condition becomes
if (std::is_scalar_v<T> || !std::is_trivially_default_constructible_v<T>) ...
Agree?
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.
This makes much more sense, thank you so much for your input. I will apply your suggestion once I have time.
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.
In addition, if T is an aggregate type, the expression T{} performs aggregate initialization, which means that the member value will be initialized to zero. There are a few types in the test suite that qualify for this.
| TYPED_TEST(Constructors, CopyIter) { | ||
| // template<class InputIterator> | ||
| // constexpr inplace_vector(InputIterator first, InputIterator last); | ||
| // Effects: Constructs an inplace_vector equal to the range [first, last). | ||
| // Complexity: Linear in distance(first, last). | ||
|
|
||
| using IV = TestFixture::IV; | ||
|
|
||
| auto reference = this->unique(); | ||
| IV device(reference.begin(), reference.end()); | ||
|
|
||
| EXPECT_EQ(device, reference); | ||
| } |
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.
What this fails to test is whether the constructor works with an iterator which is InputIterator and nothing else. I.e. single pass, going forward only.
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.
Ah I see, is there any standard library facility to warp an existing iterator to make a single pass forward only iterator?
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.
Not that I'm aware of.
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.
Alright noted.
| } | ||
|
|
||
| { | ||
| auto mid = std::midpoint(0ul, reference.size()); |
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.
0ul is preferably 0uz in c++23 to ensure it's size_t
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.
thanks for the input!
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 ended up reverting this, we would like to eventually aim to support C++20.
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.
ok, but the assumption that 0ul == size_t may not hold everywhere. I realize it's verbose but I think std::size_t(0) would be more portable.
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.
You're right here
This reverts commit 451f0c5. This is because `uz` is a C++23 feature, we will eventually support earlier versions
|
Turns out C++20 support is doable with minimal changes, I will update the docs in another PR but set the checks here for C++20. |
The goal of this PR is to add basic spec tests so the implementation would be minimally useable.
It would be fine to update the implementation itself in this branch.
See spec: https://eel.is/c++draft/inplace.vector
Meant to replace #46 which I accidentally broke.
cc: @jbab