Request for commens: Simplistic fixed_string based static reflection

162 views
Skip to first unread message

Ricardo Andrade

unread,
Apr 11, 2016, 3:12:38 AM4/11/16
to SG 7 - Reflection
I'm aware of the existing reflection proposals and the opinion of the standard committee about them, but I'm not totally satisfied with the solutions presented so far.
So, I'm interested in hearing if the following idea is feasible and if anyone feels interested in exploring more of it:

Given fixed_string: a compile time string (such as the one in N4121)

- Declaration name operators:
  - declname(identifier) => fixed_string
    - take types: yes, declname(type) => fixed_string
  - namedecl(fixed_string) => identifier
    - take user-defined fixed_string as input: yes
    - take keywords as input: why not?
- Declaration scope operator (obtain all the declared names in the given scope):
  - declscope(class/enum/namespace) => fixed_string[]
  - filtering: could be done with the resulting array but maybe a second argument for declscope(scope, what) or more operators...
  - accessibility: same as declared, breaking encapsulation is out of scope, requires diffent mechanism
- attributes, templates/concepts, function/template argument names, declaration specifiers: still need some thinking

Use cases:
- pointers: &namedecl(fixed_string), &T::namedecl(fixed_string)
- references: auto& r = namedecl(fixed_string) - being 'name' an object
- functions: namedecl(fixed_string)(value)
- type: decltype(namedecl(fixed_string))
- members: T::namedecl(fixed_string), t.namedecl(name)
- enums: namedecl(fixed_string) => value, array_size(declanames(fixed_string)) => entries
- scope: namedecl(scope)::namedecl(member), namedecl(object).namedecl(member)
- compilation error: namespace(name) if "name" does not exist in the current scope

One last thing:
- overloads: overload_set(identifier) => typelist of the signatures (but what if one of them is a template function?)

Questions/suggestion are welcome.

Thank you,
Ricardo Andrade

Axel Naumann

unread,
Apr 11, 2016, 3:17:53 AM4/11/16
to refle...@isocpp.org
Hi Ricardo,

On 11/04/16 09:12, Ricardo Andrade wrote:
> I'm aware of the existing reflection proposals and the opinion of the
> standard committee about them, but I'm not totally satisfied with the
> solutions presented so far.

Could you outline what your lack of satisfaction is about?

Cheers, Axel.
> --
> You received this message because you are subscribed to the Google
> Groups "SG 7 - Reflection" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to reflection+...@isocpp.org
> <mailto:reflection+...@isocpp.org>.
> To post to this group, send email to refle...@isocpp.org
> <mailto:refle...@isocpp.org>.
> Visit this group at
> https://groups.google.com/a/isocpp.org/group/reflection/.
> For more options, visit https://groups.google.com/a/isocpp.org/d/optout.

Matus Chochlik

unread,
Apr 11, 2016, 4:21:05 AM4/11/16
to refle...@isocpp.org
Hi,

On Mon, Apr 11, 2016 at 9:12 AM, Ricardo Andrade <ricardofabi...@gmail.com> wrote:
I'm aware of the existing reflection proposals and the opinion of the standard committee about them, but I'm not totally satisfied with the solutions presented so far.
So, I'm interested in hearing if the following idea is feasible and if anyone feels interested in exploring more of it:

Given fixed_string: a compile time string (such as the one in N4121)

You can do most if not all of these by using `reflexpr` + `get_name` which *will* return a compile time string. If you find the basic interface too verbose for your use-cases then it is only a simple matter of writing (and proposing for standardisation) of several constexpr functions.
 

- Declaration name operators:
  - declname(identifier) => fixed_string

get_name_v<reflexpr(identifier)>;
 
    - take types: yes, declname(type) => fixed_string
  - namedecl(fixed_string) => identifier

This is the only thing which cannot be done at the moment, although it would be nice to have.
 
    - take user-defined fixed_string as input: yes
    - take keywords as input: why not?

get_name_v<reflexpr(static)> (reflexpr(static) will return a MetaSpecifier in the future).
 
- Declaration scope operator (obtain all the declared names in the given scope):
  - declscope(class/enum/namespace) => fixed_string[]

playing with `get_scope` + `get_name`
 
  - filtering: could be done with the resulting array but maybe a second argument for declscope(scope, what) or more operators...

compile-time string manipulation similar to what for example the Metaparse library does.
 
  - accessibility: same as declared, breaking encapsulation is out of scope, requires diffent mechanism

`is_public<Metaobject>` - this will be more fine grained when specifier reflection is introduced.
 
- attributes, templates/concepts, function/template argument names, declaration specifiers: still need some thinking

There will be a MetaTemplate, MetaTemplateParameter, etc.
 

Use cases:
- pointers: &namedecl(fixed_string), &T::namedecl(fixed_string)
- references: auto& r = namedecl(fixed_string) - being 'name' an object
- functions: namedecl(fixed_string)(value)
- type: decltype(namedecl(fixed_string))
- members: T::namedecl(fixed_string), t.namedecl(name)
- enums: namedecl(fixed_string) => value, array_size(declanames(fixed_string)) => entries
- scope: namedecl(scope)::namedecl(member), namedecl(object).namedecl(member)
- compilation error: namespace(name) if "name" does not exist in the current scope

One last thing:
- overloads: overload_set(identifier) => typelist of the signatures (but what if one of them is a template function?)

There will be a MetaOverloaded function which will return a sequence of MetaFunctions some of which may also be MetaTemplates.

If you have a look at N4111 most of the concepts like MetaSpecifier, MetaTemplate, MetaTemplateParameter, MetaOverloadedFunction, MetaFunction, MetaConstructor, MetaOperator, etc. were already proposed, but were (temporarily) removed from the subsequent proposals at the request of some members of the committee. They *will* be reintroduced later.

Matus

Ricardo Fabiano de Andrade

unread,
Apr 11, 2016, 3:59:00 PM4/11/16
to refle...@isocpp.org
Hi Axel,
Please see my comments below.

On Mon, Apr 11, 2016 at 2:17 AM, Axel Naumann <Axel.N...@cern.ch> wrote:
Hi Ricardo,

On 11/04/16 09:12, Ricardo Andrade wrote:
> I'm aware of the existing reflection proposals and the opinion of the
> standard committee about them, but I'm not totally satisfied with the
> solutions presented so far.

Could you outline what your lack of satisfaction is about?

 
Sure.

I'm going to use P0194R0 as a base for most comparisons, as this seems to be the one that got more comments from the committee.
Here are some points of concern (at no specific order of importance):
  • Abuse of the type system to transport information about each and every declaration (i.e. each member is a different MetaObject, thus a different type). How bad would it be to compile Qt if it was purely based on this?
  • Duplication of the language constructs - the "reflection way" of doing the same thing (i.e. decltype(name) -> get_type_t<reflexpr(name)>, &name -> get_pointer_v<reflexpr(name)>).
  • Duplication between the traits found in <type_traits> and the traits on the meta namespace (i.e. meta::is_class_v<MO> vs. std::is_class_v<T>).
  • No simple "type-safe" replacement for stringfying an identifier using the preprocessor (i.e. get_name_v<reflexpr(name)> require much more compiler resources than a simple #name).
  • A reasonably sized library support for static reflection. A future proposal for dynamic reflection would then require another equally sized library.
  • Not intended for beginners. Or if used by beginners extensively, may have adverse side-effects (may take much longer to compile or take much more resources to do it).
With fixed_string based reflection I'm trying to address the above:
  • fixed_string template instances only vary by the length of the string. Using it extensively won't cause the same pressure on the type system as one type per declaration.
  • New language constructs would be added only for reflection facilities. It would rely on all existing constructs for the rest.
  • Depend on the existing type traits. New ones might be added as needed but the goal is to allow using these outside of the context of reflection as well (such as overload_set).
  • The fixed_string is reasonably cheap to obtain from an identifier and it's virtually as good as one obtained via the preprocessor.
  • Ideally not a library solution, just language support (plus traits).
  • Slightly more beginner-friendly. Or at least, aims to be lighter.
At the same, I had in mind some interesting aspects of P0194R0:
  • Dedicated operator (or keyword) makes it easier to find where reflection is being used in the code.
  • Deal equally with typed and non-typed entities (such as namespaces).
  • Obtain everything that could be needed from a given scope (class or namespace) at once.
  • Use of traits to obtain details about a given declaration.
  • In theory, it can be applied recursively from the global scope. 
 
Cheers, Axel.


I had several opportunities to discuss these concerns with the authors of both P0194R0 and P0255R0.
They have their point of view, but they are also defending their own line of thought.

I tend to agree with their ideas as long as there is not a simpler solution.
So, I'm here to be sure about that.

I hope that clarifies my motivation.

Regards,
Ricardo
 

You received this message because you are subscribed to a topic in the Google Groups "SG 7 - Reflection" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/reflection/uksZoUpQaUk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to reflection+...@isocpp.org.
To post to this group, send email to refle...@isocpp.org.

Roland Bock

unread,
Apr 12, 2016, 1:03:00 AM4/12/16
to refle...@isocpp.org
On 2016-04-11 09:12, Ricardo Andrade wrote:
> I'm aware of the existing reflection proposals and the opinion of the
> standard committee about them, but I'm not totally satisfied with the
> solutions presented so far.
> So, I'm interested in hearing if the following idea is feasible and if
> anyone feels interested in exploring more of it:
>
> Given fixed_string: a compile time string (such as the one in N4121)
>
> - Declaration name operators:
> - declname(identifier) => fixed_string
> - take types: yes, declname(type) => fixed_string
> - namedecl(fixed_string) => identifier
> - take user-defined fixed_string as input: yes
> - take keywords as input: why not?

I like your name ideas. Kind of similar to what I had in mind. I just
pushed my current thoughts to github:

https://github.com/rbock/reasoning-about-names

Cheers,

Roland

Matus Chochlik

unread,
Apr 12, 2016, 2:16:58 AM4/12/16
to refle...@isocpp.org
Hi Ricardo,

let me address your concerns more concisely than in our private conversations:

On Mon, Apr 11, 2016 at 9:58 PM, Ricardo Fabiano de Andrade <ricardofabi...@gmail.com> wrote:

[8<]
I'm going to use P0194R0 as a base for most comparisons, as this seems to be the one that got more comments from the committee.
Here are some points of concern (at no specific order of importance):
  • Abuse of the type system to transport information about each and every declaration (i.e. each member is a different MetaObject, thus a different type). How bad would it be to compile Qt if it was purely based on this?

The type system is 'abused' in C++ for many different things, in particular it is nowadays regularly used as a stand-alone *meta-programming language*, where types are used as values. P0194 just follows this pattern and allows to nicely integrate the meta-objects into meta-programs.

Furthermore there is nothing whatsoever preventing a completely lazy implementation so you are paying only for things you ask for and the meta-objects exist purely on the type level (there are no constructors, they cannot be instantiated, etc.)
So the meta-objects are very lightweight.

There were objections from other people to add full reification of things like namespaces, constructors, parameters, etc.
and to doing drastic changes to what can or cannot be a template parameter. Also we want to reason about meta-data at compile-time for many use cases.
So taking this and the well established meta-programming practices into account, representing the meta-objects by using types is IMHO the best choice.
 
  • Duplication of the language constructs - the "reflection way" of doing the same thing (i.e. decltype(name) -> get_type_t<reflexpr(name)>, &name -> get_pointer_v<reflexpr(name)>).

The constructs you listed are *not* the exactly the same things:

decltype(name) -> get_type_t<reflexpr(name)> : not the same at all

&name -> get_pointer_v<reflexpr(name)> : here you are missing an important step, namely that getting the metaobject and then obtaining the address may and will be separated in many cases.

In C++ there are many different ways to do the same things.
For example array access:

int y;
int x[10];

x[5] = y;
5[x] = y;
*(x+5) = y;

Or, is it a problem that we have both a C array and std::array? Etc, etc.
 
  • Duplication between the traits found in <type_traits> and the traits on the meta namespace (i.e. meta::is_class_v<MO> vs. std::is_class_v<T>).
The meta-level traits are there for consistency with the other meta-traits for which there are no equivalents in base-level type traits.
There is no `std::is_namespace_v`, `std::is_constructor_v`, `std::is_specifier_v`, etc. Once the whole reflection facility is completed, there may be several dozens of meta-type-traits. I see no point in breaking their consistency by removing a single or at most a couple of trait(s) for the sake of simplicity.

Also `std::is_class_v` may be *more* verbose and clumsy to use in many cases (again you are missing that the act of reflecting something and inspecting the meta-object's properties can be separated.

 
  • No simple "type-safe" replacement for stringfying an identifier using the preprocessor (i.e. get_name_v<reflexpr(name)> require much more compiler resources than a simple #name).

Preprocessor stringifying is for making *strings* from any PP tokens not just from valid identifiers.
As I said I would love to have the ability to create an identifier from a compile-time string together with some compile-time string manipulation library and something like this was mentioned in N4451 (the `identifier` operator).
But I've been told by the members of the committee to keep the proposal simple and extensible.

At the moment there is nothing in P0194 preventing the implementation of the above in the future. Are you sure that it is possible to implement that with `fixed_string`, where only the size is a compile-time constant?
 
  • A reasonably sized library support for static reflection. A future proposal for dynamic reflection would then require another equally sized library.
Yes, to make things convenient for some simple use cases, there will be additional constexpr functions and templates added to the standard library in the future. We are actively thinking about them, I've spent a big part of the last 10 years writing various reflection facilities from completely static ones to fully dynamic (even dynamically loadable) ones. There are other people who did the same.
What we need now, is to agree on a solid, fundamental compiler support for static reflection covering as many use cases as possible. You can build anything else on top of that later.

 
  • Not intended for beginners. Or if used by beginners extensively, may have adverse side-effects (may take much longer to compile or take much more resources to do it).
See the above. We are working on the fundamentals. Yes, there *will* be a convenience interface for simple (or beginner) use-cases. But you cannot build a simplifying facade if you don't have the layer below finished.

 
With fixed_string based reflection I'm trying to address the above:
  • fixed_string template instances only vary by the length of the string. Using it extensively won't cause the same pressure on the type system as one type per declaration.
I (and I assume that others too) want to have the ability to inspect and manipulate the reflected names *at compile-time*.
Can you do that with `fixed_string` where only the size is known at compile-time (Am I missing something)?

As proposed in P0194, the name can be represented in the compiler as a plain (zero terminated) C array of chars and it's there only when you request it. Is that a big 'pressure' on the compiler?
 
  • New language constructs would be added only for reflection facilities. It would rely on all existing constructs for the rest.
The same thing is proposed in P0194 and its predecessors.
 
  • Depend on the existing type traits. New ones might be added as needed but the goal is to allow using these outside of the context of reflection as well (such as overload_set).
This was addressed above.
 
  • The fixed_string is reasonably cheap to obtain from an identifier and it's virtually as good as one obtained via the preprocessor.

No it is not virtually as good as the preprocessor. You can concatenate strings, etc. but you *cannot* create identifiers at compile time, unless you have a fully static string, which is what P0194 has.
 
  • Ideally not a library solution, just language support (plus traits).

Why not a library solution? I was under the impression that in C++ things that can be done (and are not too cumbersome to do) by a library, should be done by a library.
 
  • Slightly more beginner-friendly. Or at least, aims to be lighter.
See above.

 
At the same, I had in mind some interesting aspects of P0194R0:
  • Dedicated operator (or keyword) makes it easier to find where reflection is being used in the code.
  • Deal equally with typed and non-typed entities (such as namespaces).
  • Obtain everything that could be needed from a given scope (class or namespace) at once.
  • Use of traits to obtain details about a given declaration.
  • In theory, it can be applied recursively from the global scope. 
 
Cheers, Axel.


I had several opportunities to discuss these concerns with the authors of both P0194R0 and P0255R0.
They have their point of view, but they are also defending their own line of thought.

I tend to agree with their ideas as long as there is not a simpler solution.
So, I'm here to be sure about that.

I try hard to make the interface as simple as possible without breaking consistency and without compromising important use cases, but I see no point in simplification just for its own sake.

Best regards,

Matus

Ricardo Fabiano de Andrade

unread,
Apr 13, 2016, 2:02:04 AM4/13/16
to refle...@isocpp.org
Roland,

I had similar goals in mind but having reflection first and then introducing later on the concept of "name" as a language construct by itself.
More people need to evaluate these ideas, but personally I see many benefits on using this approach.
I'm specially interested in seeing any form of this in action in a future sqlpp11. I imagine how much you're looking forward to having such feature.

The only problem I see is that the committee has decided to not move forward with a constexpr literal string operator: http://cplusplus.github.io/EWG/ewg-active.html#66
That's why I suggested fixed_string (or N4121) and new keywords/operators.

But they have also encouraged a revision, which might be the chance to introduce std::name.

I'll have your work in mind if my suggestion becomes a proposal.

Cheers,
Ricardo


You received this message because you are subscribed to a topic in the Google Groups "SG 7 - Reflection" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/reflection/uksZoUpQaUk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to reflection+...@isocpp.org.
To post to this group, send email to refle...@isocpp.org.

Ricardo Fabiano de Andrade

unread,
Apr 13, 2016, 2:57:36 AM4/13/16
to refle...@isocpp.org
Hi Matus,

We've already discussed some these items a while ago but I'm glad you make it clear that your proposal can achieve the same functionality (on its current or future state).
My point with discussion is not presenting something superior in terms what can be done, just a overall simpler way to obtain the same results, and if possible introduce re-usable elements that can be applied outside of the scope of reflection (more traits for example).

On Mon, Apr 11, 2016 at 3:21 AM, Matus Chochlik <choc...@gmail.com> wrote:
Hi,

On Mon, Apr 11, 2016 at 9:12 AM, Ricardo Andrade <ricardofabi...@gmail.com> wrote:
I'm aware of the existing reflection proposals and the opinion of the standard committee about them, but I'm not totally satisfied with the solutions presented so far.
So, I'm interested in hearing if the following idea is feasible and if anyone feels interested in exploring more of it:

Given fixed_string: a compile time string (such as the one in N4121)

You can do most if not all of these by using `reflexpr` + `get_name` which *will* return a compile time string. If you find the basic interface too verbose for your use-cases then it is only a simple matter of writing (and proposing for standardisation) of several constexpr functions.
 

It's not -only- a matter of verbosity, but also how costly it will be to keep adding ad-hoc types for every call to reflexpr.
Even though fixed_string is a not as "smart" as a type, it's sufficient to get done the job of preserving information about a declaration.

Another point is that, for example besides the clear intention of meta::get_pointer, one still needs to learn about it along with the reflection mechanism (what consists in what I call "the reflection way" of doing things).
In the other hand every C++ user already knows about the ampersand operator. They will need only to learn how to use reflection in terms I've presented.
 

- Declaration name operators:
  - declname(identifier) => fixed_string

get_name_v<reflexpr(identifier)>;
 
    - take types: yes, declname(type) => fixed_string
  - namedecl(fixed_string) => identifier

This is the only thing which cannot be done at the moment, although it would be nice to have.
 

Right, the meta-functions described in your proposal make the conversion from a reflected entity to the actual declaration initially unnecessary.
At the expense of requiring an additional meta-function for each operation for which the language itself already has already a construct.
i.e. get_pointer_v<reflexpr(identifier)> vs. &
 
    - take user-defined fixed_string as input: yes
    - take keywords as input: why not?

get_name_v<reflexpr(static)> (reflexpr(static) will return a MetaSpecifier in the future).
 

My concern is that, by following this trend we will end up with one MetaObject instance per word of C++ code.
I'm far from being an expert in compilers, but I don't think they would be able to keep up with that.
Redesigning a compiler because of the requirements for using reflection also does not sound like a good idea.

 
- Declaration scope operator (obtain all the declared names in the given scope):
  - declscope(class/enum/namespace) => fixed_string[]

playing with `get_scope` + `get_name`

Considering your proposal already have a reasonably sized library support in it, a lot of user code would still be needed for achieving the same list of fixed_strings using get_scope+get_name.
And sometimes a list of name it is all I need.
 
 
  - filtering: could be done with the resulting array but maybe a second argument for declscope(scope, what) or more operators...

compile-time string manipulation similar to what for example the Metaparse library does.

My comments about filtering were more related to separating the entities that each fixed_string refers to in groups or categories.
Mainly:
- types (nested, typedefs, using aliases),
- objects (member, variables),
- functions (member, static, constructors),
- templates/concepts, namespaces.
The idea is finding ways to obtain only entity names of one of these groups.
I have no intention to require compile-time string transformations as part of the reflection.
 
 
  - accessibility: same as declared, breaking encapsulation is out of scope, requires diffent mechanism

`is_public<Metaobject>` - this will be more fine grained when specifier reflection is introduced.
 

In my mind specifier reflection should give me the same information which is possible to obtain with meta::is_public in your proposal.
Something like: specifiers(name) => fixed_string[] i.e. "public", "const"

IMO, a way to violate the access defined by the class author should not be implemented by reflection.
In other words, one may want to access a private member without using reflection.
The fact that other languages do it by using reflection does not mean it's the best way of doing it.

I'm more towards something like a "cast" operator:
unrestricted_access(c.m) // break into the private member "m"

Or even a form of inheritance:
class X : friend Y {}; // anything in X is not public in Y
Which is cool, because by using "final" the class authors would prevent it to be done.

 
- attributes, templates/concepts, function/template argument names, declaration specifiers: still need some thinking

There will be a MetaTemplate, MetaTemplateParameter, etc.
 

Along with more pressure on the type system.
 

Use cases:
- pointers: &namedecl(fixed_string), &T::namedecl(fixed_string)
- references: auto& r = namedecl(fixed_string) - being 'name' an object
- functions: namedecl(fixed_string)(value)
- type: decltype(namedecl(fixed_string))
- members: T::namedecl(fixed_string), t.namedecl(name)
- enums: namedecl(fixed_string) => value, array_size(declanames(fixed_string)) => entries
- scope: namedecl(scope)::namedecl(member), namedecl(object).namedecl(member)
- compilation error: namespace(name) if "name" does not exist in the current scope

One last thing:
- overloads: overload_set(identifier) => typelist of the signatures (but what if one of them is a template function?)

There will be a MetaOverloaded function which will return a sequence of MetaFunctions some of which may also be MetaTemplates.

If you have a look at N4111 most of the concepts like MetaSpecifier, MetaTemplate, MetaTemplateParameter, MetaOverloadedFunction, MetaFunction, MetaConstructor, MetaOperator, etc. were already proposed, but were (temporarily) removed from the subsequent proposals at the request of some members of the committee. They *will* be reintroduced later.


My final words are that P0194R0 is a very nice proposal, it has a clear roadmap and honestly, it fits all the use cases I had in mind for reflection if everything your proposed initially gets implemented.
There's no doubt you know your stuff and did a great work on it. Passing through the committee multiple times is a good proof of it.
I'm just not convinced yet it's the best form of reflection we can have in C++.

Cheers,
Ricardo 


Matus

Roland Bock

unread,
Apr 13, 2016, 3:16:49 AM4/13/16
to refle...@isocpp.org
On 2016-04-13 08:02, Ricardo Fabiano de Andrade wrote:
> Roland,
>
> I had similar goals in mind but having reflection first and then
> introducing later on the concept of "name" as a language construct by
> itself.
> More people need to evaluate these ideas, but personally I see many
> benefits on using this approach.
> I'm specially interested in seeing any form of this in action in a
> future sqlpp11. I imagine how much you're looking forward to having such
> feature.

Indeed. sqlpp11 would benefit a lot from being able to reason about
names at compile in a similar way we do with types and values today.

Also, with reflection (analysis) and such abilities (synthesis), we
could definitely create SQL for containers, streams, etc. I have a proof
of concept available in sqlpp11-connector-stl (needs an update to work
with recent versions of sqlpp11 though).


>
> The only problem I see is that the committee has decided to not move
> forward with a constexpr literal string operator:
> http://cplusplus.github.io/EWG/ewg-active.html#66
> That's why I suggested fixed_string (or N4121) and new keywords/operators.
>
> But they have also encouraged a revision, which might be the chance to
> introduce std::name.

std::name was suggested by Richard Smith in a discussion on the Future
Proposals list, trying to avoid new keywords. But it seems I still need
the operator ($ in my current version).


>
> I'll have your work in mind if my suggestion becomes a proposal.

Likewise :-)

Cheers,

Roland
> <mailto:reflection%2Bunsu...@isocpp.org>
> > <mailto:reflection+...@isocpp.org
> <mailto:reflection%2Bunsu...@isocpp.org>>.
> > To post to this group, send email to refle...@isocpp.org <mailto:refle...@isocpp.org>
> > <mailto:refle...@isocpp.org <mailto:refle...@isocpp.org>>.
> > Visit this group at
> > https://groups.google.com/a/isocpp.org/group/reflection/.
> > For more options, visit https://groups.google.com/a/isocpp.org/d/optout.
>
> --
> You received this message because you are subscribed to a topic in
> the Google Groups "SG 7 - Reflection" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/a/isocpp.org/d/topic/reflection/uksZoUpQaUk/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> reflection+...@isocpp.org
> <mailto:reflection%2Bunsu...@isocpp.org>.

Ricardo Fabiano de Andrade

unread,
Apr 14, 2016, 1:36:39 AM4/14/16
to refle...@isocpp.org
Matus,

As always, you have very good arguments, but there's still room for discussion. Here we go.  :)


On Tue, Apr 12, 2016 at 1:16 AM, Matus Chochlik <choc...@gmail.com> wrote:
Hi Ricardo,

let me address your concerns more concisely than in our private conversations:

On Mon, Apr 11, 2016 at 9:58 PM, Ricardo Fabiano de Andrade <ricardofabi...@gmail.com> wrote:

[8<]
I'm going to use P0194R0 as a base for most comparisons, as this seems to be the one that got more comments from the committee.
Here are some points of concern (at no specific order of importance):
  • Abuse of the type system to transport information about each and every declaration (i.e. each member is a different MetaObject, thus a different type). How bad would it be to compile Qt if it was purely based on this?

The type system is 'abused' in C++ for many different things, in particular it is nowadays regularly used as a stand-alone *meta-programming language*, where types are used as values. P0194 just follows this pattern and allows to nicely integrate the meta-objects into meta-programs.


Comparatively to the usual meta-programming, the approach your proposal takes can make that abuse grow "exponentially" because each reflexpr creates a different type per reflected declaration.
 
Furthermore there is nothing whatsoever preventing a completely lazy implementation so you are paying only for things you ask for and the meta-objects exist purely on the type level (there are no constructors, they cannot be instantiated, etc.)
So the meta-objects are very lightweight.


If your proposal could be re-worked to generate one template instance per reflected declaration the solution would get closer to the cost of a string-based solution (well, more a few additional steps to get a string). That may prevent your idea of a lazy implementation to work though.

I agree that with a lazy implementation one would pay only for what is being used. However, the individual cost of each reflection operation seems unnecessarily high.
I feel like you're asking me to pay for a luxury car at a discount when I just need something for commuting. :)

 
There were objections from other people to add full reification of things like namespaces, constructors, parameters, etc.
and to doing drastic changes to what can or cannot be a template parameter.

A constexpr fixed_string can be a template parameter.
 
Also we want to reason about meta-data at compile-time for many use cases.
So taking this and the well established meta-programming practices into account, representing the meta-objects by using types is IMHO the best choice.
 

You can still have the meta-objects implemented over a solution based on fixed_string (plus any needed traits).
Which is a good thing, because one will use the meta-objects only if and when it fits a use-case.

  • Duplication of the language constructs - the "reflection way" of doing the same thing (i.e. decltype(name) -> get_type_t<reflexpr(name)>, &name -> get_pointer_v<reflexpr(name)>).

The constructs you listed are *not* the exactly the same things:

decltype(name) -> get_type_t<reflexpr(name)> : not the same at all


struct X { using Y = int; Y y; };
constexpr bool not_same = std::is_same_v<decltype(X::y), std::meta::get_type_t<reflexpr(X::y)>>; // false? How so?

 
&name -> get_pointer_v<reflexpr(name)> : here you are missing an important step, namely that getting the metaobject and then obtaining the address may and will be separated in many cases.


I haven't missed that:
&name === declname(name) -> fixed_string -> &namedecl(fixed_string)
 
In C++ there are many different ways to do the same things.
For example array access:

int y;
int x[10];

x[5] = y;
5[x] = y;
*(x+5) = y;


This example actually illustrates quite well what I'm saying.
The subscript operator is a convenience added to avoid dealing with pointer arithmetic.

std::meta::get_pointer is way less convenient than the address of operator &.
You can't avoid the reflexpr or similar operator when dealing with the reflection, but I'd be very nice to not avoid having get_pointer and many other meta-traits.

 
Or, is it a problem that we have both a C array and std::array? Etc, etc.
 
 
The problem is that your proposal introduces the equivalent of std::array without the reflection equivalent for the C array in the language.
And it doesn't need to be error-prone as the dealing with the C-arrays...

Like I said before, it seems feasible to have a simpler approach to reflection and then meta-objects could be built on top of it.

  • Duplication between the traits found in <type_traits> and the traits on the meta namespace (i.e. meta::is_class_v<MO> vs. std::is_class_v<T>).
The meta-level traits are there for consistency with the other meta-traits for which there are no equivalents in base-level type traits.
There is no `std::is_namespace_v`, `std::is_constructor_v`, `std::is_specifier_v`, etc. Once the whole reflection facility is completed, there may be several dozens of meta-type-traits. I see no point in breaking their consistency by removing a single or at most a couple of trait(s) for the sake of simplicity.


This consistency is only needed because your proposal aims to be a reflection library not just a language feature.
In the other hand, I'd love to have some of these new traits available outside of the scope of reflection.

For example, a trait for overloads would be immensely useful in meta-programming even without reflection.
Having to use reflexpr, obtain a meta-object, find the desired function somehow, check if it has overloads to only then be able to do something meaningful sounds like a waste of resources.

Of course, if you're doing this whole process for all member functions of a class at once, the meta-object may help.
The current "forced" wholesale approach of your proposal is concerning though.

Also `std::is_class_v` may be *more* verbose and clumsy to use in many cases (again you are missing that the act of reflecting something and inspecting the meta-object's properties can be separated.


Is this clumsy?
constexpr auto name_of_T = declname(T);
constexpr auto is_T_class = std::is_class_v<namedecl(nameOfT)>;
 
 
  • No simple "type-safe" replacement for stringfying an identifier using the preprocessor (i.e. get_name_v<reflexpr(name)> require much more compiler resources than a simple #name).

Preprocessor stringifying is for making *strings* from any PP tokens not just from valid identifiers.

That's why I said a "type-safe" replacement. It would only be abe to stringfy existing declarations.
 
As I said I would love to have the ability to create an identifier from a compile-time string together with some compile-time string manipulation library and something like this was mentioned in N4451 (the `identifier` operator).
But I've been told by the members of the committee to keep the proposal simple and extensible.


A reason for this request may be because it's a reasonably sized proposal even in its most simplified form (the latest iteration).

Which is another motivation for pursue a simpler approach.
If the committee is having a hard time to wrap their heads around the proposal, imagine the average user of the language.
"But reflection is for advanced users". Well, if a steep learning curve is imposed by the its design, that definitely will be true.
A self-fulling prophecy :)

 
At the moment there is nothing in P0194 preventing the implementation of the above in the future. Are you sure that it is possible to implement that with `fixed_string`, where only the size is a compile-time constant?

I personally don't see problems, but I brought the discussion here because I may be missing something.
And I'm expecting that a simpler approach may lose something in the way compared to your proposal. But if that something is only rarely needed and it can be built with few extra lines of code, why not go simpler.
 
 
  • A reasonably sized library support for static reflection. A future proposal for dynamic reflection would then require another equally sized library.
Yes, to make things convenient for some simple use cases, there will be additional constexpr functions and templates added to the standard library in the future. We are actively thinking about them, I've spent a big part of the last 10 years writing various reflection facilities from completely static ones to fully dynamic (even dynamically loadable) ones. There are other people who did the same.
What we need now, is to agree on a solid, fundamental compiler support for static reflection covering as many use cases as possible. You can build anything else on top of that later.


I truly respect your experience. In the other hand, I've spent the last 15 years dealing with real-world solutions that required reflection at different levels or which could have been benefited quite a lot of at least having basic things like a stringfy. I also had many opportunities to use the reflection facilities available in other strongly typed languages such as Java and C#.

I've always hoped that C++ reflection support could be done in way which feels natural to the language, be powerful but at the same does not impose absurd compilation times that prevent it to be used more extensively.
I'm here just working to be sure whichever is the accepted proposal, it gets as close as possible of that.
Your proposal is getting very far but doesn't completely fulfill these expectations.
 
 
  • Not intended for beginners. Or if used by beginners extensively, may have adverse side-effects (may take much longer to compile or take much more resources to do it).
See the above. We are working on the fundamentals. Yes, there *will* be a convenience interface for simple (or beginner) use-cases. But you cannot build a simplifying facade if you don't have the layer below finished.


Magical word: facade. So, there's some complexity to be hidden from the average user.
But why does it need to be complex to begin with? That's my whole point.
 
 
With fixed_string based reflection I'm trying to address the above:
  • fixed_string template instances only vary by the length of the string. Using it extensively won't cause the same pressure on the type system as one type per declaration.
I (and I assume that others too) want to have the ability to inspect and manipulate the reflected names *at compile-time*.
Can you do that with `fixed_string` where only the size is known at compile-time (Am I missing something)?


You can convert a fixed_string (char[size]) to a variadic representation (one-char-per-template-arg), in case you need that to manipulate a string at compile-time.
But I don't think you would need that for basic reflection use-cases. I'd avoid that if possible.

 
As proposed in P0194, the name can be represented in the compiler as a plain (zero terminated) C array of chars and it's there only when you request it. Is that a big 'pressure' on the compiler?

Well, how many steps you went through to generate this string?
Let's see: reflexpr(name) -> MetaObject -> get_name -> string
Compare to: declname(name) -> fixed_string

If it's a class member:
reflexpr(T) -> MetaClass -> get_data_members -> MetaVariable -> iterate -> get_name -> string -> somehow build an array
Compare to: declscope(T) -> fixed_string[]

To be fair, you would have available all the traits for every MetaVariable at this point, while in my solution more work would be required to get them.

But if I was only looking for the names, all those MetaVariables will never be used again, just living unnecessarily on the compiler's symbol table.
In the other hand, you could build a MetaObject using a fixed_string approach:

template <fixed_string Name>
struct MetaObject {
  static constexpr fixed_string name = Name;
  static constexpr bool is_class = std::is_class_v<namedecl(Name)>;
  static constexpr bool has_scope = is_class && ... ;
  ...
};

 
 
  • New language constructs would be added only for reflection facilities. It would rely on all existing constructs for the rest.
The same thing is proposed in P0194 and its predecessors.
 

I disagree. P0194R0 creates its own versions of existing type traits. get_pointer "et al." duplicate built-in language functionality.
What I meant is that only the functionality that does not exist currently in the language would be added and there would be no duplication.
 
  • Depend on the existing type traits. New ones might be added as needed but the goal is to allow using these outside of the context of reflection as well (such as overload_set).
This was addressed above.

The traits introduced by P0194R0 and predecessors depend on reflexpr to work.
They are not standalone even for the use-cases where they could perfectly be used for other reasons other than reflection.
 
 
  • The fixed_string is reasonably cheap to obtain from an identifier and it's virtually as good as one obtained via the preprocessor.

No it is not virtually as good as the preprocessor. You can concatenate strings, etc. but you *cannot* create identifiers at compile time, unless you have a fully static string, which is what P0194 has.
 

What would prevent a constexpr fixed_string to be considered a fully static string? I also don't see what prevents it to be concatenated and transformed during compile-time.
I haven't mentioned the possibility of creating identifiers, but since the basic idea would allow referring to an existing identifier by using a fixed_string, introducing identifiers based on fixed_strings seems a logical next steps to me.
If you have a chance to read Roland Bock's paper on names (link in a previous reply), you will see what I'm talking about.

  • Ideally not a library solution, just language support (plus traits).

Why not a library solution? I was under the impression that in C++ things that can be done (and are not too cumbersome to do) by a library, should be done by a library.

Well, let's use the example of the Math class that exists on other languages. The fact that mathematical functions are somehow related doesn't make a Math class a good abstraction.
Why do I need to have access to trigonometric functions when what I want is only simple things such as "abs" or "pow"?

In the same token, why do I need MetaObjects if the only thing I care is getting the names of the members of my own class internally and related to the members?

Something simple like (just an idea, not an example of implementation):
declscope(decltype(*this)) -> fixed_string names[N]
...
pair(names[i], &namedecl(names[i]))
...

 
  • Slightly more beginner-friendly. Or at least, aims to be lighter.
See above.


Adding helpers or facades on top of the library only makes it bigger, adding more things to learn.
 
 
At the same, I had in mind some interesting aspects of P0194R0:
  • Dedicated operator (or keyword) makes it easier to find where reflection is being used in the code.
  • Deal equally with typed and non-typed entities (such as namespaces).
  • Obtain everything that could be needed from a given scope (class or namespace) at once.
  • Use of traits to obtain details about a given declaration.
  • In theory, it can be applied recursively from the global scope. 
 
Cheers, Axel.


I had several opportunities to discuss these concerns with the authors of both P0194R0 and P0255R0.
They have their point of view, but they are also defending their own line of thought.

I tend to agree with their ideas as long as there is not a simpler solution.
So, I'm here to be sure about that.

I try hard to make the interface as simple as possible without breaking consistency and without compromising important use cases, but I see no point in simplification just for its own sake.


I understand and your effort is much appreciated.
If my ideas don't find any ears, I will be the first in line to try a reflection solution based in your proposal if it becomes part of the standard.
But as you said before, you're dealing with reflection libraries for a good portion of your career and maybe you're missing the point that a library may be not be the ideal or the simpler way to achieve reflection in C++.

And different ideas are always a good thing, right? :)

Cheers,
Ricardo


 
Best regards,

Matus

Matus Chochlik

unread,
Apr 14, 2016, 2:35:47 AM4/14/16
to refle...@isocpp.org


On Wed, Apr 13, 2016 at 8:57 AM, Ricardo Fabiano de Andrade <ricardofabi...@gmail.com> wrote:

Hi Matus,On Mon, Apr 11, 2016 at 9:12 AM, Ricardo Andrade <ricardofabi...@gmail.com> wrote:
I'm aware of the existing reflection proposals and the opinion of the standard committee about them, but I'm not totally satisfied with the solutions presented so far.
So, I'm interested in hearing if the following idea is feasible and if anyone feels interested in exploring more of it:

Given fixed_string: a compile time string (such as the one in N4121)

You can do most if not all of these by using `reflexpr` + `get_name` which *will* return a compile time string. If you find the basic interface too verbose for your use-cases then it is only a simple matter of writing (and proposing for standardisation) of several constexpr functions.
 

It's not -only- a matter of verbosity, but also how costly it will be to keep adding ad-hoc types for every call to reflexpr.
Even though fixed_string is a not as "smart" as a type, it's sufficient to get done the job of preserving information about a declaration.

The cost is whatever is the cost of keeping void type + a pointer to the shared object that the compiler keeps to represent the declaration (this representation must be created anyway, regardless if reflection is used or not). So for every *reflected* declaration at most a few (like 1-2) dozens of bytes.

If you are basing all your assumptions on the experimental implementation that I wrote you are doing it wrong.
That implementation is only partially lazy and terribly unoptimized and I'm certain that all  the clever people writing the compilers (for example clang), who have much deeper understanding of the compiler's internals than myself, can do wonderful things with optimizing the representation, because they've already done that in many such situations in the past.
 

Another point is that, for example besides the clear intention of meta::get_pointer, one still needs to learn about it along with the reflection mechanism (what consists in what I call "the reflection way" of doing things).
In the other hand every C++ user already knows about the ampersand operator. They will need only to learn how to use reflection in terms I've presented.

Nobody is forcing you to use reflection in situations when you can use the ampersand operator. The point you keep missing is that the decision that you want take the address of something may be buried deep in a complex metaprogram. If you use metaobjects  + get_pointer then you just need to pass the metaobject around many layers of the metaprogram and take the address only when you really need it at the place where you need it. Otherwise you also need to pass around the pointer iteself which adds unnecessary (and potentially many times unused) parameters to the metaprogram.

Could you explain what is wrong with having an additional option, which aims at situations when the ampersand cannot or would be clumsy to use?
 
 

- Declaration name operators:
  - declname(identifier) => fixed_string

get_name_v<reflexpr(identifier)>;
 
    - take types: yes, declname(type) => fixed_string
  - namedecl(fixed_string) => identifier

This is the only thing which cannot be done at the moment, although it would be nice to have.
 

Right, the meta-functions described in your proposal make the conversion from a reflected entity to the actual declaration initially unnecessary.
At the expense of requiring an additional meta-function for each operation for which the language itself already has already a construct.
i.e. get_pointer_v<reflexpr(identifier)> vs. &
 
I never said that from now on you have to use get_pointer_v<reflexpr(identifier)> where you previously used  &identifier. *But*,

template <typename MO>
void meta_routine_1(void)
{
    if(is_type_v<MO>) // this can be true for example 4.2% of the time
    { do_something_with(get_pointer_v<MO>) };
    else.
}

template <typename MO>
void meta_routine_2(void)
{
    if(something<MO>) meta_routine_1<MO>();
    else ...
}

... meta_routine_3 - meta_routine_24
 
template <typename MO>
void meta_routine_25(void)
{
    if(something<MO>) meta_routine_24<MO>();
    else ...
}

int foo;

meta_routine_25<reflexpr(std)>(); // see no & for namespaces
meta_routine_25<reflexpr(std::string)>(); // no & for types either
meta_routine_25<reflexpr(foo)>();


Are suggesting is that the following is somehow fundamentally better?


template <typename MO, typename X>
void meta_routine_1(X* sometimes_valid)
{
    if(sometimes_valid) // this can be true for example 4.2% of the time
    { do_something_with(sometimes_valid) };
    else.
}

template <typename MO, typename X>
void meta_routine_2(X* sometimes_valid)
{
    if(something<MO>) meta_routine_1<MO>(sometimes_valid);
    else ...
}

...


template <typename MO, typename X>
void meta_routine_25(X* sometimes_valid)
{
    if(something<MO>) meta_routine_24<MO>(sometimes_valid);
    else ...
}

meta_routine_25<reflexpr(std)>(nullptr);
meta_routine_25<reflexpr(std::string)>(nullptr);
meta_routine_25<reflexpr(foo)>(&foo);

 
    - take user-defined fixed_string as input: yes
    - take keywords as input: why not?

get_name_v<reflexpr(static)> (reflexpr(static) will return a MetaSpecifier in the future).
 

My concern is that, by following this trend we will end up with one MetaObject instance per word of C++ code.
I'm far from being an expert in compilers, but I don't think they would be able to keep up with that.
Redesigning a compiler because of the requirements for using reflection also does not sound like a good idea.

Until we are going to reflect every token in a C++ program, this is FUD.
 

 
- Declaration scope operator (obtain all the declared names in the given scope):
  - declscope(class/enum/namespace) => fixed_string[]

playing with `get_scope` + `get_name`

Considering your proposal already have a reasonably sized library support in it, a lot of user code would still be needed for achieving the same list of fixed_strings using get_scope+get_name.
And sometimes a list of name it is all I need.
 
 
  - filtering: could be done with the resulting array but maybe a second argument for declscope(scope, what) or more operators...

compile-time string manipulation similar to what for example the Metaparse library does.

My comments about filtering were more related to separating the entities that each fixed_string refers to in groups or categories.
Mainly:
- types (nested, typedefs, using aliases),
- objects (member, variables),
- functions (member, static, constructors),
- templates/concepts, namespaces.
The idea is finding ways to obtain only entity names of one of these groups.
I have no intention to require compile-time string transformations as part of the reflection.
 
 
  - accessibility: same as declared, breaking encapsulation is out of scope, requires diffent mechanism

`is_public<Metaobject>` - this will be more fine grained when specifier reflection is introduced.
 

In my mind specifier reflection should give me the same information which is possible to obtain with meta::is_public in your proposal.
Something like: specifiers(name) => fixed_string[] i.e. "public", "const"

IMO, a way to violate the access defined by the class author should not be implemented by reflection.
In other words, one may want to access a private member without using reflection.
The fact that other languages do it by using reflection does not mean it's the best way of doing it.

OK, so you are suggesting that all those languages are wrong and that we should drop many use cases, where the reflection of private members is necessary, like non-intrusive serialization, marshalling, OR/M mapping, etc.?
 

I'm more towards something like a "cast" operator:
unrestricted_access(c.m) // break into the private member "m"

get_data_members<MetaClass> vs. get_all_data_members<MetaClass>

B.T.W. have you noticed that you keep adding new and new operators (= reserved words, which may cause name conflicts with the zillions lines of legacy code out there), also did you think about the impact the implementation of all these operators will have on the compiler? Each one of them will have its own rules for the operands, this is somehow negligible but representing a declaration with a (minimalistic) type is bad?
 

Or even a form of inheritance:
class X : friend Y {}; // anything in X is not public in Y
Which is cool, because by using "final" the class authors would prevent it to be done.

 
- attributes, templates/concepts, function/template argument names, declaration specifiers: still need some thinking

There will be a MetaTemplate, MetaTemplateParameter, etc.
 

Along with more pressure on the type system.

Because adding new operators (implementing the same features) will not add any pressure whatsoever (not to mention again the explosion of reserved identifiers).
 
 

Use cases:
- pointers: &namedecl(fixed_string), &T::namedecl(fixed_string)
- references: auto& r = namedecl(fixed_string) - being 'name' an object
- functions: namedecl(fixed_string)(value)
- type: decltype(namedecl(fixed_string))
- members: T::namedecl(fixed_string), t.namedecl(name)
- enums: namedecl(fixed_string) => value, array_size(declanames(fixed_string)) => entries
- scope: namedecl(scope)::namedecl(member), namedecl(object).namedecl(member)
- compilation error: namespace(name) if "name" does not exist in the current scope

One last thing:
- overloads: overload_set(identifier) => typelist of the signatures (but what if one of them is a template function?)

There will be a MetaOverloaded function which will return a sequence of MetaFunctions some of which may also be MetaTemplates.

If you have a look at N4111 most of the concepts like MetaSpecifier, MetaTemplate, MetaTemplateParameter, MetaOverloadedFunction, MetaFunction, MetaConstructor, MetaOperator, etc. were already proposed, but were (temporarily) removed from the subsequent proposals at the request of some members of the committee. They *will* be reintroduced later.


My final words are that P0194R0 is a very nice proposal, it has a clear roadmap and honestly, it fits all the use cases I had in mind for reflection if everything your proposed initially gets implemented.
There's no doubt you know your stuff and did a great work on it. Passing through the committee multiple times is a good proof of it.
I'm just not convinced yet it's the best form of reflection we can have in C++.

So why don't you write (or haven't you written yet) a proposal and an implementation, preferably having the same features and covering all the use cases, convincing us all otherwise? I'd be very interested in reading / trying it out.

Regards,

Matus
 

Ricardo Andrade

unread,
Apr 20, 2016, 2:27:04 AM4/20/16
to SG 7 - Reflection
Roland,

If you have some time, I'd like to hear your opinion on the following draft:
https://github.com/ricardofandrade/cpp-name-reflection/blob/master/README.md

As you'll see I rely on your proposal for the definition of std::name.

Thank you,
Ricardo Andrade

Roland Bock

unread,
May 1, 2016, 6:26:02 AM5/1/16
to refle...@isocpp.org
Hi Ricardo,

First of all, I am happy to see that there is interest in names :-)

That being said, I am not convinced by your approach to have the
name-operator yield the fully qualified name and then use meta functions
to decompose that name.

From the top of my head, I can only think of a single use case that
requires the fully qualified name: If I want to know which namespace a
certain type/object/value was created in. And I am the first to admit
that I wanted that for the wrong reasons at the time.

Also, while potentially being useful, I wonder if some of the meta
functions you propose are even possible? For instance:

* is_function_name_v: I don't think this makes sense. Whether or not a
function is available might depend on ADL.
* is_argument_name_v: This depends on the context in which you call it.
is_argument_name_v<some_name> would have to yield true in some places
and false in others.

Similar with others.

Or those functions would tell something about the object that was used
to obtain the name. Bur in that case, the std::name would have to carry
tons of data in addition to the name.

That is not necessarily bad, but it should then have a different name.

Best,

Roland
> <javascript:>

Ricardo Fabiano de Andrade

unread,
May 3, 2016, 12:14:00 AM5/3/16
to refle...@isocpp.org
Hi Roland,
Your feedback is much appreciated.

On Sunday, May 1, 2016, Roland Bock <rb...@eudoxos.de> wrote:
Hi Ricardo,

First of all, I am happy to see that there is interest in names :-)

It's definetely very interesting. 


That being said, I am not convinced by your approach to have the
name-operator yield the fully qualified name and then use meta functions
to decompose that name.
 
Please don't see it as a requirement.
Unqualified names could work as well as the qualified ones for most cases.

From the top of my head, I can only think of a single use case that
requires the fully qualified name: If I want to know which namespace a
certain type/object/value was created in. And I am the first to admit
that I wanted that for the wrong reasons at the time.


The whole point of the "qualified names first" approach was allowing, in the context of reflection, transporting a name across several layer of metaprogramming without losing its source.
Carrying multiple names (i.e. type+member) is also an option and honestly may work better for the context of your proposal.
 
Also, while potentially being useful, I wonder if some of the meta
functions you propose are even possible? For instance:

* is_function_name_v: I don't think this makes sense. Whether or not a
function is available might depend on ADL.

Correct, all meta functions would require the entity referred by the given name to be visible from the point where it is called.
Using qualified names here would avoid dealing with issues of another entity with same name on the scope of the caller.
 
* is_argument_name_v: This depends on the context in which you call it.
is_argument_name_v<some_name> would have to yield true in some places
and false in others.


Correct. In this case, there's the additional issue of a non-existing qualified argument name.
 
Similar with others.


I've noticed this limitation while studying a way to implementing it. More on that ahead.
 
Or those functions would tell something about the object that was used
to obtain the name. Bur in that case, the std::name would have to carry
tons of data in addition to the name.


If you think in terms "normal" C++, where all the information is on the name, your thinking is correct. However, in the context of reflection the owner of the information is the compiler not the code.
In my mind, when you get a name, all you have are the characters representing that name, nothing else.
When a reflection meta function is called, at some point that name will be passed to a compiler built-in.
This built-in will execute the name lookup for the given name, obtaining the compiler's internal representation of the referred entity.
From that point on, it's trivial to generate literal values with the meta information about the entity.
My doubt here is if the compiler can "read" the characters inside of a given instance of std::name or if it needs to carry both a literal representation (the characters) and a compiler representation (the name as seen by the compiler or an id or even a "pointer" to the original entity's internal representation).
The advantage of the latter option is making it possible to obtain the correct value for the meta functions regardless of the calling context.
The problem would be having two classes of names:
- reflection std::name - with context
- user-defined std::name literals - no context
 
That is not necessarily bad, but it should then have a different name.


You may be right. Maybe reflection-obtained names and user-defined names need to be two separate things.
 
Best,

Roland


One additional note: even though I believe some of my ideas have advantages over the other reflection proposals presented so far, I received some comments that pointed to a problematic implementation. So for now, instead of working towards a separate proposal I'll try provide feedback to the existing ones.
I'll keep your ideas in mind while writing about reflection.
 
You received this message because you are subscribed to a topic in the Google Groups "SG 7 - Reflection" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/reflection/uksZoUpQaUk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to reflection+...@isocpp.org.
To post to this group, send email to refle...@isocpp.org.
Reply all
Reply to author
Forward
0 new messages