Thread overview
hasStaticMember and enums
Sep 22, 2020
Aliak
Sep 22, 2020
Jacob Carlborg
Sep 22, 2020
Jacob Carlborg
September 21, 2020
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
September 22, 2020
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”?

September 22, 2020
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
September 22, 2020
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.


September 22, 2020
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
September 22, 2020
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
September 22, 2020
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
September 22, 2020
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