Thread overview
Getting only the data members of a type
Mar 31, 2012
Ali Çehreli
Apr 01, 2012
Artur Skawina
Apr 01, 2012
Ali Çehreli
Apr 01, 2012
Artur Skawina
Apr 01, 2012
Jacob Carlborg
Apr 01, 2012
Artur Skawina
Apr 01, 2012
Artur Skawina
Apr 01, 2012
Jacob Carlborg
Apr 02, 2012
Ary Manzana
Apr 02, 2012
Jacob Carlborg
March 31, 2012
How can I determine just the data members of a struct or class, excluding the member functions? isCallable() comes short as member variable that have opCall() defined are also callable:

import std.stdio;
import std.traits;

struct M
{
    void opCall()
    {}
}

struct S
{
    int i;
    M m;

    void foo()
    {}
}

void main()
{
    foreach (member; __traits(allMembers, S)) {
        if (isCallable!(mixin("S." ~ member))) {
            writefln("%5s is a member function", member);

        } else {
            writefln("%5s is a member variable", member);
        }
    }
}

The output shows that S.m is a function but it is not:

    i is a member variable
    m is a member function     <-- unexpected
  foo is a member function

Wait! I found a solution before sending this message. :P isIntegral() works:

        if (isIntegral!(typeof(mixin("S." ~ member)))) {

Now the output is:

    i is a member function
    m is a member variable     <-- good but dubious
  foo is a member variable

The reason is, isIntegral() specifically mentions built in types:

  http://dlang.org/phobos/std_traits.html#isIntegral

<quote>
Detect whether T is a built-in integral type. Types bool, char, wchar, and dchar are not considered integral.
</quote>

Thank you,
Ali
April 01, 2012
On 03/31/12 21:09, Ali Çehreli wrote:
> How can I determine just the data members of a struct or class, excluding the member functions? isCallable() comes short as member variable that have opCall() defined are also callable:
[...]
> Wait! I found a solution before sending this message. :P isIntegral() works:
> 
>         if (isIntegral!(typeof(mixin("S." ~ member)))) {
> 
> Now the output is:
> 
>     i is a member function
>     m is a member variable     <-- good but dubious
>   foo is a member variable
[...]

Don't forget about templates (appear as members too, but don't have a type).


This will print all fields of struct/class S:

   enum s = cast(S*)null;
   foreach (i, m; s.tupleof) {
      enum name = S.tupleof[i].stringof[4..$];
      alias typeof(m) type;
      writef("(%s) %s\n", type.stringof, name);
   }

Real Programmers don't use std.traits. ;)

artur
April 01, 2012
On 03/31/2012 09:09 PM, Artur Skawina wrote:
> On 03/31/12 21:09, Ali Çehreli wrote:
>> How can I determine just the data members of a struct or class, excluding the member functions? isCallable() comes short as member variable that have opCall() defined are also callable:
> [...]
>> Wait! I found a solution before sending this message. :P isIntegral() works:
>>
>>          if (isIntegral!(typeof(mixin("S." ~ member)))) {
>>
>> Now the output is:
>>
>>      i is a member function
>>      m is a member variable<-- good but dubious
>>    foo is a member variable
> [...]
>
> Don't forget about templates (appear as members too, but don't have a type).

I see. We can't even use isCallable or isIntegral with a template member. Assuming S has this member function template:

    void bar(T)()
    {}

Then we get the following error:

/usr/include/d/dmd/phobos/std/traits.d(3223): Error: (S).bar(T) has no value

I don't know a way of saying "if a template".

> This will print all fields of struct/class S:
>
>     enum s = cast(S*)null;
>     foreach (i, m; s.tupleof) {
>        enum name = S.tupleof[i].stringof[4..$];
>        alias typeof(m) type;
>        writef("(%s) %s\n", type.stringof, name);
>     }
>
> Real Programmers don't use std.traits. ;)
>
> artur

Your method works but needing to iterate on a struct variable by s.tupleof and having to use the struct type as S.tupleof in the loop body is strange.

Looks like S.tupleof[i].stringof is the answer for what I was looking for:

import std.stdio;
import std.traits;

struct M
{
    int i;

    this(int i)
    {
        this.i = i;
    }

    void opCall()
    {}
}

struct S
{
    int i;
    M m;

    void foo()
    {}

    void bar(T)()
    {}
}

void main()
{
    enum s = S(42, M(7));

    foreach (i, m; s.tupleof) {
        writefln("%s:", i);

        enum name = S.tupleof[i].stringof[4..$];
        alias typeof(m) type;
        writefln("  S.tupleof[i]: %s", S.tupleof[i].stringof);
        writefln("  (type) name : (%s) %s", type.stringof, name);

        writefln("  m           : %s", m);
        writefln("  m.stringof  : %s", m.stringof);
    }
}

The output:

0:
  S.tupleof[i]: (S).i
  (type) name : (int) i
  m           : 42
  m.stringof  : 42
1:
  S.tupleof[i]: (S).m
  (type) name : (M) m
  m           : M(7)
  m.stringof  : m

Ali

April 01, 2012
On 04/01/12 08:18, Ali Çehreli wrote:
> On 03/31/2012 09:09 PM, Artur Skawina wrote:
>> On 03/31/12 21:09, Ali Çehreli wrote:
>>> How can I determine just the data members of a struct or class, excluding the member functions? isCallable() comes short as member variable that have opCall() defined are also callable:
>> [...]
>>> Wait! I found a solution before sending this message. :P isIntegral() works:
>>>
>>>          if (isIntegral!(typeof(mixin("S." ~ member)))) {
>>>
>>> Now the output is:
>>>
>>>      i is a member function
>>>      m is a member variable<-- good but dubious
>>>    foo is a member variable
>> [...]
>>
>> Don't forget about templates (appear as members too, but don't have a type).
> 
> I see. We can't even use isCallable or isIntegral with a template member. Assuming S has this member function template:
> 
>     void bar(T)()
>     {}
> 
> Then we get the following error:
> 
> /usr/include/d/dmd/phobos/std/traits.d(3223): Error: (S).bar(T) has no value
> 
> I don't know a way of saying "if a template".

eg:
   static if (!__traits(compiles, &__traits(getMember, obj, name)))
or just
   static if (!is(typeof(member)))

>> This will print all fields of struct/class S:
>>
>>     enum s = cast(S*)null;
>>     foreach (i, m; s.tupleof) {
>>        enum name = S.tupleof[i].stringof[4..$];
>>        alias typeof(m) type;
>>        writef("(%s) %s\n", type.stringof, name);
>>     }
>>
>> Real Programmers don't use std.traits. ;)
>>
>> artur
> 
> Your method works but needing to iterate on a struct variable by s.tupleof and having to use the struct type as S.tupleof in the loop body is strange.

That's because the compiler won't accept "foreach (i, t; S.tupleof)" and "*.tupleof[i].stringof" is necessary to get the original name. This would have worked too:

      enum name = *s.tupleof[i].stringof[4..$];

but obfuscates the code more and looks like dereferencing a null pointer.

artur
April 01, 2012
On 04/01/12 11:27, Artur Skawina wrote:
> On 04/01/12 08:18, Ali Çehreli wrote:
>> On 03/31/2012 09:09 PM, Artur Skawina wrote:
>>> This will print all fields of struct/class S:
>>>
>>>     enum s = cast(S*)null;
>>>     foreach (i, m; s.tupleof) {
>>>        enum name = S.tupleof[i].stringof[4..$];
>>>        alias typeof(m) type;
>>>        writef("(%s) %s\n", type.stringof, name);
>>>     }
>>>
>>> Real Programmers don't use std.traits. ;)
>>>
>>> artur
>>
>> Your method works but needing to iterate on a struct variable by s.tupleof and having to use the struct type as S.tupleof in the loop body is strange.
> 
> That's because the compiler won't accept "foreach (i, t; S.tupleof)" and "*.tupleof[i].stringof" is necessary to get the original name. This would have worked too:
> 
>       enum name = *s.tupleof[i].stringof[4..$];
> 
> but obfuscates the code more and looks like dereferencing a null pointer.

That should have been:

      enum name = typeof(*s).tupleof[i].stringof[4..$];

artur
April 01, 2012
On 2012-04-01 08:18, Ali Çehreli wrote:
> On 03/31/2012 09:09 PM, Artur Skawina wrote:

>  >
>  > enum s = cast(S*)null;
>  > foreach (i, m; s.tupleof) {
>  > enum name = S.tupleof[i].stringof[4..$];
>  > alias typeof(m) type;
>  > writef("(%s) %s\n", type.stringof, name);
>  > }
>  >
>  > Real Programmers don't use std.traits. ;)
>  >
>  > artur
>
> Your method works but needing to iterate on a struct variable by
> s.tupleof and having to use the struct type as S.tupleof in the loop
> body is strange.

Yeah, it's a bit strange. One could think that it would be possible to use "m.stringof" but that just returns the type. Instead of using "s.tupleof" it's possible to use "typeof(S.tupleof)".

Have a look at:

https://github.com/jacob-carlborg/orange/blob/master/orange/util/Reflection.d#L212

It's possible to get the type of a field as well, based on the name:

https://github.com/jacob-carlborg/orange/blob/master/orange/util/Reflection.d#L237

-- 
/Jacob Carlborg
April 01, 2012
On 2012-04-01 11:27, Artur Skawina wrote:

> That's because the compiler won't accept "foreach (i, t; S.tupleof)" and
> "*.tupleof[i].stringof" is necessary to get the original name. This would
> have worked too:
>
>        enum name = *s.tupleof[i].stringof[4..$];
>
> but obfuscates the code more and looks like dereferencing a null pointer.
>
> artur

But it accepts "foreach (i, t; typeof(S.tupleof))".

-- 
/Jacob Carlborg
April 01, 2012
On 04/01/12 14:10, Jacob Carlborg wrote:
> On 2012-04-01 11:27, Artur Skawina wrote:
> 
>> That's because the compiler won't accept "foreach (i, t; S.tupleof)" and
> 
> But it accepts "foreach (i, t; typeof(S.tupleof))".

It does - thank you.

This means that that ugly null-to-pointer cast from my example can go, and all that's needed is:

   foreach (i, type; typeof(S.tupleof)) {
      enum name = S.tupleof[i].stringof[4..$];
      writef("(%s) %s\n", type.stringof, name);
   }

artur
April 02, 2012
On 4/1/12 8:09 PM, Jacob Carlborg wrote:
> On 2012-04-01 08:18, Ali Çehreli wrote:
>> On 03/31/2012 09:09 PM, Artur Skawina wrote:
>
>> >
>> > enum s = cast(S*)null;
>> > foreach (i, m; s.tupleof) {
>> > enum name = S.tupleof[i].stringof[4..$];
>> > alias typeof(m) type;
>> > writef("(%s) %s\n", type.stringof, name);
>> > }
>> >
>> > Real Programmers don't use std.traits. ;)
>> >
>> > artur
>>
>> Your method works but needing to iterate on a struct variable by
>> s.tupleof and having to use the struct type as S.tupleof in the loop
>> body is strange.
>
> Yeah, it's a bit strange. One could think that it would be possible to
> use "m.stringof" but that just returns the type. Instead of using
> "s.tupleof" it's possible to use "typeof(S.tupleof)".
>
> Have a look at:
>
> https://github.com/jacob-carlborg/orange/blob/master/orange/util/Reflection.d#L212
>
>
> It's possible to get the type of a field as well, based on the name:
>
> https://github.com/jacob-carlborg/orange/blob/master/orange/util/Reflection.d#L237

This is what I don't like about D. It gives you a hammer and everyone tries to solve all problems with that single hammer. Then you get duplicated code for basic stuff, like getting the type of a field, in many projects.

It's a waste of time for a developer to have to sit down and think how we can cheat the compiler or make it talk to give us something it already knows, but only having a hammer to do so.

Either put that in the language, or in the core library. But don't make people waste time.

I'd suggest sending pull request with methods that accomplish those annoyances.

On the other hand, take a look at the implementation of std.traits. Is it really a win to implement functionLinkage in D? Right here:

https://github.com/D-Programming-Language/phobos/blob/master/std/traits.d#L704

you are repeating the linkages with their names, when that information is already available to the compiler. What's the point in duplicating information? The compiler already knows it, and much better than D. It could be implemented in a much simpler way. Is it just the pride of saying "Look what I can do with my powerful compile time reflection capabilities (basically stringof in that module)?"

I'm not angry, but I don't think things are taking the correct direction...
April 02, 2012
On 2012-04-02 02:43, Ary Manzana wrote:

> This is what I don't like about D. It gives you a hammer and everyone
> tries to solve all problems with that single hammer. Then you get
> duplicated code for basic stuff, like getting the type of a field, in
> many projects.
>
> It's a waste of time for a developer to have to sit down and think how
> we can cheat the compiler or make it talk to give us something it
> already knows, but only having a hammer to do so.
>
> Either put that in the language, or in the core library. But don't make
> people waste time.
>
> I'd suggest sending pull request with methods that accomplish those
> annoyances.
>
> On the other hand, take a look at the implementation of std.traits. Is
> it really a win to implement functionLinkage in D? Right here:
>
> https://github.com/D-Programming-Language/phobos/blob/master/std/traits.d#L704
>
>
> you are repeating the linkages with their names, when that information
> is already available to the compiler. What's the point in duplicating
> information? The compiler already knows it, and much better than D. It
> could be implemented in a much simpler way. Is it just the pride of
> saying "Look what I can do with my powerful compile time reflection
> capabilities (basically stringof in that module)?"
>
> I'm not angry, but I don't think things are taking the correct direction...

I agree.

-- 
/Jacob Carlborg