On Tue, Jan 18, 2022 at 1:09 AM Daveed Vandevoorde <firstname.lastname@example.org
I wonder what is the lifetime of underlying storage referenced by std::span returned from such functions as std::meta::members_of? Is this array persisted throughout the compilation process?
That’s a good question (and one of the reasons I was reluctant to make the vector->span change at first).
An implementation is of course free to recompute the array if it needs to. However, if the array “leaks” outside constant-evaluation, it is likely to be made persistent, but doesn’t leak.
I don’t expect span<info> objects to typically “leak”, but the “string_view” objects yes.
Thanks for the response!
I agree that `string_view` could easily "leak", a function `std::string_view to_string(Enum auto e);` is one of such examples,
but I don't see how to use `span<info>` outside of constant-evaluation, because `meta::info` couldn't be used in runtime and `span<info>` is not a structural type and so couldn't be used as NTTP.
Someone might be tempted to write:
constexpr auto mems = members_of(^S);
and then later feed that to some other reflection algorithm, I suppose. Our implementation will then make the underlying array persistent for the compilation.
I either cannot find or don’t have access to the revised paper, so apologies if this was discussed therein, but: can you comment on the choice to return a span instead of a non-random-access range object? (I.e. provide a begin() and an end(), but no size(), operator(size_t), etc.?)
It’s more pleasant to work with and I expect it to be more efficient in practice.
Returning a range would allow implementations to use their existing AST storage, minimizing overhead. Clang stores member declarations as a linked list; to return a span it would have to allocate a declaration’s members additionally as an array somewhere, if those members are reflected.
It’s tempting to provide “direct access” to AST storage, but I don’t think it’s feasible in practice. You need a thin bookkeeping layer in between: At the very least, you have to make the entities involved be things the constant-evaluator can deal with (e.g., APValue objects in Clang).
So if we are going to have to build that thin layer anyway, we might as well make it maximally convenient and performant for the client code.
Returning a range would require an additional `meta::info_iterator` type. The user would need to be able to create their own non-const versions of this of course, dereferencing to non-const meta::infos. No problem there, right?
It’s certainly feasible. But if you’re going to use a node-based representation in the constant-evaluator, that might add to the computational cost of things.
I suppose we could produce “input iterators”, but that has its own downsides.
The main benefit I can see to requiring random-access storage is that it would help in the implementation of template-for expansions and probably other expansions like splices of parameter packs. E.g. `for(constexpr auto mem : members_of(^S))` is implemented as a sequence of implicit instantiations looking like this:
constexpr auto mem = *std::next(0, members_of(^S).begin())
// loop body
constexpr auto mem = *std::next(1, members_of(^S).begin())
// loop body
which, if the underlying storage of members_of(^S) is not random access, requires quadratic instead of linear time.
But these implementation details are hidden; if that is the only reason for returning a span compilers could instead be asked to deal with the problem on their own.
Are there other good reasons to return a span instead of a range? Is the ease of use worth the overhead?
So I think in most cases the “overhead” is worse for the iterator-based model (which the Lock3 implementation currently uses). A potential exception might be requesting the list of members of a large namespace (currently not a proposed capability) knowing that most won’t be traversed. (If you have to traverse a significant fraction of them, I suspect the span approach wins again). Possibly, this might also depend on the compiler’s architecture and that of its cosntant-evaluator.