www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - hasStaticMember and enums

reply Steven Schveighoffer <schveiguy gmail.com> writes:
I was confused today when I was looking to see if a particular field 
name was a field of a struct and not a static member.

I thought __traits(hasMember, T, item) && !hasStaticMember!(T, item) 
would suffice. But then I found that this returns true for enums that 
are part of the type.

struct S
{
    enum e = "hello";
}

static assert(__traits(hasMember, S, "e"));
static assert(!hasStaticMember!(S, "e"));

The reason is because hasStaticMember ultimately uses:

alias sym = Alias!(__traits(getMember, U, member));
...
enum hasStaticMember = __traits(compiles, &sym);

which of course doesn't compile for an enum.

But I would actually consider an enum to be a static member. It's a 
member, but does not consume any part of the instance.

The docs for hasStaticMember are pretty slim, but it was added here: 
https://github.com/dlang/phobos/pull/5112

There is no mention of how this should play with enums. Would it be bad 
to add a check for enums in this? I was thinking of changing the 
__traits(compiles) line to:

__traits(compiles, (auto ref a) { } (sym));

or something like that.

Thoughts?

I'm also not sure why std.meta.Alias is used instead of a normal alias 
(maybe that used to be an issue? I tried just a straight alias and it 
works at least in this case).

-Steve
Sep 21 2020
next sibling parent Aliak <something something.com> writes:
On Monday, 21 September 2020 at 23:33:17 UTC, Steven 
Schveighoffer wrote:
 I was confused today when I was looking to see if a particular 
 field name was a field of a struct and not a static member.

 I thought __traits(hasMember, T, item) && !hasStaticMember!(T, 
 item) would suffice. But then I found that this returns true 
 for enums that are part of the type.

 struct S
 {
    enum e = "hello";
 }

 static assert(__traits(hasMember, S, "e"));
 static assert(!hasStaticMember!(S, "e"));

 The reason is because hasStaticMember ultimately uses:

 alias sym = Alias!(__traits(getMember, U, member));
 ...
 enum hasStaticMember = __traits(compiles, &sym);

 which of course doesn't compile for an enum.

 But I would actually consider an enum to be a static member. 
 It's a member, but does not consume any part of the instance.

 The docs for hasStaticMember are pretty slim, but it was added 
 here: https://github.com/dlang/phobos/pull/5112

 There is no mention of how this should play with enums. Would 
 it be bad to add a check for enums in this? I was thinking of 
 changing the __traits(compiles) line to:

 __traits(compiles, (auto ref a) { } (sym));

 or something like that.

 Thoughts?

 I'm also not sure why std.meta.Alias is used instead of a 
 normal alias (maybe that used to be an issue? I tried just a 
 straight alias and it works at least in this case).

 -Steve
Thoughts: static members are addressable so if you have metà programs that are checking if something is static and then taking the address based on that, those will break. So they are the same in how you access them but not in terms of memory. Can there be a different meta function that checks for static, non-static, and manifest? “HasAnyMember”?
Sep 21 2020
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On Monday, 21 September 2020 at 23:33:17 UTC, Steven 
Schveighoffer wrote:
 I was confused today when I was looking to see if a particular 
 field name was a field of a struct and not a static member.
You can use `tupleof` to access the fields. Combine that with `__traits(identifier)` to get the name of a field. -- /Jacob Carlborg
Sep 22 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 9/22/20 6:12 AM, Jacob Carlborg wrote:
 On Monday, 21 September 2020 at 23:33:17 UTC, Steven Schveighoffer wrote:
 I was confused today when I was looking to see if a particular field 
 name was a field of a struct and not a static member.
You can use `tupleof` to access the fields. Combine that with `__traits(identifier)` to get the name of a field.
I don't have that though. I have a name. I'm writing a meta-type that creates members for all fields using opDispatch: auto opDispatch(string name)() if (isField!(T, name)) So I could potentially loop through each member of tupleof and check if the name matches, but I would prefer a more direct route. -Steve
Sep 22 2020
parent Jacob Carlborg <doob me.com> writes:
On 2020-09-22 14:40, Steven Schveighoffer wrote:

 So I could potentially loop through each member of tupleof and check if 
 the name matches, but I would prefer a more direct route.
Yeah, that was what I was thinking of. With `.tupleof` it might be a bit more complicated to get the name, but you know for sure that you're only looking at the fields. It seems like the other approaches have edge cases. -- /Jacob Carlborg
Sep 22 2020
prev sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Monday, 21 September 2020 at 23:33:17 UTC, Steven 
Schveighoffer wrote:
 I was confused today when I was looking to see if a particular 
 field name was a field of a struct and not a static member.

 I thought __traits(hasMember, T, item) && !hasStaticMember!(T, 
 item) would suffice. But then I found that this returns true 
 for enums that are part of the type.

 struct S
 {
    enum e = "hello";
 }

 static assert(__traits(hasMember, S, "e"));
 static assert(!hasStaticMember!(S, "e"));

 The reason is because hasStaticMember ultimately uses:

 alias sym = Alias!(__traits(getMember, U, member));
 ...
 enum hasStaticMember = __traits(compiles, &sym);

 which of course doesn't compile for an enum.

 But I would actually consider an enum to be a static member. 
 It's a member, but does not consume any part of the instance.

 The docs for hasStaticMember are pretty slim, but it was added 
 here: https://github.com/dlang/phobos/pull/5112

 There is no mention of how this should play with enums. Would 
 it be bad to add a check for enums in this? I was thinking of 
 changing the __traits(compiles) line to:

 __traits(compiles, (auto ref a) { } (sym));

 or something like that.

 Thoughts?
I'd say aggregates have 4 different types of members: * instance fields and functions * static fields and functions * enum fields and functions (though enum functions may become regular instance/static functions if https://wiki.dlang.org/DIP27 is accepted). * alias-es (a case could be made that from an aggregate user point of view, alias members should be indistinguishable from normal members, but technically they're different, and this could be important for meta code to be able to distinguish. Also there are things like certain types of alias sequences that can't be neither instance, static or enum members.)
 I'm also not sure why std.meta.Alias is used instead of a 
 normal alias (maybe that used to be an issue? I tried just a 
 straight alias and it works at least in this case).

 -Steve
Before a couple of releases you couldn't alias the result of __traits() expressions and mixins (among other limitations), and this code predates those language improvements.
Sep 22 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 9/22/20 7:04 AM, Petar Kirov [ZombineDev] wrote:
 
 I'd say aggregates have 4 different types of members:
 * instance fields and functions
 * static fields and functions
 * enum fields and functions (though enum functions may become regular 
 instance/static functions if https://wiki.dlang.org/DIP27 is accepted).
 * alias-es (a case could be made that from an aggregate user point of 
 view, alias members should be indistinguishable from normal members, but 
 technically they're different, and this could be important for meta code 
 to be able to distinguish. Also there are things like certain types of 
 alias sequences that can't be neither instance, static or enum members.)
... On 9/22/20 12:50 AM, Aliak wrote:
 Thoughts: static members are addressable so if you have metà programs that
are checking if something is static and then taking the address based on that,
those will break. So they are the same in how you access them but not in terms
of memory. 
Thanks for all the replies. It looks clear that we can't add enums into this trait. We simply need more traits that can figure out the different types of members. So maybe a hasInstanceMember at a minimum (then I can use that to distinguish instance members from non-instance ones), and then I don't know if we can look further at enum and alias members. For sure alias members are odd, I don't know how you would distinguish them from other members. Indeed, in the use case I have, I would treat an alias of another field member as another separate field member, even though it points at the same field as something else. This would be problematic. On 9/22/20 7:04 AM, Petar Kirov [ZombineDev] wrote:
 Before a couple of releases you couldn't alias the result of __traits() 
 expressions and mixins (among other limitations), and this code predates 
 those language improvements.
OK, thanks. We should remove that complexity. Would be an easy PR for someone to get their feet wet! Just switch from Alias to a standard alias. -Steve
Sep 22 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 9/22/20 8:52 AM, Steven Schveighoffer wrote:
 For sure alias members are odd, I don't know how you would distinguish 
 them from other members.
Maybe, check if __traits(identifier, __traits(getMember, T, name)) == name ? Would that be correct? -Steve
Sep 22 2020