Scope Containers
bitwise
bitwise.pvt at gmail.com
Mon Mar 11 19:40:06 UTC 2019
On Monday, 11 March 2019 at 18:29:23 UTC, Atila Neves wrote:
> On Monday, 11 March 2019 at 17:00:35 UTC, bitwise wrote:
>> On Sunday, 10 March 2019 at 18:46:14 UTC, Atila Neves wrote:
>>> On Sunday, 10 March 2019 at 17:36:09 UTC, bitwise wrote:
>>>> On Sunday, 10 March 2019 at 16:10:10 UTC, Atila Neves wrote:
>>>>> On Sunday, 10 March 2019 at 15:12:03 UTC, bitwise wrote:
>>>>>> [...]
>>>>>
>>>>> Like `const`, `scope` on a member function means that the
>>>>> `this` reference is `scope`.
>>>>
>>>> Interesting. I'll have to look up some examples.
>>>>
>>>>>> [...]
>>>>>
>>>>> https://github.com/atilaneves/automem/blob/2a97acba94d6fe0bf9ba07fec99e86e46aa0f2a1/source/automem/vector.d#L134
>>>>> https://github.com/atilaneves/automem/blob/2a97acba94d6fe0bf9ba07fec99e86e46aa0f2a1/source/automem/vector.d#L159
>>>>> https://github.com/atilaneves/automem/blob/2a97acba94d6fe0bf9ba07fec99e86e46aa0f2a1/source/automem/vector.d#L145
>>>>>
>>>>> static assert(isInputRange!(Vector!int));
>>>>>
>>>>>> [...]
>>>>>
>>>>> I would think so, especially since I wrote Vector to be
>>>>> @safe with dip1000. Look at this unit test for not being
>>>>> able to escape the payload for example:
>>>>>
>>>>> https://github.com/atilaneves/automem/blob/master/tests/ut/vector.d#L229
>>>>
>>>> Ok, but this container copies the entire collection in
>>>> postblit:
>>>> https://github.com/atilaneves/automem/blob/2a97acba94d6fe0bf9ba07fec99e86e46aa0f2a1/source/automem/vector.d#L104
>>>
>>> Well, yes. As it says in the module ddoc, it's my version of
>>> C++'s std::vector. If you don't want it to copy, pass by ref.
>>>
>>>> So if you try to treat it like a range, you'll take a
>>>> performance hit every time you trigger the postblit, which
>>>> seems likely.
>>>
>>> You can pass it by ref like I mention above, or you could
>>> slice it and pass the slice instead. Without DIP1000 passing
>>> the slice wouldn't be @safe but with it, it is.
>>>
>>> Again, just like std::vector, which is pretty much always
>>> passed by const ref. But safer and with a default allocator.
>>>
>>>> Isn't it reasonable to assume a range should act like a
>>>> reference to data, rather than a copy of it?
>>>
>>> It depends.
>>
>> This container seems good for short-lived usage.
>
> Why?
>
>> If I wanted to pass a bunch of filenames to a function for
>> processing, I would consider this container Ok, because it
>> would allocate once, then be consumed as a range without
>> having to copy anything.
>
> Or you don't consume it all and slice it (as I mentioned in my
> last post):
>
> @safe unittest {
> scope v = vector(1, 2, 3);
> static void fun(R)(R range) {
> import std.array: array;
> assert(range.array == [1, 2, 3]);
> }
> fun(v[]);
> // wasn't consumed
> assert(v[] == [1, 2, 3]);
> }
>
> Again, it's @safe because the slice is scoped.
>
>> For any kind of persistent storage though, I would consider
>> the performance costs of using this container's range
>> functionality too high.
>
> There aren't any performance costs. You pass a fat pointer just
> like you would with a GC array.
>
>> I do think the idea of making the container itself the range
>> is interesting, but what ruins it for me is the fact that
>> ranges are consumable.
>
> Which is why Vector has both opSlice and copies by default.
> It's only a range itself if the element type is mutable,
> otherwise it doesn't even define popFront.
>
>> So I'm wondering if it would work well to use an integer index
>> for the range implementation
>
> That's what slices are.
At first, I missed the use of scope here:
> auto opSlice(this This)() scope return
My reasoning was that using automem/vector for persistent storage
required it to be iterated over, which couldn't be done without
consuming the container, or copying it. I see now that you can
iterate by returning a scoped slice of the internal data store,
but this could be unsafe too. If you called reserve on the
container, the returned slice could end up dangling if a non-gc
allocator was used, even if the returned slice was scoped.
I think that if opSlice returned a scoped range with a pointer
back to the original container, then it would be easy for the
range object to detect the state of the container, even including
if it had been moved from and left in it's initial state. As long
as the range couldn't leave the stack-frame of the container, I
think this would be totally safe.
More information about the Digitalmars-d
mailing list