matus-chochlik / std_cpp_refl

Proposal to add static reflection to C++
14 stars 2 forks source link

Scope on non-scoped enumerators #6

Open matus-chochlik opened 8 years ago

matus-chochlik commented 8 years ago

What is the scope of the enumerators of non-strongly typed enums ?

namespace foo {
enum E {
    a, b, c, d, e, f
};
} // namespace foo

using meta_foo = reflexpr(foo);
using meta_E = reflexpr(foo::E);
using meta_a = reflexpr(foo::a);

A) The enum itself:

meta::reflects_same_v<meta::get_scope_m<meta_a>, meta_E>; // true
meta::reflects_same_v<meta::get_type_m<meta_a>, meta_E>; // true

B) The enclosing scope:

meta::reflects_same_v<meta::get_scope_m<meta_a>, meta_foo>; // true
meta::reflects_same_v<meta::get_type_m<meta_a>, meta_E>; // true

C) Two scopes ("declaration" scope vs. "lexical"/"lookup" scope)

meta::reflects_same_v<meta::get_scope_m<meta_a>, meta_E>; // true
meta::reflects_same_v<meta::get_lookup_scope_m<meta_a>, meta_foo>; // true
ricardofandrade commented 8 years ago

The solution C) could also be used for anonymous namespaces, unions and structs (anonymous scopes in general).

For example:


namespace A {
    namespace {
        struct S {};
    }
    class C {
        union {
            int a;
            char b[sizeof(int)];
        };
    }
    struct X {
        struct {
             int y;
        };
    };
}

A::S - declaration scope is the anonymous namespace of A, the scope is A. A::C::a - declaration scope is the anonymous union of C, the scope is C. A::X::y - declaration scope is the anonymous struct of X, the scope is X.

ricardofandrade commented 8 years ago

I was discussing this issue with a colleague and he got confused by the name get_lookup_scope_m. There's some potential behind his line of thought so please allow me to voice his opinion here. He thought get_lookup_scope_m meant the "resulting scope of a name lookup" - not sure if these are the right terms but here is his example:


namespace A {
    namespace B {
        int a;
    }
}
using namespace A;
using meta_a1 = reflexpr(B::a);
constexpr auto same_as_B = meta::reflects_same_v, reflexpr(B)>; // true
using namespace B;
using meta_a2 = reflexpr(::a);
constexpr auto same_as_Global = meta::reflects_same_v, reflexpr(::)>; // true

On top of that, he would expect B) the be the actual default behavior.

That said, I'd like to make a few observations:

  1. get_lookup_scope_m should actually be called get_declscope_m (or get_declaration_scope_m). This way there's no confusion what it means: the scope where the "thing" was declared. In more precise terms, the immediate parent scope.
  2. get_scope_m, as it seems to be expected by most, is the scope you would need to type in order to access a "thing" directly (i.e. ::std for std::string) as there was no using namespace involved. Also, in more precise terms, the nearest named scope - please note that regular enum won't name its scope.
  3. I don't really think this is needed but... get_lookup_scope_m would be scope where a "thing" is currently visible. (i.e. all of std is on :: after a using namespace std;, so get_lookup_scope_m(string_view) is the global scope). Please note that this seems to always be the same scope passed to reflexpr.
matus-chochlik commented 8 years ago

For the moment we have decided that get_scope would return the declaration scope. I.e. for enumerators in a non-scoped enum, the scope is the enum. Same for members of an anonymous namespace, their scope is the anonymous namespace. One can use the is_scoped operation for enums and is_anonymous operation for other scopes to detect the special cases.

Another operation that would return the "lookup" scope can be added later, or implemented on top of what we already have in a library.