www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Help with reflection?

reply "bitwise" <nothx gmail.com> writes:
Hey, I am trying to put together a basic reflection system, but 
I've run into a problem. I'm not sure if this is a bug or not.

I am trying to store method information about a class. So 
basically, I am using Traits to go through all members of the 
class, and then trying to create delegates to each member. Then, 
at runtime, the function will be dynamically invoked by changing 
the ".ptr" property of the delegate, and calling it.

These are the errors I am getting from these 3 lines from the 
code below:

//del.ptr = null;
-Error: CTFE internal error: unsupported assignment del.ptr = 
null (TestD)

//del.funcptr = mixin("&T." ~ member);
-Error: CTFE internal error: unsupported assignment del.funcptr = 
& someFunction (TestD)
-Also, why has 'T' been removed after mixin?

//del.funcptr = mixin("&SomeClass." ~ member);
-Error: CTFE internal error: unsupported assignment del.funcptr = 
& someFunction (TestD)

Any insight here would be much appreciated!



abstract class Method_Info
{
	string name();
	void invoke(Object obj, ...);
}

class Method_Info_t(T, D) : Method_Info
{
	alias _paramTypes = ParameterTypeTuple!D;
	
	string _name;
	private D _del;
	
	this(string name, D del) {
		_name =  name;
		_del = del;
	}
	
	this(string name) {
		_name =  name;
	}
	
	override string name() {
		return _name;
	}
	
	override void invoke(Object obj, ...)
	{
		if(_arguments.length != _paramTypes.length)
			throw new Exception("Invoke Failed: Invalid Argument Count");
		
		if(obj is null || typeid(T) != typeid(obj))
			throw new Exception("Invoke Failed: Invalid Object");
		
		foreach(i, ty; _paramTypes)
		{
			if(_arguments[i] != typeid(_paramTypes[i]))
				throw new Exception("Invoke Failed: Invalid Argument Type - " 
~
				                    "Expected \'" ~ _paramTypes[i].stringof ~ 
"\', " ~
				"Received \'" ~ _arguments[i].toString ~ "\'.");
		}
		
		_paramTypes tup;
		
		foreach(i, ty; _paramTypes)
			tup[i] = va_arg!(_paramTypes[i])(_argptr);

		_del.ptr = cast(void*)obj;
		_del(tup);
	}
}

static Method_Info[] _createMethodInfo(T)()
{
	enum allMembers = __traits(allMembers, T);
	
	int ct = 0;
	
	Method_Info[] ret = new Method_Info[allMembers.length];
	
	foreach(member; allMembers)
	{
		enum compiled = __traits(compiles, {
			alias typeof(__traits(getMember, T, member)) func_type;
			alias ReturnType!func_type ret_type;
			alias ParameterTypeTuple!func_type params;
			ret_type delegate(params) del;
		});
		
		static if(compiled)
		{
			alias typeof(__traits(getMember, T, member)) func_type;
			
			enum callable = isCallable!func_type;
			
			static if(callable)
			{
				alias ReturnType!func_type return_type;
				alias ParameterTypeTuple!func_type params;
				
				return_type delegate(params) del;

				//del.ptr = null;
				//del.funcptr = mixin("&T." ~ member);
				//del.funcptr = mixin("&SomeClass." ~ member);

				ret[ct++] = new Method_Info_t!(T, typeof(del))(member, del);
			}
		}
	}
	
	return ret[0 .. ct];
}

class SomeClass
{
	int someFunction(int x)
	{
		writeln(x);
		return x * 2;
	}
	
	void otherFunction(string s, int a, float b)
	{
		writeln(s);
		writeln(a);
		writeln(b);
	}

	static Method_Info[] _methodInfo = 
_createMethodInfo!(SomeClass)();
}
Oct 10 2014
parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Friday, 10 October 2014 at 17:00:01 UTC, bitwise wrote:
 Hey, I am trying to put together a basic reflection system, but 
 I've run into a problem. I'm not sure if this is a bug or not.

 I am trying to store method information about a class. So 
 basically, I am using Traits to go through all members of the 
 class, and then trying to create delegates to each member. 
 Then, at runtime, the function will be dynamically invoked by 
 changing the ".ptr" property of the delegate, and calling it.

 These are the errors I am getting from these 3 lines from the 
 code below:

 //del.ptr = null;
 -Error: CTFE internal error: unsupported assignment del.ptr = 
 null (TestD)

 //del.funcptr = mixin("&T." ~ member);
 -Error: CTFE internal error: unsupported assignment del.funcptr 
 = & someFunction (TestD)
 -Also, why has 'T' been removed after mixin?

 //del.funcptr = mixin("&SomeClass." ~ member);
 -Error: CTFE internal error: unsupported assignment del.funcptr 
 = & someFunction (TestD)
Without looking too much at your code, the internal error seems to definitely imply a bug, I recommend reporting it on the issue tracker. Also, I made a Reflection module as well, you may find some of the code useful (such as getting methods at compile-time and invoking at run-time): https://shardsoft.com/stash/projects/SHARD/repos/shardtools/browse/source/ShardTools/Reflection.d?until=45ded3019f3f05d7b68e5746d (links to a specific commit, after that I started trying to unify runtime and compile-time reflection into one API which gave me some troubles). It can get a bit complicated depending on what your needs are, as you would need to deal with vtables and interfaces depending on what you want to support. My implementation definitely isn't perfect, but works for my usage.
Oct 10 2014
parent "bitwise" <nothx gmail.com> writes:
On Friday, 10 October 2014 at 17:43:13 UTC, Kapps wrote:
 On Friday, 10 October 2014 at 17:00:01 UTC, bitwise wrote:
 Hey, I am trying to put together a basic reflection system, 
 but I've run into a problem. I'm not sure if this is a bug or 
 not.

 I am trying to store method information about a class. So 
 basically, I am using Traits to go through all members of the 
 class, and then trying to create delegates to each member. 
 Then, at runtime, the function will be dynamically invoked by 
 changing the ".ptr" property of the delegate, and calling it.

 These are the errors I am getting from these 3 lines from the 
 code below:

 //del.ptr = null;
 -Error: CTFE internal error: unsupported assignment del.ptr = 
 null (TestD)

 //del.funcptr = mixin("&T." ~ member);
 -Error: CTFE internal error: unsupported assignment 
 del.funcptr = & someFunction (TestD)
 -Also, why has 'T' been removed after mixin?

 //del.funcptr = mixin("&SomeClass." ~ member);
 -Error: CTFE internal error: unsupported assignment 
 del.funcptr = & someFunction (TestD)
Without looking too much at your code, the internal error seems to definitely imply a bug, I recommend reporting it on the issue tracker. Also, I made a Reflection module as well, you may find some of the code useful (such as getting methods at compile-time and invoking at run-time): https://shardsoft.com/stash/projects/SHARD/repos/shardtools/browse/source/ShardTools/Reflection.d?until=45ded3019f3f05d7b68e5746d (links to a specific commit, after that I started trying to unify runtime and compile-time reflection into one API which gave me some troubles). It can get a bit complicated depending on what your needs are, as you would need to deal with vtables and interfaces depending on what you want to support. My implementation definitely isn't perfect, but works for my usage.
Thanks for the help! I tried a workaround, which was to store the function pointer and then assign it to the delegate at runtime, at which point the compiler told me that the function pointer was not a compile time constant. So that may have been the root of the problem. Finally, I was able to get my code working by accessing the vtable directly like your code. My code is posted here: https://github.com/bitwise-github/D-Reflection
Oct 10 2014