Discussion:
loop through specific class members
Trass3r
2009-01-19 15:32:13 UTC
Permalink
Is there any way to loop through specific members of a class, e.g. all
public functions?
I've already seen a function getMembers in druntime's ClassInfo class
but I can't find anything related to the attributes there.
Daniel Keep
2009-01-19 15:38:49 UTC
Permalink
Post by Trass3r
Is there any way to loop through specific members of a class, e.g. all
public functions?
I've already seen a function getMembers in druntime's ClassInfo class
but I can't find anything related to the attributes there.
Assuming you're using D2, http://digitalmars.com/d/2.0/traits.html might
prove to be of interest.

-- Daniel
Trass3r
2009-01-19 17:41:49 UTC
Permalink
Post by Daniel Keep
Assuming you're using D2, http://digitalmars.com/d/2.0/traits.html might
prove to be of interest.
-- Daniel
It is indeed of interest though being not exactly what I want. Seems
like there's currently no way to get attributes like public etc.

But I think an acceptable workaround would be to use a common name
prefix for specific methods and then use that allMembers trait.
Lutger
2009-01-19 17:56:45 UTC
Permalink
Post by Trass3r
Post by Daniel Keep
Assuming you're using D2, http://digitalmars.com/d/2.0/traits.html might
prove to be of interest.
-- Daniel
It is indeed of interest though being not exactly what I want. Seems
like there's currently no way to get attributes like public etc.
But I think an acceptable workaround would be to use a common name
prefix for specific methods and then use that allMembers trait.
Indeed, perhaps it should be there. You could use getVirtualFunctions to get all public+protected member functions that are not declared final, but that's not ideal. Another possible hack: if used from a different module, you could use the 'compiles' trait with allMembers to find out if a member can be accessed.
BCS
2009-01-19 19:30:43 UTC
Permalink
Reply to Lutger,
Post by Lutger
Another possible hack: if used from a
different module, you could use the 'compiles' trait with allMembers
to find out if a member can be accessed.
you could define a template in another module that does the check and returns
the result.
Trass3r
2009-01-19 19:59:13 UTC
Permalink
Post by BCS
Reply to Lutger,
Post by Lutger
Another possible hack: if used from a
different module, you could use the 'compiles' trait with allMembers
to find out if a member can be accessed.
you could define a template in another module that does the check and
returns the result.
Well, it'd indeed be used from a different module than class, in fact
from a different package.
But this only gives the public members, right?
Daniel Keep
2009-01-19 21:02:48 UTC
Permalink
Post by Trass3r
Post by BCS
Reply to Lutger,
Post by Lutger
Another possible hack: if used from a
different module, you could use the 'compiles' trait with allMembers
to find out if a member can be accessed.
you could define a template in another module that does the check and
returns the result.
Well, it'd indeed be used from a different module than class, in fact
from a different package.
But this only gives the public members, right?
It depends on what exactly you're trying to do. Some time ago, I wrote
a library that created XML loaders for structs, and it needed to know
the names of fields. Pre-traits, this is what I used:

struct Stuff
{
int foo;
char[] bar;

alias Tuple!("foo", "bar") _fields;
}

Then I just looped over _fields.

-- Daniel
BCS
2009-01-19 21:10:24 UTC
Permalink
Reply to Daniel,
Post by Daniel Keep
It depends on what exactly you're trying to do. Some time ago, I
wrote a library that created XML loaders for structs, and it needed to
struct Stuff
{
int foo;
char[] bar;
alias Tuple!("foo", "bar") _fields;
}
Then I just looped over _fields.
-- Daniel
you can get the names even in D1.0. Not exactly clean but...

http://codepad.org/Eu16XqFu
Daniel Keep
2009-01-19 23:58:08 UTC
Permalink
Post by BCS
Reply to Daniel,
Post by Daniel Keep
It depends on what exactly you're trying to do. Some time ago, I
wrote a library that created XML loaders for structs, and it needed to
struct Stuff
{
int foo;
char[] bar;
alias Tuple!("foo", "bar") _fields;
}
Then I just looped over _fields.
-- Daniel
you can get the names even in D1.0. Not exactly clean but...
http://codepad.org/Eu16XqFu
I remember trying that, but abandoning it for some reason. This code is
getting old now; over two years now, so that might not have worked back
then.

-- Daniel
Trass3r
2009-01-20 15:01:43 UTC
Permalink
Post by BCS
you can get the names even in D1.0. Not exactly clean but...
http://codepad.org/Eu16XqFu
Yeah but only works for structs. When used with classes returns an
ExpressionTuple. Furthermore you only get the fields, I need the methods ;)
Trass3r
2009-01-19 21:41:31 UTC
Permalink
Post by Daniel Keep
Post by Trass3r
Post by BCS
Post by Lutger
Another possible hack: if used from a
different module, you could use the 'compiles' trait with allMembers
to find out if a member can be accessed.
you could define a template in another module that does the check and
returns the result.
Well, it'd indeed be used from a different module than class, in fact
from a different package.
But this only gives the public members, right?
a library that created XML loaders for structs, and it needed to know
struct Stuff
{
int foo;
char[] bar;
alias Tuple!("foo", "bar") _fields;
}
Then I just looped over _fields.
What I want is a way that doesn't require much changes to the class
itself but rather does the work in the module that actually uses the
method names.

What I'm trying to do is providing a mechanism for LuaLib to register a
whole class automatically (and probably also checking all method's
parameters for correctness). So far you have to register each method to
be used in lua individually.

So I was initially thinking about registering all protected methods or
something like that. Guess I will use the prefix solution here cause it
probably is the best way for this particular problem anyway.

But yet I'm interested in (and also somewhat impressed by) the
possibilities to do this ;)


(reminds me of my search for a way to debug template and mixin stuff)
Trass3r
2009-01-20 15:29:21 UTC
Permalink
It seems like there is no way to automatically get the class methods in
D1 currently?!
__traits isn't supported, std.traits doesn't give anything usable,
.tupleof only gets the fields (plus only giving an ExpressionTuple for
classes).
Jarrett Billingsley
2009-01-20 16:50:44 UTC
Permalink
It seems like there is no way to automatically get the class methods in D1
currently?!
__traits isn't supported, std.traits doesn't give anything usable, .tupleof
only gets the fields (plus only giving an ExpressionTuple for classes).
You're correct. __traits was introduced in D2 mostly because D1's
compile-time introspection is so paltry.
Trass3r
2009-01-20 19:13:00 UTC
Permalink
Post by Jarrett Billingsley
It seems like there is no way to automatically get the class methods in D1
currently?!
__traits isn't supported, std.traits doesn't give anything usable, .tupleof
only gets the fields (plus only giving an ExpressionTuple for classes).
You're correct. __traits was introduced in D2 mostly because D1's
compile-time introspection is so paltry.
Yeah, __traits works quite well to get the function names, but I still
can't manage to get the corresponding function object to pass it to
ParameterTypeTuple (for checking the parameters for correctness).

Tried using ClassInfo's getMembers function but that apparently doesn't
work.

Base base = new Base;
auto members = __traits(allMembers, typeof(base));
foreach(m; members)
writefln(base.classinfo.getMembers(m).length);

gives 0 for every member :(
Jarrett Billingsley
2009-01-20 19:26:58 UTC
Permalink
Yeah, __traits works quite well to get the function names, but I still can't
manage to get the corresponding function object to pass it to
ParameterTypeTuple (for checking the parameters for correctness).
Ah, that's what __traits(getMember) is for. You pass it an
object/class and a string of the member name, and it gives you what it
corresponds to.
Tried using ClassInfo's getMembers function but that apparently doesn't
work.
Base base = new Base;
auto members = __traits(allMembers, typeof(base));
foreach(m; members)
writefln(base.classinfo.getMembers(m).length);
gives 0 for every member :(
I think that's actually a bug. DMD doesn't generate that info for
some reason. I think LDC does.
Trass3r
2009-01-20 19:30:42 UTC
Permalink
Post by Jarrett Billingsley
Yeah, __traits works quite well to get the function names, but I still can't
manage to get the corresponding function object to pass it to
ParameterTypeTuple (for checking the parameters for correctness).
Ah, that's what __traits(getMember) is for. You pass it an
object/class and a string of the member name, and it gives you what it
corresponds to.
OMG, I've already seen this function in the docs some time ago but
somehow I couldn't remember.
Many thanks!
Christopher Wright
2009-01-21 00:42:46 UTC
Permalink
Post by Jarrett Billingsley
It seems like there is no way to automatically get the class methods in D1
currently?!
__traits isn't supported, std.traits doesn't give anything usable, .tupleof
only gets the fields (plus only giving an ExpressionTuple for classes).
You're correct. __traits was introduced in D2 mostly because D1's
compile-time introspection is so paltry.
But unless you're doing very basic stuff or only touching a few classes,
compile-time introspection ends up being quite costly. I want runtime
reflection for that reason.

On the other hand, you can get the non-final, non-private methods of a
class with something like:
foreach (member; __traits (allMembers, Class))
{
foreach (overload; __traits (getVirtualFunctions, Class, member))
{
// do stuff
}
}

Naturally, __traits and foreach don't mix, so you'll have to use
template recursion. This is pretty damn ugly.

Note that getVirtualFunctions returns methods that are not virtual. You
need to use isVirtualFunction to determine whether a method is virtual.
Jarrett Billingsley
2009-01-21 01:56:20 UTC
Permalink
Naturally, __traits and foreach don't mix, so you'll have to use template
recursion. This is pretty damn ugly.
I never understood why __traits likes returning arrays so much. Why not tuples?
Note that getVirtualFunctions returns methods that are not virtual. You need
to use isVirtualFunction to determine whether a method is virtual.
Lol.
Trass3r
2009-01-21 11:48:07 UTC
Permalink
Post by Christopher Wright
On the other hand, you can get the non-final, non-private methods of a
foreach (member; __traits (allMembers, Class))
{
foreach (overload; __traits (getVirtualFunctions, Class, member))
{
// do stuff
}
}
Naturally, __traits and foreach don't mix, so you'll have to use
template recursion. This is pretty damn ugly.
But why doesn't that work? It doesn't even work when forced to run at
compile time by CTFE.
Had a look at the compiler code, it uses the same mechanism as
pragma(msg, for example, so shouldn't something like the above
theoretically be possible?
Sergey Gromov
2009-01-26 00:49:35 UTC
Permalink
Post by Trass3r
Post by Christopher Wright
On the other hand, you can get the non-final, non-private methods of a
foreach (member; __traits (allMembers, Class))
{
foreach (overload; __traits (getVirtualFunctions, Class, member))
{
// do stuff
}
}
Naturally, __traits and foreach don't mix, so you'll have to use
template recursion. This is pretty damn ugly.
But why doesn't that work? It doesn't even work when forced to run at
compile time by CTFE.
Had a look at the compiler code, it uses the same mechanism as
pragma(msg, for example, so shouldn't something like the above
theoretically be possible?
foreach() is a runtime construct. It may be *interpreted* at
compile-time, but it's interpreted as if it were run time nevertheless.
It dynamically changes the value of 'member' variable.

pragma() and __traits() are compile-time constructs. They are fully and
statically expanded before anything is being interpreted and cannot
handle dynamically changing variables.
BCS
2009-01-26 04:50:10 UTC
Permalink
Hello Sergey,
Post by Sergey Gromov
foreach() is a runtime construct. It may be *interpreted* at
compile-time, but it's interpreted as if it were run time
nevertheless. It dynamically changes the value of 'member' variable.
OTOH a foreach on a tuple is a compile time construct, but it is a distinct
construct from the array foreach and is not what you are using (If i'm reading
stuff correctly)
Sergey Gromov
2009-01-27 02:26:28 UTC
Permalink
Post by BCS
Hello Sergey,
Post by Sergey Gromov
foreach() is a runtime construct. It may be *interpreted* at
compile-time, but it's interpreted as if it were run time
nevertheless. It dynamically changes the value of 'member' variable.
OTOH a foreach on a tuple is a compile time construct, but it is a distinct
construct from the array foreach and is not what you are using (If i'm reading
stuff correctly)
I somehow missed that in the specs. Definitely, this works:

template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
foreach (a; Tuple!(int, char, long))
pragma(msg, a.stringof);
}

but this doesn't:

template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
foreach (a; Tuple!("a", "b", "c"))
pragma(msg, a);
}

$ dmd -c test.d
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'

*This* seems like a bug to me.
BCS
2009-01-27 04:35:29 UTC
Permalink
Hello Sergey,
Post by Sergey Gromov
template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
foreach (a; Tuple!("a", "b", "c"))
pragma(msg, a);
}
$ dmd -c test.d
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'
*This* seems like a bug to me.
try indexing it:

template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
alias Tuple!("a", "b", "c") Tpl;
foreach (i,a; Tpl)
pragma(msg, Tpl[i]);
}

(BTW that is a known bug)
Trass3r
2009-03-29 22:22:16 UTC
Permalink
Post by BCS
Hello Sergey,
Post by Sergey Gromov
template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
foreach (a; Tuple!("a", "b", "c"))
pragma(msg, a);
}
$ dmd -c test.d
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'
test.d(8): Error: string expected for message, not 'a'
*This* seems like a bug to me.
template Tuple(T...)
{
alias T Tuple;
}
void foo()
{
alias Tuple!("a", "b", "c") Tpl;
foreach (i,a; Tpl)
pragma(msg, Tpl[i]);
}
(BTW that is a known bug)
Do you know the bug number?

Loading...