Skip to content
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

Fix Issue 16745 - Add template helper for creating static arrays with the size inferred #4936

Closed
wants to merge 1 commit into from

Conversation

John-Colvin
Copy link
Contributor

No description provided.

@dlang-bot
Copy link
Contributor

dlang-bot commented Dec 8, 2016

Thanks for your pull request, @John-Colvin! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.

Some tips to help speed things up:

  • smaller, focused PRs are easier to review than big ones

  • try not to mix up refactoring or style changes with bug fixes or feature enhancements

  • provide helpful commit messages explaining the rationale behind each change

Bear in mind that large or tricky changes may require multiple rounds of review and revision.

Please see CONTRIBUTING.md for more information.

Bugzilla references

Auto-close Bugzilla Description
16745 Add template helper for creating static arrays with the size inferred

@wilzbach
Copy link
Member

wilzbach commented Dec 8, 2016

This has been tried before: #4090, see especially Andrei's closing comment

@jmdavis
Copy link
Member

jmdavis commented Dec 8, 2016

I don't know why you're using emplaceRef. I believe that it makes it so that this can't be @nogc, and

T[n] staticArray(T, size_t n)(auto ref T[n] arr)
{
    return arr;
}

should be equivalent. However, regardless of that, I'm pretty sure that your solution doesn't work with VRP thanks to https://issues.dlang.org/show_bug.cgi?id=16779. So, something like

auto sa = staticArray!ubyte([1, 2, 3, 4]);

wouldn't work, whereas

ubyte[4] sa = [1, 2, 3, 4];

does. Also, there's the question of whether it works with runtime variables with the equivalent of

int i = 7;
int[4] arr = [1, 2, 3, i];

which I think that what you have does do, though you're not testing for it.

I think that any solution for this should be @nogc, work with VRP, and allow runtime variables, because you can do all those things when you initialize the static array directly with a known size. Based on a recent D.Learn discussion on the topic, the best solution seem to be

template staticArray(T, E...)
{
    pragma(inline, true) T[E.length] staticArray() @property { return [E]; }
}
template staticArray(E...)
{
    pragma(inline, true) typeof([E][0])[E.length] staticArray() @property { return [E]; }
}

which works with VRP properly, unlike what you have right now. It doesn't have a range overload like what you have now, but that's easily fixed. The bigger problem would be that it doesn't work with what you're proposing with dynamic arrays (which is something that you can do with direct initialization and therefore really should work). And it's not going to work with dynamic arrays without an overload that takes the dynamic array as a function argument rather than a template argument. That being the case,

T[n] staticArray(T, size_t n)(auto ref T[n] arr)
{
    return arr;
}

would be a better solution so long as https://issues.dlang.org/show_bug.cgi?id=16779 gets fixed.

@jmdavis
Copy link
Member

jmdavis commented Dec 8, 2016

This has been tried before: #4090, see especially Andrei's closing comment

So, is his big concern then that if you do something like

int[] arr = staticArray([1, 2, 3, 4]);

you have memory problems? I think that a fix for https://issues.dlang.org/show_bug.cgi?id=12625 would fix that problem, because slicing a static array that's an rvalue should always be an error. As such, I don't think that it's worth worrying about as far as adding a function like this to Phobos goes.

@John-Colvin
Copy link
Contributor Author

I don't know why you're using emplaceRef. I believe that it makes it so that this can't be @nogc, and

T[n] staticArray(T, size_t n)(auto ref T[n] arr)
{
   return arr;
}

should be equivalent.

I am using it because in that overload the user can specify a TargetElemT so we need to place each element in to a new type, just like std.array.array does for handling qualified types. I have added a new test that shows it is @nogc.

However, regardless of that, I'm pretty sure that your solution doesn't work with VRP thanks to https://issues.dlang.org/show_bug.cgi?id=16779. So, something like

auto sa = staticArray!ubyte([1, 2, 3, 4]);

wouldn't work, whereas

ubyte[4] sa = [1, 2, 3, 4];

does

Correct, that's a pain. Maybe it will get fixed soon?

Also, there's the question of whether it works with runtime variables with the equivalent of

int i = 7;
int[4] arr = [1, 2, 3, i];

which I think that what you have does do, though you're not testing for it.

Added a test for that.

I've been experimenting with different designs, but I keep being thwarted by https://issues.dlang.org/show_bug.cgi?id=16957. Seeing as some of the errors in there disappear when not inferring static array length, perhaps it's also linked to vrp problem.

@9il 9il added @andralex Approval from Andrei is required Needs Decision labels Dec 8, 2016
@jmdavis
Copy link
Member

jmdavis commented Dec 8, 2016

I am using it because in that overload the user can specify a TargetElemT so we need to place each element in to a new type, just like std.array.array does for handling qualified types.

Hmmm. Well, that seems like it adds a fair bit of extra overhead for a case that does not work with normal static array initialization. The

int[] arr = [1, 2, 3, 4];
const(int)[2] sa = arr[1 .. $ - 1];

case works and would work with staticArray without emplaceRef. It's just

int[] arr = [1, 2, 3, 4];
long[2] sa = arr[1 .. $ - 1];

that wouldn't. I'm inclined to argue that we should be trying to get staticArray to function as much like normal static array initialization as possible save for the fact that it infers the length, and choosing a solution with additional overhead to support other uses cases doesn't seem like a good idea to me.

@wilzbach
Copy link
Member

I'm inclined to argue that we should be trying to get staticArray to function as much like normal static array initialization as possible save for the fact that it infers the length, and choosing a solution with additional overhead to support other uses cases doesn't seem like a good idea to me.

I absolutely agree here. I think the most common use case is just this:

[1, 2, 3].asStatic

(for which btw the name asStatic would play very nicely with UFCS).

would be a better solution so long as https://issues.dlang.org/show_bug.cgi?id=16779 gets fixed.

So what's the consensus here?

A) Use this PR
B) Wait until 16779 gets fixed for full dynamic array support
C) Don't allow the user to pass in a custom type without length (as it's restricted by 16779)

@MetaLang
Copy link
Member

MetaLang commented Aug 6, 2017

@John-Colvin since this has been open since December of last year without getting merged, and since Andrei closed the previous PR, I'm going to close this one as well. @andralex pleas re-open if you've changed your mind.

@andralex
Copy link
Member

andralex commented Aug 7, 2017

OK, I wasn't being passive-aggressive here, this just slipped. I'll add a few comments within.

@andralex andralex reopened this Aug 7, 2017
@John-Colvin
Copy link
Contributor Author

Ok, no problem. I personally don't normally care much about waiting, although of course "good for the project" and all that...

I've rebased to fix the conflicts

Copy link
Member

@andralex andralex left a comment

Choose a reason for hiding this comment

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

staticArray with a range (including an array) seems to be sufficient. The documentation should dedicate a paragraph to the fact that the result is an rvalue, therefore uses like [1, 2, 3].staticArray.find(x) may be inefficient.

auto sa4 = staticArray!r;
static assert(sa4.length == 3);
assert(sa4 == [0, 1, 2]);
}
Copy link
Member

Choose a reason for hiding this comment

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

This strikes me as an obscure feature, and the example doesn't add confidence in the usefulness of the approach. I'd think a CTFE range evaluation should work, i.e. the example above should spell like:

auto sa4 = iota(3).staticArray;
assert(is(typeof(sa4) == int[3]));
assert(sa4 == [0, 1, 2]);

Copy link
Member

Choose a reason for hiding this comment

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

* Optionally, a target type can be provided as the first template argument, which will result
* in a static array with that element type.
*/
T[N] staticArray(size_t N, T)(T[N] a)
Copy link
Member

Choose a reason for hiding this comment

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

perhaps you could add auto ref to the parameter


/// ditto
TargetElemT[N] staticArray(TargetElemT, size_t N, T)(T[N] a)
{
Copy link
Member

Choose a reason for hiding this comment

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

add constraint if (is(T : TargetElemT))

Copy link
Member

Choose a reason for hiding this comment

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

and possibly auto ref to the parameter


import std.conv : emplaceRef;
foreach (i; 0 .. N)
emplaceRef!TargetElemT(result[i], a[i]);
Copy link
Member

Choose a reason for hiding this comment

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

If any of these throws, the destructors of the already-constructed elements is not called.

foreach (i; 0 .. N)
emplaceRef!TargetElemT(result[i], a[i]);

return (() @trusted => cast(TargetElemT[N])result)();
Copy link
Member

Choose a reason for hiding this comment

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

I'm not clear why this should be always trusted.

///
@safe nothrow unittest
{
auto sa = staticArray([1, 2]);
Copy link
Member

Choose a reason for hiding this comment

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

Examples should use the preferred notation [1, 2].staticArray.

@andralex andralex removed @andralex Approval from Andrei is required Needs Decision labels Aug 7, 2017
Copy link
Member

@andralex andralex left a comment

Choose a reason for hiding this comment

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

To summarize, the changes I'm suggesting are:

  • Delete the overload taking an alias
  • Add an overload that takes a range and is meant to be evaluated during compilation
  • Add documentation explaining the tradeoffs of the rvalue return

@wilzbach
Copy link
Member

@timotheecour has revived this to DRuntime: dlang/druntime#2093

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants