Date: Thu, 20 Jan 2022 11:46:36 -0500
> On Jan 20, 2022, at 11:24 AM, David Rector <davrec_at_[hidden]> wrote:
>
>
>
>> On Jan 20, 2022, at 10:28 AM, Daveed Vandevoorde <daveed_at_[hidden] <mailto:daveed_at_[hidden]>> wrote:
>>
>>
>>
>>> On Jan 20, 2022, at 8:05 AM, David Rector <davrec_at_[hidden] <mailto:davrec_at_[hidden]>> wrote:
>>>
>>>
[…]
>>> Re namespaces, does the fact that the number of members can increase between reflections require further specification?
>>
>> Well we don’t currently propose such a facility (mostly because we haven’t found a compelling use case and it might cause some surprises).
>> If we did, I’d expect that the answer to your “still error?” questions below would be “yes”.
>>
>> Daveed
>
>
> I’m a little confused — are you are saying it is an error to declare a namespace after having reflected it — i.e. the second `namespace foo {…}` is illegal? Or is the use of the subscript operator the problem?
Let me re-quote your example for context, and I’ll add some annotations to identify lines
```
namespace foo {
... // 10 members
}
constexpr auto fooMemsA = members_of(^foo);
fooMemsA[15]; //error (1)
namespace foo {
... // 10 more members
}
fooMemsA[15]; //still error? (2)
constexpr auto fooMemsB = members_of(^foo);
fooMemsB[15]; //okay (3)
fooMemsA[15]; //still error? (4)
```
(This is all assuming we allow members_of for namespaces, which would mean metaprograms might have to deal with collections of thousands or even tens of thousands of reflections.)
So in line (1) we just go a span with 10 elements but are trying to get the 16th: That’s obviously an error.
In line (2), nothing has changed: It’s the same span.
In line (3), we just obtained a span with 20 elements, and it’s okay to get the 16th.
In line (4), fooMemsA still hasn’t changed and so we still cannot access an element it doesn’t have.
>
> If the latter the example should actually have been written fooMemsA/B = members_of(^foo).data(), i.e. fooMemsA/B is an `info*`, so span's operator[] def isn’t called to check that the index is in bounds; instead only the constant evaluation implementation is responsible for checking if the result of the pointer addition invoked by fooMemsA[15] is in bounds.
Oh, I see what you mean now. You’re asking: Can we enlarge the storage underlying the span after creating the span?
Did I understand that correctly?
AFAIK, the answer must be “no”.
>
> So the real question is, what an implementation is required to do to ensure persistent memory storage (in decreasing order of memory usage, which as you note might be substantial for namespaces):
> 1. allocate a new array for every distinct reflection of a namespace’s members or
> 2. allocate a new array if and only if a) this is the first reflection of those members or b) new members have been added since the last reflection of its members, or
> 3. allocate a new vector of members the first time a namespace is reflected, then add to it as more namespaces are declared, moving the underlying array around as the capacity is exceeded, and (assuming we don’t want to invalidate old pointers) use an additional layer of indirection during constant evaluation of these pointers to ensure that multiple calls to members_of(^foo).data() are always "equal", i.e. point to the most recent allocation, even though multiple calls to members_of(^foo).size() may be different.
> 4. same as #3, but only add new members to the internal vector of namespace members upon a subsequent reflection.
>
> #1: "still error?" is an error both times
> #2: ""
> #3: first "still error?" is an error, second is not
> #4: neither are errors
If we enabled the feature, I would expect #1 or #2 (likely the latter).
#3 and #4 would make all of constant-evaluation pay to make it possible. I don’t think that’s acceptable.
Daveed
>
>
>
>> On Jan 20, 2022, at 10:28 AM, Daveed Vandevoorde <daveed_at_[hidden] <mailto:daveed_at_[hidden]>> wrote:
>>
>>
>>
>>> On Jan 20, 2022, at 8:05 AM, David Rector <davrec_at_[hidden] <mailto:davrec_at_[hidden]>> wrote:
>>>
>>>
[…]
>>> Re namespaces, does the fact that the number of members can increase between reflections require further specification?
>>
>> Well we don’t currently propose such a facility (mostly because we haven’t found a compelling use case and it might cause some surprises).
>> If we did, I’d expect that the answer to your “still error?” questions below would be “yes”.
>>
>> Daveed
>
>
> I’m a little confused — are you are saying it is an error to declare a namespace after having reflected it — i.e. the second `namespace foo {…}` is illegal? Or is the use of the subscript operator the problem?
Let me re-quote your example for context, and I’ll add some annotations to identify lines
```
namespace foo {
... // 10 members
}
constexpr auto fooMemsA = members_of(^foo);
fooMemsA[15]; //error (1)
namespace foo {
... // 10 more members
}
fooMemsA[15]; //still error? (2)
constexpr auto fooMemsB = members_of(^foo);
fooMemsB[15]; //okay (3)
fooMemsA[15]; //still error? (4)
```
(This is all assuming we allow members_of for namespaces, which would mean metaprograms might have to deal with collections of thousands or even tens of thousands of reflections.)
So in line (1) we just go a span with 10 elements but are trying to get the 16th: That’s obviously an error.
In line (2), nothing has changed: It’s the same span.
In line (3), we just obtained a span with 20 elements, and it’s okay to get the 16th.
In line (4), fooMemsA still hasn’t changed and so we still cannot access an element it doesn’t have.
>
> If the latter the example should actually have been written fooMemsA/B = members_of(^foo).data(), i.e. fooMemsA/B is an `info*`, so span's operator[] def isn’t called to check that the index is in bounds; instead only the constant evaluation implementation is responsible for checking if the result of the pointer addition invoked by fooMemsA[15] is in bounds.
Oh, I see what you mean now. You’re asking: Can we enlarge the storage underlying the span after creating the span?
Did I understand that correctly?
AFAIK, the answer must be “no”.
>
> So the real question is, what an implementation is required to do to ensure persistent memory storage (in decreasing order of memory usage, which as you note might be substantial for namespaces):
> 1. allocate a new array for every distinct reflection of a namespace’s members or
> 2. allocate a new array if and only if a) this is the first reflection of those members or b) new members have been added since the last reflection of its members, or
> 3. allocate a new vector of members the first time a namespace is reflected, then add to it as more namespaces are declared, moving the underlying array around as the capacity is exceeded, and (assuming we don’t want to invalidate old pointers) use an additional layer of indirection during constant evaluation of these pointers to ensure that multiple calls to members_of(^foo).data() are always "equal", i.e. point to the most recent allocation, even though multiple calls to members_of(^foo).size() may be different.
> 4. same as #3, but only add new members to the internal vector of namespace members upon a subsequent reflection.
>
> #1: "still error?" is an error both times
> #2: ""
> #3: first "still error?" is an error, second is not
> #4: neither are errors
If we enabled the feature, I would expect #1 or #2 (likely the latter).
#3 and #4 would make all of constant-evaluation pay to make it possible. I don’t think that’s acceptable.
Daveed
Received on 2022-01-20 16:46:39