#include <reflexpr>
#include <iostream>
#include <vector>
namespace foo
{
class bar{};
using V = std::vector<bar>;
}
int main()
{
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(V)>> << std::endl;
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(std::vector<foo::bar>)>> << std::endl;
}
foo
std::__1
using T = std::vector<int>
using MV = get_aliased_m<reflexpr(T)>;
#include <reflexpr>
#include <iostream>
template<typename T>
auto foo(const T& t)
{
std::cout << std::meta::get_base_name_v<reflexpr(T)> << std::endl;
}
int main()
{
foo(42);
}
In the past few days I finally found the time to play with the experimental reflection implementation in clang by Matus Chochlik (https://github.com/matus-chochlik/clang/tree/reflexpr). Very impressive!Thanks to Matus Chochlık, Axel Naumann, David Sankel for the proposals and the experimental implementation. It is fun playing with it. If you haven't tried to experiment with it, I strongly suggest you do. This gives you the chance to get a feel for what the current proposal (P0194R2) really has to offer.
While doing so, I ran into the occasional bug (it is experimental, so this is to be expected), and wrote down a couple of coments/questions regarding the proposal (P0194R2) and its rationale (P0385R1). Bugs are reported on github, comments and questions follow below:*) Metaobjects are types:Not being a language lawyer at all, I would still argue that the wording is sub-optimal. In other contexts, objects have a type. These metaobjects are types? In particular, a metaobject can be the type of an object, since you can construct an object of a metaobject. That just seems confusing to me.
Maybe we should call them meta types instead?
However, to be consistent with the proposal, I will continue to call them metaobjects below.*) Metaobjects are "opaque":Earlier revisions of the proposal did not allow the creation of metaobject objects. Maybe then it made sense to just not say anything concrete about the metaobjects.However, now that we can construct them, we should say a bit more, I guess. For instance, it seemed like the most natural thing for me to create constexpr objects of metaobjects. The current proposal does not say whether that is legal.IMO, the easiest way to go about it would be to say that metaobjects are empty structs. They have all the traits we need and we don't have to specify their behavior (other than in our concepts and traits). And the mystical opaqueness would just vanish.
*) Namespace for metaobjects:The proposal does not say anything about the namespace of metaobjects, but it would be helpful to know for ADL, for instance.But since metaobjects are types, they can be reflected (so much for opaqueness, btw). So we can take a look at where they live in the experimental implementation: They reside in namespace std.I believe that the proposal should specify the namespace for metaobjects and I believe that should be std::meta. For instance, we could then add constexpr functions in parallel to all the current traits (e.g. constexpr auto get_aliased_f(const T&) { return get_aliased_m<T>{}; }) that would work well with ADL.
*) Reflecting (record) variables:I understand from the examples in P0385R1 one of the main motivations for reflecting variables is obtaining their name. Also, I can
- learn if the variable is static
- obtain a pointer (or class data member pointer) to it.
And I have the well known traits to know whether it is const or volatile.Missing traits are IMO:
- is_constexpr
- is_inline
- has_default_value (in case of class members)
*) Namespaces:Consider this program
#include <reflexpr>
#include <iostream>
#include <vector>
namespace foo
{
class bar{};
using V = std::vector<bar>;
}
int main()
{
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(V)>> << std::endl;
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(std::vector<foo::bar>)>> << std::endl;
}It prints:
foo
std::__1`V`'s scope is `foo`, which seems obvious. `V`'s namespace is `std::__1`. This is also fine, because vector is actually declared inside the inline namespace `__` inside namespace std in libc++. Still, it is a bit surprising.Again, it would be nice to have an `is_inline` trait to check whether a namespace is inline. That way I could choose to omit the `::__1` when printing the name, for instance.
It might also be interesting to obtain associated namespaces. This might be useful for diagnostic purposes when trying to understand ADL for complex types/functions. See also next item.
*) Template specializations:Is there a way to obtain the arguments of a template specialization? For instance,
using T = std::vector<int>
using MV = get_aliased_m<reflexpr(T)>;Is there a way to learn that `MV` represents a template specialization of `std::vector` (or `std::__1::vector`) with `int` being the argument for the first template parameter, while the other parameters take their default arguments?How can I compose the "display name" from the base names otherwise?
*) Alias or the real thing?Consider this code:
#include <reflexpr>
#include <iostream>
template<typename T>
auto foo(const T& t)
{
std::cout << std::meta::get_base_name_v<reflexpr(T)> << std::endl;
}
int main()
{
foo(42);
}This program prints `T`. I won't say that that is wrong, but I can say that it took me by surprise. I was expecting to see `int` (probably because I read that part of the proposal only afterwards).Personally, I would prefer if the meta object yielded by `reflexpr(T)` represented `int` in this case and there were a meta function `template <typename T> struct get_alias<T>;` that provides a meta object representing the `T`.I claim that `get_alias` to get the alias (e.g. `T`) would also be more intuitive than `get_aliased` to get the underlying type (e.g. `int`).
*) Suffix `_m`:I find the suffix `_m` confusing rather than helpful. Those metafunctions return types. They are implemented as `template<typename T> using xxx_m = typename xxx<T>::type;`.Why not use the canonical suffix `_t`? I understand it was like this before. I do not understand why it was changed to `_m`. Does not make much sense to me. Sure, they might be those special opaque meta types (see my first items), but they are still types.
*) get_base_classes/get_base_class/get_base_name:
- get_base_classes takes a meta Class and returns a meta Sequence of meta Inheritances
- it does not yield base classes, but inheritances
- get_base_class takes a meta Inheritance and returns a meta Class
- it does not yield a base class of an inheritance (no such thing)
- get_base_name has nothing to do with inheritance at all, yet the current naming scheme seems to imply that.
Suggested renaming:s/get_base_classes/get_inheritances/s/get_base_class/get_class/
*) is_class/is_struct:I wonder if this distinction is really useful. The only difference between struct and class is the default accessibility of members and base classes, isn't it? And we can obtain the access specifiers of each member. So what's the point?They are so interchangeable that a specialization of a template struct can be a class and vice versa.
Also, there is a concept meta::Class but not meta::Struct.
So much for the moment. Thanks for reading :-)
On Mon, Jan 2, 2017 at 6:56 PM, Roland Bock <rb...@eudoxos.de> wrote:*) Metaobjects are types:Not being a language lawyer at all, I would still argue that the wording is sub-optimal. In other contexts, objects have a type. These metaobjects are types? In particular, a metaobject can be the type of an object, since you can construct an object of a metaobject. That just seems confusing to me.I agree that the name 'metaobject' is confusing mainly because 'object' has several meanings in CompSci and even in the C++ standard; likewise 'meta' in 'metaprogramming'.
On the other hand the term 'metaobject' (and 'meta-level' vs. 'base-level') is pretty established in reflection terminology. We have considered other terms like 'meta-entity' (as if 'entity' is any clearer than 'object' :-P), 'meta-declaration', 'meta-expression', 'reflection' [noun], etc. but we eventually decided to stick with 'metaobject'.Maybe we should call them meta types instead?As I see it a meta-type is just a subset of a larger set of 'meta-objects' reflecting various "stuff" in the language.
*) Namespace for metaobjects:The proposal does not say anything about the namespace of metaobjects, but it would be helpful to know for ADL, for instance.But since metaobjects are types, they can be reflected (so much for opaqueness, btw). So we can take a look at where they live in the experimental implementation: They reside in namespace std.I believe that the proposal should specify the namespace for metaobjects and I believe that should be std::meta. For instance, we could then add constexpr functions in parallel to all the current traits (e.g. constexpr auto get_aliased_f(const T&) { return get_aliased_m<T>{}; }) that would work well with ADL.The namespace (and the name) of metaobjects is intentionally left unspecified (at the moment, but see below).
What you are proposing can be easily achieved by wrapping the metaobject into a concrete template:
[Note]Currently the metaobjects are implemented as constexpr values of a special type `__metaobject_id` and the metaobjects themselves are implemented as:
``template <__metaobject_id>struct __metaobject { };
``We are still debating whether we should expose this and make it part of the "official" interface. I personally think that it would be a good idea (even if it limits the implementations), Axel seems to disagree ;).[/Note]
*) Alias or the real thing?Consider this code:
#include <reflexpr>
#include <iostream>
template<typename T>
auto foo(const T& t)
{
std::cout << std::meta::get_base_name_v<reflexpr(T)> << std::endl;
}
int main()
{
foo(42);
}This program prints `T`. I won't say that that is wrong, but I can say that it took me by surprise. I was expecting to see `int` (probably because I read that part of the proposal only afterwards).Personally, I would prefer if the meta object yielded by `reflexpr(T)` represented `int` in this case and there were a meta function `template <typename T> struct get_alias<T>;` that provides a meta object representing the `T`.I claim that `get_alias` to get the alias (e.g. `T`) would also be more intuitive than `get_aliased` to get the underlying type (e.g. `int`).We have considered to add a similar operation -- `get_known_aliases` which would return a sequence of meta-aliases, reflecting all known aliases (template parameters, type aliases, namespace aliases, etc.), for a metaobject. I personally like the current interface more.
In any case it is possible to strip the alias pretty easily if it is not desired. Again the Mirror library shows how to implement this.
*) Suffix `_m`:I find the suffix `_m` confusing rather than helpful. Those metafunctions return types. They are implemented as `template<typename T> using xxx_m = typename xxx<T>::type;`.Why not use the canonical suffix `_t`? I understand it was like this before. I do not understand why it was changed to `_m`. Does not make much sense to me. Sure, they might be those special opaque meta types (see my first items), but they are still types.We are considering turning `*_m` back to `*_t` in the next revision of P0194 and we will probably do that (although personally I find it useful being able to distinguish between operations returning "regular" types and operations returning metaobjects, I can live without it though).
--
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.
To post to this group, send email to 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.
-- Hal Finkel Lead, Compiler Technology and Programming Languages Leadership Computing Facility Argonne National Laboratory
On Monday, January 2, 2017 at 9:26:09 PM UTC+1, Matúš Chochlík wrote:I agree that the name 'metaobject' is confusing mainly because 'object' has several meanings in CompSci and even in the C++ standard; likewise 'meta' in 'metaprogramming'.
On the other hand the term 'metaobject' (and 'meta-level' vs. 'base-level') is pretty established in reflection terminology. We have considered other terms like 'meta-entity' (as if 'entity' is any clearer than 'object' :-P), 'meta-declaration', 'meta-expression', 'reflection' [noun], etc. but we eventually decided to stick with 'metaobject'.Maybe we should call them meta types instead?As I see it a meta-type is just a subset of a larger set of 'meta-objects' reflecting various "stuff" in the language.Ah, so you're thinking of all the reflectable things and summarize them as objects, hence metaobjects.I interpret the name from the user's perspective. What I receive from reflexpr() is type, therefore metatype.In the end it is bike-shedding :-)
*) Namespace for metaobjects:The proposal does not say anything about the namespace of metaobjects, but it would be helpful to know for ADL, for instance.But since metaobjects are types, they can be reflected (so much for opaqueness, btw). So we can take a look at where they live in the experimental implementation: They reside in namespace std.I believe that the proposal should specify the namespace for metaobjects and I believe that should be std::meta. For instance, we could then add constexpr functions in parallel to all the current traits (e.g. constexpr auto get_aliased_f(const T&) { return get_aliased_m<T>{}; }) that would work well with ADL.The namespace (and the name) of metaobjects is intentionally left unspecified (at the moment, but see below).
What you are proposing can be easily achieved by wrapping the metaobject into a concrete template:Sure, but then I need to do that in my library or use some 3rd party library, e.g. mirror.I would like to see those functions in the standard, too, maybe in a later proposal. And then it might be useful to have pinned the namespace.Do you really gain anything from not pinning the namespace?
[Note]Currently the metaobjects are implemented as constexpr values of a special type `__metaobject_id` and the metaobjects themselves are implemented as:``template <__metaobject_id>struct __metaobject { };
``We are still debating whether we should expose this and make it part of the "official" interface. I personally think that it would be a good idea (even if it limits the implementations), Axel seems to disagree ;).[/Note]Personally, I'd rather specify the namespace than how the types are specified by the compiler. I don't really see what you might gain from the latter.
I claim that `get_alias` to get the alias (e.g. `T`) would also be more intuitive than `get_aliased` to get the underlying type (e.g. `int`).We have considered to add a similar operation -- `get_known_aliases` which would return a sequence of meta-aliases, reflecting all known aliases (template parameters, type aliases, namespace aliases, etc.), for a metaobject. I personally like the current interface more.I've seen something like that in the revision history. Getting all known aliases would be overkill, I think. With a few nested namespaces and types this could explode quickly into something rather unwieldy.I would stick with the variant you currently have: Make the "current" alias and the underlying type/namespace available. I would just change the initial focus to the underlying type/namespace.
In any case it is possible to strip the alias pretty easily if it is not desired. Again the Mirror library shows how to implement this.Sure, that was the first wrapper I wrote myself :-)
If I correctly recall the discussion, supporting the construction of pretty-printing logic using template metaprogramming turned out to be something we specifically wanted to discourage. Pretty printing is complicated and modern compilers already have sophisticated, and sometimes system-specific, logic for this purpose. Providing programmatic access to that logic was discussed favorably, but trying to reconstruct that kind of logic in template metaprograms was predicted to lead to sub-par outcomes.
On 01/02/2017 11:56 AM, Roland Bock wrote:
*) Namespaces:
Consider this program
#include <reflexpr>
#include <iostream>
#include <vector>
namespace foo
{
class bar{};
using V = std::vector<bar>;
}
int main()
{
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(V)>> << std::endl;
std::cout << std::meta::get_display_name_v<std::meta::get_scope_m<reflexpr(std::vector<foo::bar>)>> << std::endl;
}
It prints:
foo
std::__1
`V`'s scope is `foo`, which seems obvious. `V`'s namespace is `std::__1`. This is also fine, because vector is actually declared inside the inline namespace `__` inside namespace std in libc++. Still, it is a bit surprising.
Again, it would be nice to have an `is_inline` trait to check whether a namespace is inline. That way I could choose to omit the `::__1` when printing the name, for instance.
Hi Matus,
Thanks for your explanations :-)
Looking forward to future iterations.
Cheers,
Roland
PS: Let me know if I can help, e.g. by testing or reading