-
-
Notifications
You must be signed in to change notification settings - Fork 745
Static array UFCS-style #6489
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
Static array UFCS-style #6489
Conversation
Thanks for your pull request and interest in making D better, @dukc! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + phobos#6489" |
35d43c7
to
8ac17a6
Compare
Circleci crashed. Trying to restart... |
Anybody know how I can fix the Circleci seqfault? |
Unfortunately that's a known issue: https://issues.dlang.org/show_bug.cgi?id=18667 (I just restarted the CI as it only happens in ~ every 20-30 run) |
Oh and you can only restart CircleCi by either
|
91bfbaf
to
8ecb0d2
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.
Not sure about the rangeLength
out parameter. Is there a good use case for it?
std/array.d
Outdated
} | ||
|
||
/// ditto | ||
auto staticArray(size_t n, T)(T a, out size_t rangeLength) |
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.
Do we need rangeLength
? (i.e. is there any good use case?)
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.
Andrei wanted that feature in #6214 review, and because it's so similar I assume the same wish applies here too.
std/array.d
Outdated
return theArray; | ||
}()); | ||
|
||
for (auto iter = a.take(n); !iter.empty; iter.popFront()) |
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.
Why not foreach
?
std/array.d
Outdated
{ | ||
// Compile-time version to avoid unchecked memory access. | ||
Unqual!U[n] ret; | ||
for (auto iter = a.take(n); !iter.empty; iter.popFront()) |
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.
Why not foreach
?
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.
It was a ref foreach in #6178, but Andrei pointed out that it woud not work with rvalue elements, and suggested this. Would foreach without ref make a needless copy with lvalue elements? Not sure.
return (() @trusted => cast(U[n]) ret)(); | ||
} | ||
|
||
auto ret = (() @trusted |
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.
Does this need to be @trusted
?
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 removing @trusted
would prevent the function from being safe if the element type is some pointer. Because they are initialized later, the function should be safe with pointers too.
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 right. Forgot that using void
makes every unsafe. Sorry.
std/array.d
Outdated
|
||
Returns: A static array constructed from `a`. | ||
+/ | ||
|
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.
Nit: we typically don't have an empty line here.
/cc @andralex still wanted a final review on the issue. |
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.
Getting there. Exceptions safety still not present.
std/array.d
Outdated
} | ||
|
||
pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) nothrow @safe pure @nogc | ||
if (!is(T == U) && is(T : U)) |
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.
Do we need the first test? is(T : U)
should be sufficient.
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.
Aye. Tried without, there was ambiquity with the first overload in this pr.
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.
Oh, indeed. I missed the negation on the first is
...
std/array.d
Outdated
|
||
for (auto iter = a.take(n); !iter.empty; iter.popFront()) | ||
{ | ||
emplaceRef!U(ret[i++], iter.front); |
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.
If an exception is thrown, we're toast - uninitialized values will be incorrectly destroyed. See @timotheecour 's PR where I posted code that can be used to fix that.
@andralex exceptions and destructor thing addressed. Good catch btw! I think no-one else noticed. |
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.
one more look pls
std/array.d
Outdated
} | ||
|
||
pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) nothrow @safe pure @nogc | ||
if (!is(T == U) && is(T : U)) |
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.
Oh, indeed. I missed the negation on the first is
...
std/array.d
Outdated
|
||
/// ditto | ||
auto staticArray(Un : U[n], U, size_t n, T)(T a) nothrow | ||
if ((isInputRange!T) && is(ElementType!T : U)) |
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.
redundant parens
std/array.d
Outdated
} | ||
|
||
/// ditto | ||
auto staticArray(Un : U[n], U, size_t n, T)(T a) nothrow |
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.
wait can't this throw if copying objects throws?
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.
Indeed. That's a leftover from Timothee. I think I also need to add a test to prevent errors like this.
std/array.d
Outdated
|
||
/// ditto | ||
auto staticArray(Un : U[n], U, size_t n, T)(T a, out size_t rangeLength) | ||
if ((isInputRange!T) && is(ElementType!T : U)) |
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.
redundant parens
@andralex "Looked" once more. |
Btw the previous version did not compile with elaborate destructors, the default-initializator was incorrectly written. But tests didn't catch that because of the static if. I solved that by putting the same code that default-initializes the rest of the array when the range is too short in |
@wilzbach CircleCI fails to compile but my local compiler and the autotester do not. What is happening? |
The auto-tester doesn't test with -dip1000, only CircleCi does. You can check locally with the std/array.test target |
Understood. I have to look at that later. |
std/array.d
Outdated
return theArray; | ||
}()); | ||
|
||
if (true) |
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.
if (true)
?
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.
It's for the scope(exit) inside it. I could use just a regular block statement but if (true) looks better to the eye IMO. Does the style guide say anything about this?
std/array.d
Outdated
import std.range : take; | ||
import std.conv : emplaceRef; | ||
|
||
size_t i; |
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'd declare these i
s locally next to the loops to make it clearer what the scope is. Stylistic choice, though.
changelog/std-array-asStatic.md
Outdated
template argument to avoid having to specify the number of elements | ||
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`). | ||
|
||
Note: $(D fun([1, 2, 3].staticArray)) may be inefficient because of the copies involved. |
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.
Shouldn't we be able to make sure that RVO happens?
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.
Ask Walter, not me. Because the comment was here already I thought there may be a reason for it and decided to leave it there..
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 would remove the note from the changelog as I think it's clear that in the general case static arrays need to be copied and such details belong into the detailed documentation of the function.
See also: https://godbolt.org/g/GEsh3E
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.
Oh, i didn't notice that static array was being fed to a function. I thought he meant that [1, 2, 3] might be unnecessarily copied when initializing. Sounded unlikely, but I thought that Timothee might know something I do not.
But since it means that feeding a static array to a function involves a copy, I will remove that.
But looking at that godbolt example, why does LDC make bar
to copy the static array when passing it to foo
when it does not use it for anything else? Can't it rely on foo
to copy it if it mutates it?
std/array.d
Outdated
@system nothrow unittest | ||
{ | ||
// exists only to allow doing something in the destructor | ||
static int preventersDestroyed = 0; |
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.
Shouldn't this be compared against the expected value later?
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 originally did, but it was larger than I excepted. That tells me that the value is implementatation-dependant and thus better to not be tested.
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.
However I decided to add assert (false)
after the static array initializer to ensure the copy preventing postblit works, and also added the above rationale to a comment.
std/array.d
Outdated
|
||
nothrow pure @safe unittest | ||
{ | ||
|
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.
Superfluous whitespace.
std/array.d
Outdated
{ | ||
// NOTE: without `cast`, getting error: | ||
// cannot deduce function from argument types !(length)(Result) | ||
return .staticArray!(cast(size_t) a.length)(a); |
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 this be size_t(a.length)
to avoid accidentally introducing bugs?
2d7125f
to
0eedb3c
Compare
Thanks for the comments. All explained and/or fixed. |
ed55a66
to
0fd213f
Compare
@andralex Tests pass and it's ready for another try at review. |
ping @andralex |
changelog/std-array-asStatic.md
Outdated
template argument to avoid having to specify the number of elements | ||
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`). | ||
|
||
Note: $(D fun([1, 2, 3].staticArray)) may be inefficient because of the copies involved. |
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 would remove the note from the changelog as I think it's clear that in the general case static arrays need to be copied and such details belong into the detailed documentation of the function.
See also: https://godbolt.org/g/GEsh3E
std/array.d
Outdated
} | ||
|
||
[cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]); | ||
|
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.
unnecessary empty line.
} | ||
|
||
/// ditto | ||
auto staticArray(size_t n, T)(scope T a, out size_t rangeLength) |
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'm still not sure whether it's a good idea to expose the rangeLength
API if there's no good use case for it. Or did you manage to find one?
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 believe it was wanted for cases like reading input to a fixed-size buffer. So that one can get the length of the input actually in the buffer without using tee
or enumerate
.
See: #6178 (comment) and #6214 (review)
Okay, addressed. |
ping @andralex |
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.
A few final nits.
@dukc would you mind squashing this to one commit afterwards, s.t. we can finally move on?
Unfortunately we missed 2.081, but I really want to see this in 2.082
changelog/std-array-asStatic.md
Outdated
Added `staticArray` to construct a static array from input array / range / CT range. | ||
|
||
The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`) | ||
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`). |
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.
"of" sounds a bit confusing, maybe "will result in an array of type", "results type", "creates type" or "yields"?
changelog/std-array-asStatic.md
Outdated
|
||
The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`) | ||
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`). | ||
When `a` is a range with length not known at compile time, the number of elements must be given as template argument |
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.
s/with/whose/
changelog/std-array-asStatic.md
Outdated
The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`) | ||
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`). | ||
When `a` is a range with length not known at compile time, the number of elements must be given as template argument | ||
(eg `myrange.staticArray!2`). |
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.
s/eg/e.g./
changelog/std-array-asStatic.md
Outdated
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`). | ||
When `a` is a range with length not known at compile time, the number of elements must be given as template argument | ||
(eg `myrange.staticArray!2`). | ||
Size and type can be combined, if source range elements are implicitly |
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.
if the source range elements?
changelog/std-array-asStatic.md
Outdated
convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`). | ||
When the range `a` is known at compile time, it can also be specified as a | ||
template argument to avoid having to specify the number of elements | ||
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`). |
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.
s/eg/e.g./
std/array.d
Outdated
Size and type can be combined (eg: `2.iota.staticArray!(byte[2])`). | ||
When the range `a` is known at compile time, it can also be specified as a | ||
template argument to avoid having to specify the number of elements | ||
(eg: `staticArray!(2.iota)` or $(D staticArray!(double, 2.iota))). |
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.
s/eg/e.g./
std/array.d
Outdated
/++ | ||
Constructs a static array from `a`. | ||
The type of elements can be specified implicitly ($(D [1,2].staticArray) of type `int[2]`) | ||
or explicitly ($(D [1,2].staticArray!float) of type `float[2]`). |
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.
Same comment about "of"
std/array.d
Outdated
Constructs a static array from `a`. | ||
The type of elements can be specified implicitly ($(D [1,2].staticArray) of type `int[2]`) | ||
or explicitly ($(D [1,2].staticArray!float) of type `float[2]`). | ||
When `a` is a range with length not known at compile time, the number of elements must be given as a template argument |
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.
s/with/whose/
std/array.d
Outdated
template argument to avoid having to specify the number of elements | ||
(eg: `staticArray!(2.iota)` or $(D staticArray!(double, 2.iota))). | ||
|
||
Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient. |
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 already agreed on removing this from the changelog. I think the same argumentation applies for the docs too?
@wilzbach Docs corrected. But I don't want to squash, because I don't know how to squash commits by others. I don't want to accidently remove all credit to Timothee! I let you to squash them, if you want. But please do not remove Timothee. |
Okay squashed them down to two commits (i.e. all your commits into one and commits of Timothee into one commit). |
Auto-merge toggled on |
This is #6178 rebooted, so most of this is work of @timotheecour , not mine. It tries to address the last review @andralex made of it, and also some feedback made to #6214, which is my pull request aiming for same functionality.