www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - I give up! I tried to create a reflection library but D's traits are

reply Alex <AJ gmail.com> writes:
The follow code is a reflection library that unifies all the junk 
in std.traits and __traits and makes it using reflection as if it 
were runtime and one can even keep the info around at runtime...

But D's type system is too screwed up. Fields and members are not 
treated as types in and have themselves so 1. Building a compact 
code base is impossible. 2. The type system sometimes requires 
using alias and sometimes a type yet there is no way to unify the 
two to reduce code bloat and everything must be duplicated. 3. 
The Type's do not carry their moodules and import automatically 
so the symbols can be used. This requires getting the module and 
importanting things. Further more certain _traits want a this 
object to work for fields and others don't but none of the 
methods seem sensical. 4. If a symbol is private CT __traits 
fails..., again non-sensical.

The following code works flawlessly for aggregates. I'm able to 
get all the proper info but I've spend 10x as long on this code 
just trying to make sense of fields and members and can't get 
anything to work properly in any way that makes sense.

I will stop working on this unless someone can tell me how I'm 
suppose to fill in the appropriate info for fields and members or 
fixes the compiler to work corretly.


The idea here is that instead of having to use D's terrible 
std.traits and __traits(which are hard to remember and nuanced)), 
the code simply wraps all the calls and get's all the meta info 
in to a oop class hierarchy(at CT using CTFE).

auto CR = Reflect!(cDerived!int);

CR is a class that we can then use to find out all the meta info. 
CR.TypeName, CR.MangledName, CR.DType, CR.ModuleName, 
CR.Attributes, etc...

Everything gets unified with standard oop and it's all their at 
compile time. Since it just wraps most of the std.traits and 
__traits, it only provides a common and uniform interface. It 
reduces a lot of the boil plate code of having to deal with this 
stuff(Which is why I attempted it). I got tired of having to do 
the same things over and over such as recurse through the type 
hierarchy to find something. Here one can use CTFE and runtime 
programming and avoid most of the CT code. (although since it 
collects all the information at once it probably is extremely 
inefficent).

Ideally D would have provided us with a simple Type that 
contained all this info from the get go.

I'd hate abandonding this since it is working quite well and 
virtually has no issues except the fields and members problem 
which is pretty much making the whole program useless(sure it 
works on functions and aggregates but fields and members are 
probaby even more important).

Hopefully someone can figure out how to make it work.



int main()
{
	pragma(msg, printModelHierarchy(Reflect!(cDerived!int)));
}

This is the output of the program, it shows the "complete"(if the 
issues didn't exist) reflection of cDerived.


	Id = cDerived
	TypeName = cDerived!int
	FullName = mModel.cDerived!(int)
	ModuleName = mModel
	MangledName = C6mModel__T8cDerivedTiZQm
	Protection = public
	Body =
	Uses = []
	Attributes = [
		sAttributeReflection("fdsa", "string"),
		sAttributeReflection("8", "int")
	]
	IsNested = false
	IsInnerClass = false
	HasUnsharedAliasing = true
	HasNested = false
	HasIndirections = true
	HasElaborateDestructor = false
	HasElaborateCopyConstructor = false
	HasElaborateAssign = false
	HasAliasing = true
	TypeParameters = []
	AliasThis = [sTypeReflection("type", "cType")]
	NestedAggregates = []
	DerivedClasses = []
	Fields = [
		Id = type
		TypeName = cDerived!int.type
		FullName = mModel.cDerived!(int).type
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection =
		Body =
		Uses = []
		Attributes = []
		DType = field
		,
		Id = testField1
		TypeName = cDerived!int.testField1
		FullName = mModel.cDerived!(int).testField1
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection =
		Body =
		Uses = []
		Attributes = []
		DType = field
		,
		Id = testField2
		TypeName = cDerived!int.testField2
		FullName = mModel.cDerived!(int).testField2
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection =
		Body =
		Uses = []
		Attributes = []
		DType = field
	]
	Methods = [
		Id =  property int()
		TypeName = cDerived!int.ValueProp
		FullName = mModel.cDerived!(int).ValueProp
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection =
		Body =
		Uses = []
		Attributes = []
		DType = delegate
		Overloads = [
			Id =
			TypeName =
			FullName =
			ModuleName =
			MangledName =
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = function
			Signature =  property int()
			NumArgs = 0
			Linkage = D
			Parameters = []
		]
	]
	InheritedInterfaces = [
		Id = iX
		TypeName = iX
		FullName = mModel.iX
		ModuleName = mModel
		MangledName = C6mModel2iX
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id =
			TypeName = iX.
			FullName = mModel.iX.
			ModuleName = mModel
			MangledName = C6mModel2iX
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = []
		DType = interface
		InheritedInterfaces = []
		,
		Id = iY
		TypeName = iY
		FullName = mModel.iY
		ModuleName = mModel
		MangledName = C6mModel2iY
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id =
			TypeName = iY.
			FullName = mModel.iY.
			ModuleName = mModel
			MangledName = C6mModel2iY
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = []
		DType = interface
		InheritedInterfaces = []
	]
	DType = class
	IsAbstract = false
	Alignment = 8
	InheritedClasses = [
		Id = cBase
		TypeName = cBase
		FullName = mModel.cBase
		ModuleName = mModel
		MangledName = C6mModel5cBase
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id = testField
			TypeName = cBase.testField
			FullName = mModel.cBase.testField
			ModuleName = mModel
			MangledName = C6mModel5cBase
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = [
			Id = cBase(iBase c)
			TypeName = cBase.fooBase
			FullName = mModel.cBase.fooBase
			ModuleName = mModel
			MangledName = C6mModel5cBase
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = delegate
			Overloads = [
				Id =
				TypeName =
				FullName =
				ModuleName =
				MangledName =
				Protection =
				Body =
				Uses = []
				Attributes = []
				DType = function
				Signature = cBase(iBase)
				NumArgs = 1
				Linkage = D
				Parameters = []
			]
		]
		InheritedInterfaces = [
			Id = iBase
			TypeName = iBase
			FullName = mModel.iBase
			ModuleName = mModel
			MangledName = C6mModel5iBase
			Protection = public
			Body =
			Uses = []
			Attributes = [
				sAttributeReflection("fdsa", "string"),
				sAttributeReflection("4", "int")
			]
			IsNested = false
			IsInnerClass = false
			HasUnsharedAliasing = true
			HasNested = false
			HasIndirections = true
			HasElaborateDestructor = false
			HasElaborateCopyConstructor = false
			HasElaborateAssign = false
			HasAliasing = true
			TypeParameters = []
			AliasThis = []
			NestedAggregates = []
			DerivedClasses = []
			Fields = [
				Id =
				TypeName = iBase.
				FullName = mModel.iBase.
				ModuleName = mModel
				MangledName = C6mModel5iBase
				Protection =
				Body =
				Uses = []
				Attributes = []
				DType = field
			]
			Methods = [
				Id = iBase(iBase)
				TypeName = iBase.fooBase
				FullName = mModel.iBase.fooBase
				ModuleName = mModel
				MangledName = C6mModel5iBase
				Protection =
				Body =
				Uses = []
				Attributes = []
				DType = delegate
				Overloads = [
					Id =
					TypeName =
					FullName =
					ModuleName =
					MangledName =
					Protection =
					Body =
					Uses = []
					Attributes = []
					DType = function
					Signature = iBase(iBase)
					NumArgs = 1
					Linkage = D
					Parameters = []
				]
			]
			DType = interface
			InheritedInterfaces = []
		]
		DType = class
		IsAbstract = false
		Alignment = 4
		InheritedClasses = [Object]
	]












module mReflect;

import mExTraits;
import std.meta, std.conv, std.typecons, std.typetuple, 
std.string, std.algorithm, std.range, std.variant;


struct sTypeReflection
{
	string Name;
	string Type;
	this(string n, string t) { Name = n; Type = t; }
}


struct sAttributeReflection
{
	string Value;
	string Type;
	this(string v, string t) { Value = v; Type = t; }
}



/*
	Encapsulation of basic reflection
*/
abstract class cBaseReflection
{
	// Declared in reverse order for proper output
	sAttributeReflection[] Attributes;			// Attributes on the type
	cBaseReflection[] Uses;						// Known types that use this type 
as a field, parameter, return type, etc
	string Body;								// D does not allow getting a body but this 
is included if it ever does
	string Protection;
	string MangledName;
	string ModuleName;							// The module name
	string FullName;							// The fully qualified name
	string TypeName;							// The type name
	string Id;									// The id  = __traits(identifier,T)
	
	string DType() { return "Unknown"; }

	auto Reflect(alias T)()
	{
		
		Id = __traits(identifier, T);	
		static if (__traits(compiles, T.stringof)) TypeName = 
T.stringof;
		static if (__traits(compiles, moduleName!T)) ModuleName = 
moduleName!(T);
		static if (__traits(compiles, fullyQualifiedName!T)) FullName = 
fullyQualifiedName!(T);
		static if (__traits(compiles, mangledName!T)) MangledName = 
mangledName!T;
		static if (__traits(compiles, __traits(getProtection, T))) 
Protection = __traits(getProtection, T);
		// Get the attributes for the type
		static if (__traits(compiles, __traits(getAttributes, T)))
			static foreach(a;  __traits(getAttributes, T)) Attributes ~= 
sAttributeReflection(to!string(a), typeof(a).stringof);

		return this;
	}
}

/*
Encapsulates an Enumeration Reflection
*/
class cEnumReflection : cBaseReflection
{
	static struct sValueReflection
	{
		string Name;
		string Value;		
		sAttributeReflection[] Attributes;			
		this(string n, string v) { Name = n; Value = v; }
	} sValueReflection[] Values;

	string BaseType;
	override string DType() { return "enum"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;
		
		//BaseType = TypeOf(T).stringof;
		static foreach(e; EnumMembers!T)
		{{
			Values ~= sValueReflection(to!string(e), e.stringof);
			mixin("alias E = __traits(getAttributes, 
T."~to!string(e)~");");			
			static foreach(a; E) 						
				Values[$-1].Attributes ~= sAttributeReflection(to!string(a), 
typeof(a).stringof);							
		}}

		return this;
	}
}







/*
	Encapsulates an Aggregate Type
*/
abstract class cAggregateReflection : cBaseReflection
{		

	
	
	cMethodReflection[] Methods;							// The methods
	cFieldReflection[] Fields;								// The fields
	cClassReflection[] DerivedClasses;						// Known immedate 
derived class that derive this type
	cAggregateReflection[] NestedAggregates;				// Any nested 
aggregates
	sTypeReflection[] AliasThis;							// The Alias This	
	sTypeReflection[] TypeParameters;						// Any type parameters 
used.
	bool HasAliasing;
	bool HasElaborateAssign;
	bool HasElaborateCopyConstructor;
	bool HasElaborateDestructor;
	bool HasIndirections;
	bool HasNested;
	bool HasUnsharedAliasing;
	bool IsInnerClass;
	bool IsNested;
	
	override string DType() { return "Unknown"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		// Get the Alias This
		static foreach(a;  __traits(getAliasThis, T))
		{{
			mixin("import "~moduleName!T~";");		
			mixin("AliasThis ~= sTypeReflection(to!string(a), 
typeof("~moduleName!T~"."~T.stringof~"."~to!string(a)~").stringof);");
		}}

		HasAliasing = hasAliasing!T;
		HasElaborateAssign = hasElaborateAssign!T;
		HasElaborateCopyConstructor = hasElaborateCopyConstructor!T;
		HasElaborateDestructor = hasElaborateDestructor!T;
		HasIndirections = hasIndirections!T;
		HasNested = hasNested!T;
		HasUnsharedAliasing = hasUnsharedAliasing!T;
		static if (__traits(compiles, isInnerClass!T)) IsInnerClass = 
isInnerClass!T;
		static if (__traits(compiles, isNested!T)) IsNested = 
isNested!T;
		
	
		// Get the fields
		alias n = FieldNameTuple!T;
		static foreach(k, f; std.traits.Fields!T)
		{{
			Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]);
		}}


		static foreach(k, m; __traits(derivedMembers, T))
		{{
			static if (__traits(compiles, __traits(getVirtualMethods, T, 
m)))
			static foreach(j, v; typeof(__traits(getVirtualMethods, T, 
m)))		
			{{
				//pragma(msg, T.stringof, " --- ", v.stringof);
				Methods ~= (new cMethodReflection()).Reflect!(T, m);

			}}
		}}
		return this;
	}
}


/*
Encapsulates a Field Reflection
*/
class cFieldReflection : cBaseReflection
{
	override string DType() { return "field"; }

	auto Reflect(alias T, string name)()
	{		
		// There is no field type so must manually construct and 
duplicate code, else we could reflect directly

		Id = name;	
		static if (__traits(compiles, TypeName = T.stringof)) TypeName 
= T.stringof~"."~name;
		ModuleName = moduleName!(T);
		FullName = fullyQualifiedName!(T)~"."~name;
		MangledName = mangledName!T;
		//mixin(`FullName = mangledName!(`~T.stringof~`.`~name~`);`);
		/*
		
		
		
		
		

		*/

		

		// Get the attributes for the field
		import mModel;
		static if (__traits(compiles, mixin(`static if 
(__traits(compiles, __traits(getAttributes, 
`~T.stringof~`.`~name~`)))`)))
		mixin(`static if (__traits(compiles, __traits(getAttributes, 
`~T.stringof~`.`~name~`))) pragma(msg, "fdadsf"); `);
		//static if (aFound) { mixin(`static foreach(a;  
__traits(getAttributes, `~T.stringof~`.`~name~`)) Attributes ~= 
sAttributeReflection(to!string(a), typeof(a).stringof);`);}

		//pragma(msg, T);
		//T t;
		//mixin("alias X = t."~name~";");
		//super.Reflect!X;

		return this;
	}
}


/*
Encapsulates a Method Reflection
*/
class cMethodReflection : cBaseReflection
{
	cFunctionReflection[] Overloads;

	override string DType() { return "delegate"; }

	auto Reflect(alias T, string name)()
	{
		
		// There is no method type so must manually construct and 
duplicate code, else we could reflect directly
		//super.Reflect!T;

		mixin(`import `~moduleName!T~`;`);	// Must import the type to 
be able to inspect it(fragile and bizzare and ridiculous)
		T t = T.init;


		Id = name;	
		static if (__traits(compiles, TypeName = T.stringof)) TypeName 
= T.stringof~"."~name;
		ModuleName = moduleName!(T);
		FullName = fullyQualifiedName!(T)~"."~name;
		MangledName = mangledName!T;
		//mixin(`MangledName = 
mangledName!(`~T.stringof~`).`~name~`);`);

		static if (__traits(compiles, __traits(getOverloads, T, name)))
		{			
			static foreach (f; typeof(__traits(getOverloads, T, name)))
			{
				//pragma(msg, f, " --- ", f.stringof);
				Overloads ~= (new cFunctionReflection()).Reflect!(Alias!f);
				//pragma(msg, name, " --- ", (t.f).stringof);
				mixin(`Id = "`~f.stringof~`";`);
				/*

				mixin(`Signature = 
FunctionTypeOf!(`~T.stringof~`.`~name~`).stringof;`);
				pragma(msg, `Signature = 
FunctionTypeOf!(`~T.stringof~`.`~name~`).stringof;`);
				NumArgs = arity!T;
				Linkage = functionLinkage!T;
				VariadicFunctionStyle = variadicFunctionStyle!T;
				mixin(`Protection = __traits(getProtection, 
(`~T.stringof~`).`~name~`);`);
				*/
			}
		}

		return this;
	}
}


/*
	Encapsulates a Union Reflection
*/
class cUnionReflection : cAggregateReflection
{
	override string DType() { return "union"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;
	}
}


/*
	Encapsulates a Struct Reflection
*/
class cStructReflection : cAggregateReflection
{
	override string DType() { return "struct"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;

	}
}



/*
	Encapsulates a Interface Reflection
*/
class cInterfaceReflection : cAggregateReflection
{	
	cInterfaceReflection[] InheritedInterfaces;				// The inherited 
interfaces
	override string DType() { return "interface"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;
		
		// Reflect on inherited interfaces
		static foreach(t; BaseTypeTuple!T)
			static if (isInterface!t)
				InheritedInterfaces ~= (new 
cInterfaceReflection()).Reflect!(t);


		return this;
	}
}



/*
	Encapsulates a Class Reflection
*/
class cClassReflection : cInterfaceReflection
{


	
	cClassReflection[] InheritedClasses;					// Any Inherited 
class(D only allows single class inheritance, but we allow for 
possibly more for uniformity)	
	int Alignment;
	bool IsAbstract = false;
	override string DType() { return "class"; }
	auto Reflect(alias T)()
	{
		super.Reflect!T;

		IsAbstract = isAbstractClass!T;
		Alignment = classInstanceAlignment!T;

		// Reflect on inherited class
		static foreach(t; BaseTypeTuple!T)
			static if (isClass!t)
				InheritedClasses ~= (new cClassReflection()).Reflect!(t);


		return this;
	}
}






/*
	Encapsulates a Function Reflection
*/
class cFunctionReflection : cBaseReflection
{
	static struct sParameterReflection
	{
		string Name;
		string Type;
		string DefaultValue = "none";
		string DefaultValueType = "void";
		struct sStorageClass
		{
			static foreach(a; EnumMembers!ParameterStorageClass)
				mixin(`bool Is`~strip(capitalize(to!string(a)),"_")~";");
		} sStorageClass StorageClass;
		this(string n, string t, string dv, string dt) { Name = n; Type 
= t; DefaultValue = dv; DefaultValueType = dt; }
	}

	static struct sFunctionAttributes
	{
		static foreach(a; EnumMembers!FunctionAttribute)
			mixin(`bool Is`~strip(capitalize(to!string(a)),"_")~";");
	} sFunctionAttributes FunctionAttributes;
	sParameterReflection[] Parameters;
	sTypeReflection ReturnType;



	
	string Linkage;
	Variadic VariadicFunctionStyle;
	int NumArgs;
	string Signature;
	override string DType() { return "function"; }

	auto Reflect(T)()
	{
		Signature = FunctionTypeOf!T.stringof;
		NumArgs = arity!T;
		Linkage = functionLinkage!T;
		VariadicFunctionStyle = variadicFunctionStyle!T;

		return this;
	}

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		Signature = FunctionTypeOf!T.stringof;
		NumArgs = arity!T;
		Linkage = functionLinkage!T;
		VariadicFunctionStyle = variadicFunctionStyle!T;
		
		
		static foreach(a; EnumMembers!FunctionAttribute)
			mixin(`FunctionAttributes.Is`~strip(capitalize(to!string(a)),"_")~" =
(functionAttributes!T & FunctionAttribute."~to!string(a)~") != 0;");

		static foreach(k, a; std.traits.Parameters!T)
		{{
			static if (__traits(compiles, 
typeof(ParameterDefaults!T[k]).stringof))
				enum dt = typeof(ParameterDefaults!T[k]).stringof;
			else
				enum dt = "void";

			Parameters ~= 
sParameterReflection(ParameterIdentifierTuple!T[k], a.stringof, 
ParameterDefaults!T[k].stringof, dt);			
			static foreach(j, a; EnumMembers!ParameterStorageClass)
				mixin(`Parameters[k].StorageClass.Is`~strip(capitalize(to!string(a)),"_")~"
= (ParameterStorageClassTuple!T[k] & ParameterStorageClass."~to!string(a)~") !=
0;");
		}}

		ReturnType = sTypeReflection("", 
std.traits.ReturnType!T.stringof);

	}
}

/*
Encapsulates a Delegate Reflection
*/
class cDelegateReflection : cFunctionReflection
{
	override string DType() { return "delegate"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;
	}
}








/*
	Takes a type and builds it's inheritance and uses hierarchy.
*/
auto Reflect(Ts...)()
{
	alias T = Ts[0];
	
	static foreach(t; ["class", "interface", "struct", "union", 
"function", "delegate", "enum"])
		mixin("static if (is"~capitalize(t)~"!(T)) { auto model = new 
c"~capitalize(t)~"Reflection(); model.Reflect!T(); } ");


	return model;
}




auto printModelHierarchy(T, int depth = 1)(T model)
{static if (depth > 20) return "ERROR"; else {
	auto res = "";
	auto tab = "\t".replicate(depth);
	if (model.TypeName == "Object") return "Object";
	
	// Print out basic info, allMembers returns funky as it returns 
the most derived first from top to bottom. Generally we want to 
display the least derived from top to bottom. Reversing gets us 
bottom to top though so we must declare them in reverse order in 
the types.
	static foreach(k, m; [Reverse!(__traits(allMembers, T))])
	{{
		static if (!canFind(["factory", "Monitor", "opEquals", "opCmp", 
"toHash", "toString", "Reflect"], m))		// Ignore these
		{
			mixin(`alias Q = TypeOf!(T.`~m~`);`);	
			
			static if (isPrimitive!Q || isString!Q)																	// 
Print primitives
				mixin(`res ~= tab~m~" = 
"~to!string(model.`~m.stringof[1..$-1]~`)~"\n";`);	
			else static if (__traits(compiles, is(ReturnType!Q == string)) 
&& is(ReturnType!Q == string))			// Print functions that return 
strings
			{			
				mixin(`res ~= tab~m~" = 
"~model.`~m.stringof[1..$-1]~`()~"\n";`);	
			}
			else																									// Print other types
			{				
				static if (is(Q U : U[]) && !is(U == Object))														// 
Print Arrays, handling recursion
				{{										
					mixin("auto arr = model."~m~";");
					if (arr.length > 0)
					{
						string[] r;
						foreach(a; arr)
						{
								static if (is(U : cBaseReflection))
									r ~= printModelHierarchy!(U, depth + 1)(a).strip("\t\n 
,");								
								else
									r ~= to!string(a).strip("\t\n ,");
						}

						// Prepare results
						auto q = r.join(",");
						if (q.length > 60 || r.canFind("\n") || r.canFind(","))
							static if (is(U : cBaseReflection))
								q = 
"\n"~tab~"\t"~r.join("\n"~tab~"\t,\n"~tab~"\t")~"\n"~tab;
							else
								q = "\n"~tab~"\t"~r.join(",\n"~tab~"\t")~"\n"~tab;
						res ~= tab~m~" = ["~q~"]\n";
					} else res ~= tab~m~" = []\n";
					
				}}
			}
		
		
		}
	}}

	return res;
}}






















module mExTraits;

public import std.traits;
public import std.meta;


enum isPrimitive(T) = is(T == bool) || is(T == byte) || is(T == 
cdouble) || is(T == creal) || is(T == cfloat) || is(T == char) || 
is(T == dchar) ||
						is(T == double) || is(T == float) || is(T == idouble) || 
is(T == ifloat) || is(T == int) || is(T == ireal) || is(T == 
long) || is(T == real) ||
						is(T == short) || is(T == ubyte)  || is(T == uint) || is(T 
== ulong) || is(T == ushort) || is(T == wchar);
enum isPrimitive(alias T) = is(T == bool) || is(T == byte) || 
is(T == cdouble) || is(T == creal) || is(T == cfloat) || is(T == 
char) || is(T == dchar) ||
						is(T == double) || is(T == float) || is(T == idouble) || 
is(T == ifloat) || is(T == int) || is(T == ireal) || is(T == 
long) || is(T == real) ||
						is(T == short) || is(T == ubyte) || is(T == uint) || is(T 
== ulong) || is(T == ushort) || is(T == wchar);
enum Is(A,B) = is(A == B);
enum isFloatingPoint(T) = is(T == float) || is(T == double) || 
is(T == real) || is(T == cfloat) || is(T == cdouble) || is(T == 
creal) || is(T == ifloat) || is(T == idouble) || is(T == ireal);
enum isClass(T) = is(T == class);
enum isClass(alias T) = is(T == class);
enum isInterface(T) = is(T == interface);
enum isInterface(alias T) = is(T == interface);
enum isObject(T) = isClass!(T) || isInterface!(T);
enum isStruct(T) = is(T == struct);
enum isStruct(alias T) = is(T == struct);
enum isUnion(T) = is(T == union);
enum isUnion(alias T) = is(T == union);
enum isArray(T) = is(T U : U[]);
enum isStaticArray(T) = is(T : U[n], U, size_t n);
enum isString(T) = is(T : string) || is(T : wstring) || is(T : 
dstring);
enum isString(alias T) = is(T : string) || is(T : wstring) || 
is(T : dstring);
enum isAssociativeArray(T) = 
is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T);
enum isPointer(T) = is(T U : U*);
enum isFunctionPointer(T) = is(typeof(*T) == function);
enum isEnum(T) = is(T == enum);
enum isEnum(alias T) = is(T == enum);
enum isReference(T) = isObject!(T) || isPointer!(T);
enum isVoid(T) = is(T == void);
enum isSymbol(alias arg) = __traits(compiles, 
__traits(getAttributes, arg));
enum isType(alias symbol) = __traits(compiles, 
expectType!(symbol)); private template expectType(T) {}
alias TypeOfDataType(T) = T.DataType;
alias ElementTypeOfArray(T : T[]) = T;
template BaseTypeOfPointer (T) { static if (is(T U : U*)) alias 
BaseTypeOfPointer!(U) BaseTypeOfPointer; else alias T 
BaseTypeOfPointer; }
template BaseTypeOfEnum (T) { static if (is(T U == enum)) alias 
BaseTypeOfEnum!(U) BaseTypeOfEnum; else alias T BaseTypeOfEnum; }
template KeyTypeOfAssociativeArray (T) { static 
assert(isAssociativeArray!(Unqual!(T)), "The type needs to be an 
associative array"); alias typeof(T.init.keys[0]) 
KeyTypeOfAssociativeArray; }
template ValueTypeOfAssociativeArray (T) { static 
assert(isAssociativeArray!(Unqual!(T)), "The type needs to be an 
associative array"); alias typeof(T.init.values[0]) 
ValueTypeOfAssociativeArray; }
template TypeOf(alias expr) { static if (isType!(expr)) alias 
expr TypeOf; else alias typeof(expr) TypeOf; }



template StringToType(alias s)
{
	mixin("alias T = "~s~";");
	alias StringToType = Alias!T;
}























module mModel;


struct sStruct
{
	int Y;

	int opIndex(int i)
	{
		return i;
	}
}

 (4) enum eEnum : long
{
	X,
	Y = 43,
	 ("sdfdsa")  (3) Z = 4
}

 ("fdsa", 4) interface iBase
{
	iBase fooBase(iBase);
}

union uUnion
{
	int X;
	long Y;
}

class cBase : iBase
{
	string testField;
	cBase fooBase(iBase c) { return cast(cBase)c; }
	//cType barBase(cDerived c) { return c.type; }

}

interface iX { };
interface iY { };


 ("fdsa",8) class cDerived(Q) : cBase, iX, iY
{
	alias type this;
	cType type;

	 ("XXXRRERES") int testField1;
	private double testField2;

	 property int ValueProp() { return 3; }
}

class cType
{

}
Apr 03 2019
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 3 April 2019 at 07:22:10 UTC, Alex wrote:

 But D's type system is too screwed up. Fields and members are 
 not treated as types in and have themselves so
I don't know what this means.
 1. Building a compact code base is impossible.
I beg to differ: https://github.com/kaleidicassociates/autowrap/blob/master/reflection/source/autowrap/reflection.d
 2. The type system sometimes requires using alias and sometimes 
 a type yet there is no way to unify the two to reduce code 
 bloat and everything must be duplicated.
void foo(T...)() if(T.length == 1) { /* ... */ }
3. The Type's do not carry their moodules and
 import automatically so the symbols can be used.
import std.traits: moduleName; mixin(`import `, moduleName!thingie, `;`);
 This requires getting the module and importanting things. 
 Further more certain _traits want a this object to work for 
 fields
Use T.init when that's the case.
 and others don't but none of the methods seem sensical. 4. If a 
 symbol is private CT __traits fails..., again non-sensical.
See my reply to your other post.
Apr 03 2019
parent reply Alex <AJ gmail.com> writes:
On Wednesday, 3 April 2019 at 09:24:23 UTC, Atila Neves wrote:
 On Wednesday, 3 April 2019 at 07:22:10 UTC, Alex wrote:

 But D's type system is too screwed up. Fields and members are 
 not treated as types in and have themselves so
I don't know what this means.
One cannot pass a field type around like one can a class. Using Fields and FieldsNameTuple does not create a type. This then requires one trying to exact the type info by hacking which then requires all kinds of tricks that never ultimately succeed. If they do work in one aspect they completely breakdown in another. The BaseReflection class should is supppose to encapsulate getting the name, id, module, etc of any base reflected type. Works fine for aggregates... The moment you try to use it on a field it fails because fields don't have a type. Specializing for fields then sends one off in a different direction and it fail fails. I posted all the code for you. It should not be difficult to understand and get working with your knowledge about D's meta system. Simply put the different code snippets in their appropriate modules and run. It should work. Note the output and the fields and methods section. Now try to get the code to fill in the appropriate data for them. That is all. It should be an easy task, right? (I have specialized for the fields by hacking but the private bug stops it from being general enough)
 1. Building a compact code base is impossible.
I beg to differ: https://github.com/kaleidicassociates/autowrap/blob/master/reflection/source/autowrap/reflection.d
You can beg all you want, I'm not talking about specific examples, I'm talking about in general. Meaning that in general when one is trying to create certain domains of meta programming they run in to a brick wall like I have.
 2. The type system sometimes requires using alias and 
 sometimes a type yet there is no way to unify the two to 
 reduce code bloat and everything must be duplicated.
void foo(T...)() if(T.length == 1) { /* ... */ }
Doesn't work well, I have used that. Doesn't always work.
3. The Type's do not carry their moodules and
 import automatically so the symbols can be used.
import std.traits: moduleName; mixin(`import `, moduleName!thingie, `;`);
Um, that is not safe.
 This requires getting the module and importanting things. 
 Further more certain _traits want a this object to work for 
 fields
Use T.init when that's the case.
Doesn't always work. I have done it.
 and others don't but none of the methods seem sensical. 4. If 
 a symbol is private CT __traits fails..., again non-sensical.
See my reply to your other post.
I posted all my code, have fun trying to get it to work... I have used all your "solutions" in the process and none of them ultimate worked. I did this before you posted. These solutions do not work in all cases while other times they. Since you are a master at it, it should take you no time to fix up the code above. Just need to get the members or fields properly You can
Apr 03 2019
next sibling parent reply drug <drug2004 bk.ru> writes:
On 03.04.2019 12:35, Alex wrote:
 On Wednesday, 3 April 2019 at 09:24:23 UTC, Atila Neves wrote:
 On Wednesday, 3 April 2019 at 07:22:10 UTC, Alex wrote:

 But D's type system is too screwed up. Fields and members are not 
 treated as types in and have themselves so
I don't know what this means.
One cannot pass a field type around like one can a class. Using Fields and FieldsNameTuple does not create a type. This then requires one trying to exact the type info by hacking which then requires all kinds of tricks that never ultimately succeed. If they do work in one aspect they completely breakdown in another. The BaseReflection class should is supppose to encapsulate getting the name, id, module, etc of any base reflected type. Works fine for aggregates... The moment you try to use it on a field it fails because fields don't have a type. Specializing for fields then sends one off in a different direction and it fail fails. I posted all the code for you. It should not be difficult to understand and get working with your knowledge about D's meta system. Simply put the different code snippets in their appropriate modules and run. It should work. Note the output and the fields and methods section. Now try to get the code to fill in the appropriate data for them. That is all. It should be an easy task, right? (I have specialized for the fields by hacking but the private bug stops it from being general enough)
IIUC, the reason is that there are three types of data that frontend operates on - Types, Symbols and Expressions. Symbols and Expressions are often used together, so we have two kinds in fact - Types and Symbols+Expressions. Types processing differs from Symbols processing so we have this issue you have encountered - Types do not have protection, for example. You need to check if the entity you worked with is Types or Symbols and process it accordingly.
Apr 03 2019
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Wednesday, 3 April 2019 at 10:08:14 UTC, drug wrote:
 Types do not have protection, for example. You need to check if 
 the entity you worked with is Types or Symbols and process it 
 accordingly.
Types do not have protection? struct A { private struct B { } }?
Apr 03 2019
parent drug <drug2004 bk.ru> writes:
On 03.04.2019 13:56, FeepingCreature wrote:
 
 Types do not have protection?
 
 struct A { private struct B { } }?
 
 
Yes, I was wrong, they have. I've solved my problem about year(s) ago and now unfortunately don't remember details, sorry for wrong statement.
Apr 03 2019
prev sibling parent Alex <AJ gmail.com> writes:
On Wednesday, 3 April 2019 at 10:08:14 UTC, drug wrote:
 On 03.04.2019 12:35, Alex wrote:
 On Wednesday, 3 April 2019 at 09:24:23 UTC, Atila Neves wrote:
 On Wednesday, 3 April 2019 at 07:22:10 UTC, Alex wrote:

 But D's type system is too screwed up. Fields and members 
 are not treated as types in and have themselves so
I don't know what this means.
One cannot pass a field type around like one can a class. Using Fields and FieldsNameTuple does not create a type. This then requires one trying to exact the type info by hacking which then requires all kinds of tricks that never ultimately succeed. If they do work in one aspect they completely breakdown in another. The BaseReflection class should is supppose to encapsulate getting the name, id, module, etc of any base reflected type. Works fine for aggregates... The moment you try to use it on a field it fails because fields don't have a type. Specializing for fields then sends one off in a different direction and it fail fails. I posted all the code for you. It should not be difficult to understand and get working with your knowledge about D's meta system. Simply put the different code snippets in their appropriate modules and run. It should work. Note the output and the fields and methods section. Now try to get the code to fill in the appropriate data for them. That is all. It should be an easy task, right? (I have specialized for the fields by hacking but the private bug stops it from being general enough)
IIUC, the reason is that there are three types of data that frontend operates on - Types, Symbols and Expressions. Symbols and Expressions are often used together, so we have two kinds in fact - Types and Symbols+Expressions. Types processing differs from Symbols processing so we have this issue you have encountered - Types do not have protection, for example. You need to check if the entity you worked with is Types or Symbols and process it accordingly.
The problem is that some of the traits do not use either types or symbols but strings... or they are split in to the two different camps without an easy way to connect them... Again, I've given the code, it is not hard. There is a cFieldsReflection class, in that class one must fill out the info. Try it, you will find it is impossible to get anything to work correctly and generically. Things can be hacked to some degree... with methods it is even harder. And things are not uniform so one has to make special cases. For example. The base class gets the basic common info, but it fails to be callable for fields, if you make it work for fields it will fail for aggregates... and so it requires creating two separate pathways to handle essentially the same "types" in the language. These things should be very simple to do but they are not. I had no problem getting the code working for aggregates and everything worked as expected. Functions also were not a problem... but fields and methods are impossible or there documentation is invalid or there is some other traits that works. Anyone claiming it is simple has the full code and it shouldn't take but minutes to demonstrate it working. One can give simplified test cases that work but then in a real application they fail so they are actually invalid.
Apr 03 2019
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 3 April 2019 at 09:35:35 UTC, Alex wrote:
 On Wednesday, 3 April 2019 at 09:24:23 UTC, Atila Neves wrote:
 On Wednesday, 3 April 2019 at 07:22:10 UTC, Alex wrote:
 [...]
void foo(T...)() if(T.length == 1) { /* ... */ }
Doesn't work well, I have used that. Doesn't always work.
Do you have an example where it doesn't work?
 [...]
import std.traits: moduleName; mixin(`import `, moduleName!thingie, `;`);
Um, that is not safe.
How so?
 [...]
Use T.init when that's the case.
Doesn't always work. I have done it.
Do you have an example?
 it should take you no time to fix up the code above.
Hire me and I'd be glad to.
Apr 03 2019
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/3/19 3:22 AM, Alex wrote:
 The follow code is a reflection library that unifies all the junk in 
 std.traits and __traits and makes it using reflection as if it were 
 runtime and one can even keep the info around at runtime...
 
 But D's type system is too screwed up.
Alex, thank you for this work. After looking at it, I was all morning on the phone with my students Eduard Staniloiu and Razvan Nitu, discussing in good part how to make inroads into the introspection matter. Introspection is important. It is also, more or less incidentally, central to Eduard's doctoral dissertation and also important for Razvan's. They will look through your code trying to identify and look at fixes for the problems you encountered. One thing you could help with is to break down the problems you found in bite-size chunks - a simple example, the expected behavior, and the actual behavior. In some cases it may be something desirable but not a showstopper. Example: "D forces code duplication". In some other cases there may be no code example, e.g. "I need introspection artifact X to do Y and there isn't any." Please assign all of these issues to Eduard. Thanks!
Apr 03 2019
parent reply Alex <AJ gmail.com> writes:
On Wednesday, 3 April 2019 at 17:56:08 UTC, Andrei Alexandrescu 
wrote:
 On 4/3/19 3:22 AM, Alex wrote:
 The follow code is a reflection library that unifies all the 
 junk in std.traits and __traits and makes it using reflection 
 as if it were runtime and one can even keep the info around at 
 runtime...
 
 But D's type system is too screwed up.
Alex, thank you for this work. After looking at it, I was all morning on the phone with my students Eduard Staniloiu and Razvan Nitu, discussing in good part how to make inroads into the introspection matter. Introspection is important. It is also, more or less incidentally, central to Eduard's doctoral dissertation and also important for Razvan's. They will look through your code trying to identify and look at fixes for the problems you encountered. One thing you could help with is to break down the problems you found in bite-size chunks - a simple example, the expected behavior, and the actual behavior. In some cases it may be something desirable but not a showstopper. Example: "D forces code duplication". In some other cases there may be no code example, e.g. "I need introspection artifact X to do Y and there isn't any." Please assign all of these issues to Eduard. Thanks!
Thanks for taking this serious enough to take it serious enough! (meaning looking in to it). 1. One of the problems here is that as I wrote the code and came up against problems I'd have to try something and if it didn't work I'd move on to try something else not taking in to account why it specifically didn't work. I will try to break it down though in to pieces. First, the code is relatively straight forward: Oop is used because I thought it would better unify CT and RT. The core feature is this: abstract class cBaseReflection { // Declared in reverse order for proper output sAttributeReflection[] Attributes; // Attributes on the type cBaseReflection[] Uses; // Known types that use this type as a field, parameter, return type, etc string Body; // D does not allow getting a body but this is included if it ever does string Protection; string MangledName; string ModuleName; // The module name string FullName; // The fully qualified name string TypeName; // The type name string Id; // The id = __traits(identifier,T) string DType() { return "Unknown"; } auto Reflect(alias T)() { Id = __traits(identifier, T); static if (__traits(compiles, T.stringof)) TypeName = T.stringof; static if (__traits(compiles, moduleName!T)) ModuleName = moduleName!(T); static if (__traits(compiles, fullyQualifiedName!T)) FullName = fullyQualifiedName!(T); static if (__traits(compiles, mangledName!T)) MangledName = mangledName!T; static if (__traits(compiles, __traits(getProtection, T))) Protection = __traits(getProtection, T); // Get the attributes for the type static if (__traits(compiles, __traits(getAttributes, T))) static foreach(a; __traits(getAttributes, T)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof); return this; } } cBaseReflection is the prototype for all the classes. It simply contains the common type info of all types. All the other classes are just specializing for the different D types but have the same structure. Reflect is a common templated method who's purpose is to fill out that information using __traits and std.traits. These then wrap those functions so we don't have to call them by hand, we just Reflect!T and it will return the class with the data filled out. For example, a typical problem in D meta programming is to get the enum members names and values, the following specialization of cBaseReflection does this: /* Encapsulates an Enumeration Reflection */ class cEnumReflection : cBaseReflection { static struct sValueReflection { string Name; string Value; sAttributeReflection[] Attributes; this(string n, string v) { Name = n; Value = v; } } sValueReflection[] Values; string BaseType; override string DType() { return "enum"; } auto Reflect(alias T)() { super.Reflect!T; //BaseType = TypeOf(T).stringof; static foreach(e; EnumMembers!T) {{ Values ~= sValueReflection(to!string(e), e.stringof); mixin("alias E = __traits(getAttributes, T."~to!string(e)~");"); static foreach(a; E) Values[$-1].Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof); }} return this; } } Normally we would have to manually do the for each in our code, now we can just Reflect!SomeEnum.Values[k].Value to get the kth value. This is much easier than having to remember the specifics of the `static foreach(e; EnumMembers!T)` because sometimes there are __traits to use and sometimes their are templates. Sometimes one has to use typeof and other times TypeOf(becausse of aliasing issues). So Reflect! Abstracts all those issues away(ideally). Reflect!T calls the parent base Reflect to fill in the more general info: super.Reflect!T; The idea is simple, to reduce duplicate code. There should be no reason to have to duplicate code to get the same info. Since cBaseReflection.Reflect gets all the general info(and we can use __traits(compiles,) to handle any minor discrepancies, let cBaseReflection.Reflect do it's job for all specialized types and it puts the code in one singular place. All this basically works without a hitch(although I have duplicated getting the attributes for the enum. We could possibly let cBaseReflection or add a Reflect!T to sAttributeReflection to handle it. Looking at cAggregateReflection, we have /* Encapsulates an Aggregate Type */ abstract class cAggregateReflection : cBaseReflection { cMethodReflection[] Methods; // The methods cFieldReflection[] Fields; // The fields cClassReflection[] DerivedClasses; // Known immedate derived class that derive this type cAggregateReflection[] NestedAggregates; // Any nested aggregates sTypeReflection[] AliasThis; // The Alias This sTypeReflection[] TypeParameters; // Any type parameters used. bool HasAliasing; bool HasElaborateAssign; bool HasElaborateCopyConstructor; bool HasElaborateDestructor; bool HasIndirections; bool HasNested; bool HasUnsharedAliasing; bool IsInnerClass; bool IsNested; override string DType() { return "Unknown"; } auto Reflect(alias T)() { super.Reflect!T; // Get the Alias This static foreach(a; __traits(getAliasThis, T)) {{ mixin("import "~moduleName!T~";"); mixin("AliasThis ~= sTypeReflection(to!string(a), typeof("~moduleName!T~"."~T.stringof~"."~to!string(a)~").stringof);"); }} HasAliasing = hasAliasing!T; HasElaborateAssign = hasElaborateAssign!T; HasElaborateCopyConstructor = hasElaborateCopyConstructor!T; HasElaborateDestructor = hasElaborateDestructor!T; HasIndirections = hasIndirections!T; HasNested = hasNested!T; HasUnsharedAliasing = hasUnsharedAliasing!T; static if (__traits(compiles, isInnerClass!T)) IsInnerClass = isInnerClass!T; static if (__traits(compiles, isNested!T)) IsNested = isNested!T; // Get the fields alias n = FieldNameTuple!T; static foreach(k, f; std.traits.Fields!T) {{ Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]); }} static foreach(k, m; __traits(derivedMembers, T)) {{ static if (__traits(compiles, __traits(getVirtualMethods, T, m))) static foreach(j, v; typeof(__traits(getVirtualMethods, T, m))) {{ //pragma(msg, T.stringof, " --- ", v.stringof); Methods ~= (new cMethodReflection()).Reflect!(T, m); }} }} return this; } } and one can see it's pretty straight forward: It's just wrapping all the basic aggreigate std.trait calls so we don't have to call them individually. HasAliasing = hasAliasing!T; HasElaborateAssign = hasElaborateAssign!T; HasElaborateCopyConstructor = hasElaborateCopyConstructor!T; HasElaborateDestructor = hasElaborateDestructor!T; HasIndirections = hasIndirections!T; HasNested = hasNested!T; HasUnsharedAliasing = hasUnsharedAliasing!T; It obviously calls the super.Reflect! so to avoid duplicating code. An aggregate may not have methods or certain other entries but we include them in this class to be able to handle them more generally as to avoid duplicate code. Because we have __traits(compiles,) we can always ignore the cases that will fail very easily. The code is quite simple. It's just really basic oop and wrapping a lot of the traits code, again, to abstract them away. The general model is class Child : Parent { data auto Reflect(T)() { super.Reflect!T; ..fill out data.. return this; } } And calling child.Reflect will cascade all the calls and have each class in the hierarchy fill out the appropriate information for the type it is acting on(ideally there will be a corresponding D type(class, field, enum, interface, template, etc)). If none of this helps in understanding the code you can reach me at Incipient email.com and I'll try to explain it better. The code was in it's initial stages as it was more prototyping and seeing what could be done. Ideally one would be able to get any piece of CT information one desires from Reflect!. I didn't get finished filling out all the details before I ran in to the issues below. ------------------------------------------------- ------------------------------------------------- Now, when we get to filling out field info the issues start to occur. An aggregate may have fields and so we want to get the meta info for them: // Get the fields alias n = FieldNameTuple!T; static foreach(k, f; std.traits.Fields!T) {{ Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]); }} To get information about fields one has to use FieldNameTyple to get the string names of the Id's for the fields and std.traits.Fields!T to get the types. //std.traits.Fields template Fields(T) { static if (is(T == struct) || is(T == union)) alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]); else static if (is(T == class)) alias Fields = typeof(T.tupleof); else alias Fields = AliasSeq!T; } The problem here is that there is no Field Type in D. That is, a field is not a type in and of itself. A class is a type. So we have to pass the type and name that is returned, rather than a type which would encapsulate that info(and which could then be gotten inside cFieldsReflection and this would help encapsulate code) From the help: import std.meta : AliasSeq; struct S { int x; float y; } static assert(is(Fields!S == AliasSeq!(int, float))); So, if we want cFieldsReflection to do all the work we have to then not just parse the field type passed(since we can't) but pass the info that lets us build the field type. This alone breaks the design of the code. Specifics are having to be handled in more general code, which reduces isolation: E.g, ideally we could do this: // Get the fields TYPES static foreach(k, f; std.traits.FieldsTYPE!T) {{ Fields ~= (new cFieldReflection()).Reflect!(f); }} // TYPE for emphasis rather than Type and then f, as a type(a type in the sense of a self contained compiler meta unit that can be passed around as a type. It may not make sense that it is a true D type but for meta programming it acts like one. E.g. hypothetically we could do class X { int somefield; } class Y { typeof(FieldsTYPE!(X)[0]) anotherintfield; } ). So, instead we have to pass the best info we can that will let us hopefully get at the info we need to fill out the field data. Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]); Here T is passed(the Alias is left over from trying various work arounds), which is the class the field is in, and n[k] is the name of the kth field. The idea is that we can build `T.(n[k])` such as someclass.somefield. So this gets passed to /* Encapsulates a Field Reflection */ class cFieldReflection : cBaseReflection { override string DType() { return "field"; } auto Reflect(alias T, string name)() { Id = name; static if (__traits(compiles, TypeName = T.stringof)) TypeName = T.stringof~"."~name; ModuleName = moduleName!(T); FullName = fullyQualifiedName!(T)~"."~name; MangledName = mangledName!T; //mixin(`FullName = mangledName!(`~T.stringof~`.`~name~`);`); // Get the attributes for the field import mModel; static if (__traits(compiles, mixin(`static if (__traits(compiles, __traits(getAttributes, `~T.stringof~`.`~name~`)))`))) mixin(`static if (__traits(compiles, __traits(getAttributes, `~T.stringof~`.`~name~`))) pragma(msg, "fdadsf"); `); //static if (aFound) { mixin(`static foreach(a; __traits(getAttributes, `~T.stringof~`.`~name~`)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof);`);} //pragma(msg, T); //T t; //mixin("alias X = t."~name~";"); //super.Reflect!X; return this; } } Note that cBaseReflection.Reflect code is duplicated rather than just calling super.Reflect. Why? Because we have no type to pass, and Reflect takes a type and extracts the info. We have to build the info manually using hacks such as: Id = name; // easy because the name is the id TypeName = T.stringof~"."~name; // this just uses someclass.somefield The rest just use T, the class or uses someclass.somefield and hopes for the best ModuleName = moduleName!(T); FullName = fullyQualifiedName!(T)~"."~name; MangledName = mangledName!T; //mixin(`FullName = mangledName!(`~T.stringof~`.`~name~`);`); // leftover from testing, was just using fullname for holding the string For example, their is no mangling for the field(should their be, ultimately?). We just use the mangled name of the class, should work good enough but it is not general. Now we have to get the attributes, again, cBaseReflection does all that but we can't call it because Reflect requires a type and cBaseReflection shouldn't try to handle fields using field names and the class. // Get the attributes for the field import mModel; // We have to import the model because we have no type that encapsulates it all. static if (__traits(compiles, mixin(`static if (__traits(compiles, __traits(getAttributes, `~T.stringof~`.`~name~`)))`))) mixin(`static if (__traits(compiles, __traits(getAttributes, `~T.stringof~`.`~name~`))) pragma(msg, "fdadsf"); `); //static if (aFound) { mixin(`static foreach(a; __traits(getAttributes, `~T.stringof~`.`~name~`)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof);`);} Note that all this code is simply trying to get the attributes. Notice how easy it is for a the base type: // Get the attributes for the type static if (__traits(compiles, __traits(getAttributes, T))) static foreach(a; __traits(getAttributes, T)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof); But because there is no `T` for fields we can't do it so easy, so we have to construct the name and use string mixins. But things start to break down here. One would think that we could just do // Get the attributes for the field import mModel; mixin(`static foreach(a; __traits(getAttributes, `~T.stringof~`.`~name~`)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof);`); which is just the same code except we have to import the model because now we are using it directly(not as a type but as someclass.somefield). This code then breaks down for private fields! mReflect.d-mixin-201(201): Deprecation: `mModel.cDerived!int.cDerived.testField2` is not visible from module `mReflect` Why? because we are trying to access it directly and it is private, although it is at compile time, we are in CTFE so the protection scheme prevents us from accessing. for example, this code works: mixin(`import `~moduleName!T~`;`); static if (name != "") mixin(`static foreach(a; __traits(getAttributes, `~T.stringof~`.`~name~`)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof);`); Notice how much more "complicated"(rather, messy and error prone) it is... just because we have to construct the field. Also that it fails for private fields, it works here because it's only depreciated, but later on for methods we get an error instead! Fields are relatively simple because they are just a name, attributes, and a type. Here are the issues though: Because their is no Field Type: 1. We have to hack things together using string mixins. 2. It requires importing the module that contains the type just to reflect on it. This then creates protection issues and possible name collisions(although we can alias the module) 3. We have to pass around the parent type and field in to templates and construct the fields by hand. This makes then makes a non-unified system. Notice how much easier it is to deal with when we just have a field type. It's not just the 2 line of code but also how it integrates with the rest of the code and the cBaseReflection. (and all the other templates such as mangledName and such) Solution: Seems that adding a Field Type to D would completely solve all these issues. It is just another type who's main purpose is to encapsulate field as types making them "1st class". ---------- Now, moving on to methods, essentially the same problem. Because methods(and functions) are not types in and of themselves, it requires hacks like the above, but much worse since they are more complicated. Some thing here, we gotta import the module that has the method, meaning same issues as above mixin(`import `~moduleName!T~`;`); // Must import the type to be able to inspect it(fragile and bizzare and ridiculous) T t = T.init; // left over from trying to get things to work Then, again, we can't call super.Reflect!T to do the work and most duplicate code: Id = name; static if (__traits(compiles, TypeName = T.stringof)) TypeName = T.stringof~"."~name; ModuleName = moduleName!(T); FullName = fullyQualifiedName!(T)~"."~name; MangledName = mangledName!T; //mixin(`MangledName = mangledName!(`~T.stringof~`).`~name~`;`); After messing with this stuff for a few days or so and it just getting becoming more obvious that it wasn't going to work well I decided to give up. While it may be possible to hack most things and get them to work. For example, mReflect.d-mixin-222(222): Error: no property `ValueProp` for type `string` Of course, it is giving fields, so I have to modify the cAggregiateReflection code to only return appropriate methods: static foreach(k, m; __traits(derivedMembers, T)) {{ static if (__traits(compiles, __traits(getVirtualMethods, T, m))) static foreach(j, v; typeof(__traits(getVirtualMethods, T, m))) {{ //pragma(msg, T.stringof, " --- ", v.stringof); Methods ~= (new cMethodReflection()).Reflect!(T, m); }} }} so it suggests we need a derivedMembers. I tried to use getVirtualMethods exclude fields but I guess it didn't work right, I see that I left the m in their instead of changing it to a v but v is the type of the method and not a name like m is. So, the whole point of the Reflect library is to provide a uniform basis to get the desired information without all this nonsense(IMO). It's not that it isn't necessarily doable but one can see that it requires a lot of unpleasant coding and really destroys the elegance of using oop and CTFE to solve the problem. If I have to duplicate the code in each class and use string mixins then what really is the point? (one could use a single template and string mixins to solve that but it becomes such a mess that it isn't fun and makes me wonder what other kinda nonsense will I run in to in the future?) My suggestions: Every D concept should have D type. attributes are types, unions, classes, fields, methods, enum members, symbols, expressions, code bodies, etc. This is akin to Object in oop. Every type template should then work find with all these types then. Want the module name of an attribute, just do moduleName!a where a is an attribute type returned by getAttributes. This may not be feasible for all the things in D at least fields and methods and whatever other major ones that would be needed. (templates? I think they are covered under methods?) Maybe the idea would be better served to be able to box up info to make a type such as DType and then be able to pass that around. In that case I could build the field and method types and then simply use Reflect and all the other easy code to get it to work. Ideally the compiler itself would expose all the info available to a D program in a unified way. e.g., the Reflect library here would essentially be integrated(conceptually, not the code) in to the d compiler. This would replace the need for traits. D could have a reflect keyword: reflect!T and one gets back a reflection type that contains all the info one wants in hierarchal form: reflect!MyClass.Name reflect!MyClass.Module.RootTypes reflect!MyClass.Module.Imports reflect!MyEnum.Uses // Lets one get all the known uses of the enum reflect!MyFunc.Body // returns the code body(we can do this now by importing the module as text and locating it but it is fragile) reflect!MyFunc.Location // returns the Source location(file, line, etc) reflect!MyAttribute.New(Name = MyOtherAttribute, Value = 434) // Lets one construct a new type based off the old one, maybe not a great idea) reflect!MyClass.New(Name = Name~Other, Body = { }) etc... The goal of the code I wrote was to provide as much info as I can get in a unified way(as I've said many times already). Since it just wraps traits there is no drawback that I can think of and there shouldn't be(although since it uses classes it might not be usable in all areas of D). If those in the know got serious about this idea it could provide a very powerful and elegant solution for D's meta system that will heavily complement it(it would also be easy to extend since one just ads to the hierarchy and all the internals are abstracted away). I use meta programming heavily in D and that is the main thing I like about it. Being able to write generic code means being able to work in general terms. This then requires the ability to introspect in the type system. D has the abilities but the conceptualization is very messy. For one, there is __traits and then there is std.traits. One could argue that one can wrap everything in __traits but that is easier said than done(as I have proven). One problem with a "library" solution is speed. The compiler already parses all this information once and by having a internal reflect! the data can be filled out once during compilation and the using it is just looking up that data(at the cost of memory, could be lazy though). I'm not sure the cost of repeatedly calling traits functions, if they are essentially memorized or if it requires some calculation, but if it does require any significant time, I imagine that a reflect! would then drastically increase compilation times when heavy meta programming is involved) as most introspection's would just be a look up. Anyways, hopefully you guys can come up with something usable. I find that D's meta programming is very capable but also can be very tiresome to do simple things. I find myself having to use string mixins quite often which makes it very difficult to debug and read but also seems extremely unnecessary. The same patterns are repeated over and over and over. While much of this work may be long term(D3?)[although, to be honest, it didn't take me that long to through the code I wrote together. Most is just wrapping. It's the anomalies of traits that caused the problem] I'd hope that at the very least a good solution to the main problems of fields and methods/functions in this library could be created. Thanks.
Apr 03 2019
next sibling parent reply Alex <AJ gmail.com> writes:
I went ahead and decided to tidy up the code a bit and fix what 
could be fixed. For the most part everything is working pretty 
nice. By being careful of what was returned and dealing with any 
problems correctly I was able to get most of the code to be as I 
intend. So far, assuming everything is right, the only problem is 
that overloads are not returning parameter information correctly 
which I demonstrate at the bottom(Since the method code is now 
using the function code and the function code works it suggests a 
subtle issue).



import std.stdio;
import mExModel;
import mModel;
import mReflect;
import mExTraits;

struct attr(T)
{
	T name;
	int value;
}

 attr!string("test", 432)  (4) void foo(int x, ref double y, 
float z = 43234.34) { }


int main()
{
	
	pragma(msg, printModelHierarchy(Reflect!(cDerived!int)));
	pragma(msg, printModelHierarchy(Reflect!(foo)));
	pragma(msg, printModelHierarchy(Reflect!(eEnum)));

     return 0;
}






module mModel;


struct sStruct
{
	int Y;

	int opIndex(int i)
	{
		return i;
	}
}

 (4) enum eEnum : long
{
	X,
	Y = 43,
	 ("sdfdsa")  (3) Z = 4
}

 ("fdsa", 4) interface iBase
{
	iBase fooBase(iBase);
}

union uUnion
{
	int X;
	long Y;
}

class cBase : iBase
{
	string testField;
	cBase fooBase(iBase c) { return cast(cBase)c; }
	//cType barBase(cDerived c) { return c.type; }

}

interface iX { };
interface iY { };


 ("fdsa",8) class cDerived(Q) : cBase, iX, iY
{
	alias type this;
	cType type;

	 ("XXXRRERES") int testField1;
	 ("XXXRRERES4") private double testField2;

	 ("A") int foo() { return 2; }
	private  ("B") int foo(int x, int y) { return x; }

	 ("VP att")  property int ValueProp() { return 3; }
}

class cType
{

}









module mReflect;

import mExTraits;
import std.meta, std.conv, std.typecons, std.typetuple, 
std.string, std.algorithm, std.range, std.variant;

/*
	A basic type
*/
struct sTypeReflection
{
	string Name;
	string Type;
	this(string n, string t) { Name = n; Type = t; }
}


/*
	A Basic attribute
*/
struct sAttributeReflection
{
	string Value;
	string Type;
	this(string v, string t) { Value = v; Type = t; }
}



/*
	Encapsulation of basic reflection
*/
abstract class cBaseReflection
{
	// Declared in reverse order for proper output
	sAttributeReflection[] Attributes;			// Attributes on the type
	cBaseReflection[] Uses;						// Known types that use this type 
as a field, parameter, return type, etc
	string Body;								// D does not allow getting a body but this 
is included if it ever does
	string Protection;
	string MangledName;
	string ModuleName;							// The module name
	string FullName;							// The fully qualified name
	string TypeName;							// The type name
	string Id;									// The id  = __traits(identifier,T)
	
	string DType() { return "Unknown"; }

	auto Reflect(Ts...)()
	{
		alias T = Ts[0];
		
		static if (__traits(compiles, __traits(identifier, T))) Id = 
__traits(identifier, T);	
		static if (__traits(compiles, T.stringof)) TypeName = 
T.stringof;
		static if (__traits(compiles, moduleName!T)) ModuleName = 
moduleName!(T);
		static if (__traits(compiles, fullyQualifiedName!T)) FullName = 
fullyQualifiedName!(T);
		static if (__traits(compiles, mangledName!T)) MangledName = 
mangledName!T;
		static if (__traits(compiles, __traits(getProtection, T))) 
Protection = __traits(getProtection, T);

		// Get the attributes for the type
		static if (__traits(compiles, __traits(getAttributes, T)))
			static foreach(a;  __traits(getAttributes, T)) Attributes ~= 
sAttributeReflection(to!string(a), typeof(a).stringof);

		return this;
	}
}

/*
	Encapsulates an Enumeration Reflection
*/
class cEnumReflection : cBaseReflection
{
	static struct sValueReflection
	{
		string Name;
		string Value;		
		sAttributeReflection[] Attributes;			
		this(string n, string v) { Name = n; Value = v; }
	} sValueReflection[] Values;

	string BaseType;
	override string DType() { return "enum"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;
		
		BaseType = OriginalType!(T).stringof;
		static foreach(e; EnumMembers!T)
		{{
			Values ~= sValueReflection(to!string(e), e.stringof);
			mixin("alias E = __traits(getAttributes, 
T."~to!string(e)~");");			
			static foreach(a; E) 						
				Values[$-1].Attributes ~= sAttributeReflection(to!string(a), 
typeof(a).stringof);							
		}}

		return this;
	}
}







/*
	Encapsulates an Aggregate Type
*/
abstract class cAggregateReflection : cBaseReflection
{		

	
	
	cMethodReflection[] Methods;							// The methods
	cFieldReflection[] Fields;								// The fields
	cClassReflection[] DerivedClasses;						// Known immedate 
derived class that derive this type
	cAggregateReflection[] NestedAggregates;				// Any nested 
aggregates
	sTypeReflection[] AliasThis;							// The Alias This	
	sTypeReflection[] TypeParameters;						// Any type parameters 
used.
	bool HasAliasing;
	bool HasElaborateAssign;
	bool HasElaborateCopyConstructor;
	bool HasElaborateDestructor;
	bool HasIndirections;
	bool HasNested;
	bool HasUnsharedAliasing;
	bool IsInnerClass;
	bool IsNested;
	
	override string DType() { return "Unknown"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		// Get the Alias This
		static foreach(a;  __traits(getAliasThis, T))
		{{
			mixin("import "~moduleName!T~";");		
			mixin("AliasThis ~= sTypeReflection(to!string(a), 
typeof("~moduleName!T~"."~T.stringof~"."~to!string(a)~").stringof);");
		}}

		HasAliasing = hasAliasing!T;
		HasElaborateAssign = hasElaborateAssign!T;
		HasElaborateCopyConstructor = hasElaborateCopyConstructor!T;
		HasElaborateDestructor = hasElaborateDestructor!T;
		HasIndirections = hasIndirections!T;
		HasNested = hasNested!T;
		HasUnsharedAliasing = hasUnsharedAliasing!T;
		static if (__traits(compiles, isInnerClass!T)) IsInnerClass = 
isInnerClass!T;
		static if (__traits(compiles, isNested!T)) IsNested = 
isNested!T;
		
	
		// Get the fields
		alias n = FieldNameTuple!T;
		static foreach(k, f; std.traits.Fields!T)
		{{
			Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]);

		}}

		// Get the methods
		static foreach(k, m; __traits(derivedMembers, T))
		{{
			static if (__traits(compiles, __traits(getVirtualMethods, T, 
m)))
			static foreach(j, v; typeof(__traits(getVirtualMethods, T, 
m)))		
			{{				
				//Methods ~= (new cMethodReflection()).Reflect!(T, m, v);	// 
If we could pass the appropriate type, like we can a function, 
then everythign would work great.
				mixin(`import `~moduleName!T~`;`);	
				
				static foreach (f; typeof(__traits(getOverloads, T, m)))
				{					
					mixin(`Methods ~= (new 
cMethodReflection()).Reflect!(`~T.stringof~`.`~m~`, f);`);
				}

			}}
		}}
		return this;
	}
}


/*
	Encapsulates a Field Reflection
*/
class cFieldReflection : cBaseReflection
{
	override string DType() { return "field"; }

	auto Reflect(alias T, string name)()
	{		
		//pragma(msg, "Field: ", T, " -- ", name);
		static if (name != "")
		{
			// There is no field type so must manually construct and 
duplicate code, else we could reflect directly
			mixin(`import `~moduleName!T~`;`);	

			// super.Reflect!T

			// -------- Cannod delegate work to base class, copy, paste, 
fix instead
			Id = name;	
			static if (__traits(compiles, TypeName = T.stringof)) TypeName 
= T.stringof~"."~name;
			ModuleName = moduleName!(T);
			FullName = fullyQualifiedName!(T)~"."~name;
			MangledName = mangledName!T;

			// Get the attributes for the field
			mixin(`Protection = __traits(getProtection, 
(`~T.stringof~`).`~name~`);`);
			mixin(`static if (__traits(compiles, __traits(getProtection, 
`~T.stringof~`.`~name~`)))				
				static foreach(a;  __traits(getAttributes, 
`~T.stringof~`.`~name~`)) Attributes ~= 
sAttributeReflection(to!string(a), typeof(a).stringof);`);
		}
		return this;
		
	}
}


/*
	Encapsulates a Method Reflection
*/
class cMethodReflection : cFunctionReflection
{
	cFunctionReflection[] Overloads;

	override string DType() { return "delegate"; }

	auto Reflect(Ts...)()
	{
		alias T = Ts[0];
		alias V = Ts[1];
		//pragma(msg, "Method: ", T, " -- ", V.stringof);		
		
		super.Reflect!V;	
		(cast(cBaseReflection)this).Reflect!T; // Fill in base 
information to get correct information

		return this;
	}
}


/*
	Encapsulates a Union Reflection
*/
class cUnionReflection : cAggregateReflection
{
	override string DType() { return "union"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;
	}
}


/*
	Encapsulates a Struct Reflection
*/
class cStructReflection : cAggregateReflection
{
	override string DType() { return "struct"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;

	}
}



/*
	Encapsulates a Interface Reflection
*/
class cInterfaceReflection : cAggregateReflection
{	
	cInterfaceReflection[] InheritedInterfaces;				// The inherited 
interfaces
	override string DType() { return "interface"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;
		
		// Reflect on inherited interfaces
		static foreach(t; BaseTypeTuple!T)
			static if (isInterface!t)
				InheritedInterfaces ~= (new 
cInterfaceReflection()).Reflect!(t);


		return this;
	}
}



/*
	Encapsulates a Class Reflection
*/
class cClassReflection : cInterfaceReflection
{
	
	cClassReflection[] InheritedClasses;					// Any Inherited 
class(D only allows single class inheritance, but we allow for 
possibly more for uniformity)	
	int Alignment;
	bool IsAbstract = false;
	override string DType() { return "class"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		IsAbstract = isAbstractClass!T;
		Alignment = classInstanceAlignment!T;

		// Reflect on inherited class
		static foreach(t; BaseTypeTuple!T)
			static if (isClass!t)
				InheritedClasses ~= (new cClassReflection()).Reflect!(t);


		return this;
	}
}






/*
	Encapsulates a Function Reflection
*/
class cFunctionReflection : cBaseReflection
{
	static struct sParameterReflection
	{
		string Name;
		string Type;
		string DefaultValue = "none";
		string DefaultValueType = "void";
		struct sStorageClass
		{
			static foreach(a; EnumMembers!ParameterStorageClass)
				mixin(`bool Is`~strip(capitalize(to!string(a)),"_")~";");
		} sStorageClass StorageClass;
		this(string n, string t, string dv, string dt) { Name = n; Type 
= t; DefaultValue = dv; DefaultValueType = dt; }
	}

	static struct sFunctionAttributes
	{
		static foreach(a; EnumMembers!FunctionAttribute)
			mixin(`bool Is`~strip(capitalize(to!string(a)),"_")~";");
	}
	
	
	sFunctionAttributes FunctionAttributes;
	sParameterReflection[] Parameters;
	string ReturnType;
	string Linkage;
	Variadic VariadicFunctionStyle;
	int NumArgs;
	string Signature;
	override string DType() { return "function"; }

	auto Reflect(Ts...)()
		if (Ts.length == 1 && isCallable!Ts)
	{
		alias T = Ts[0];
		
		super.Reflect!T;	// Note that we can delegate to the function 
to a base class, but not the a method

		
		Signature = FunctionTypeOf!T.stringof;
		NumArgs = arity!T;
		Linkage = functionLinkage!T;
		VariadicFunctionStyle = variadicFunctionStyle!T;
		ReturnType = std.traits.ReturnType!T.stringof;

		static foreach(a; EnumMembers!FunctionAttribute)
			mixin(`FunctionAttributes.Is`~strip(capitalize(to!string(a)),"_")~" =
(functionAttributes!T & FunctionAttribute."~to!string(a)~") != 0;");

		static foreach(k, a; std.traits.Parameters!T)
		{{
			static if (__traits(compiles, 
typeof(ParameterDefaults!T[k]).stringof))
				enum dt = typeof(ParameterDefaults!T[k]).stringof;
			else
				enum dt = "void";

			Parameters ~= 
sParameterReflection(ParameterIdentifierTuple!T[k], a.stringof, 
ParameterDefaults!T[k].stringof, dt);			
			static foreach(j, a; EnumMembers!ParameterStorageClass)
				mixin(`Parameters[k].StorageClass.Is`~strip(capitalize(to!string(a)),"_")~"
= (ParameterStorageClassTuple!T[k] & ParameterStorageClass."~to!string(a)~") !=
0;");
		}}

		

	}
}

/*
	Encapsulates a Delegate Reflection
*/
class cDelegateReflection : cFunctionReflection
{
	override string DType() { return "delegate"; }

	auto Reflect(alias T)()
	{
		super.Reflect!T;

		return this;
	}
}








/*
	Takes a type and builds it's inheritance and uses hierarchy.
*/
auto Reflect(T...)()
{		
	static foreach(t; ["class", "interface", "struct", "union", 
"function", "delegate", "enum"])
		mixin("static if (is"~capitalize(t)~"!(T)) { auto model = new 
c"~capitalize(t)~"Reflection(); model.Reflect!T(); } ");

	return model;
}




auto printModelHierarchy(T, int depth = 1)(T model)
{static if (depth > 20) return "ERROR"; else {
	auto res = "";
	auto tab = "\t".replicate(depth);
	if (model.TypeName == "Object") return "Object";
	
	// Print out basic info, allMembers returns funky as it returns 
the most derived first from top to bottom. Generally we want to 
display the least derived from top to bottom. Reversing gets us 
bottom to top though so we must declare them in reverse order in 
the types.
	static foreach(k, m; [Reverse!(__traits(allMembers, T))])
	{{
		static if (!canFind(["factory", "Monitor", "opEquals", "opCmp", 
"toHash", "toString", "Reflect"], m))		// Ignore these
		{
			mixin(`alias Q = TypeOf!(T.`~m~`);`);	
			
			static if (isPrimitive!Q || isString!Q)																	// 
Print primitives
				mixin(`res ~= tab~m~" = 
"~to!string(model.`~m.stringof[1..$-1]~`)~"\n";`);	
			else static if (__traits(compiles, is(ReturnType!Q == string)) 
&& is(ReturnType!Q == string))			// Print functions that return 
strings
			{			
				mixin(`res ~= tab~m~" = 
"~model.`~m.stringof[1..$-1]~`()~"\n";`);	
			}
			else																									// Print other types
			{				
				static if (is(Q U : U[]) && !is(U == Object))														// 
Print Arrays, handling recursion
				{{										
					mixin("auto arr = model."~m~";");
					if (arr.length > 0)
					{
						string[] r;
						foreach(a; arr)
						{
								static if (is(U : cBaseReflection))
									r ~= printModelHierarchy!(U, depth + 1)(a).strip("\t\n 
,");								
								else
									r ~= to!string(a).strip("\t\n ,");
						}

						// Prepare results
						auto q = r.join(",");
						if (q.length > 60 || r.canFind("\n") || r.canFind(","))
							static if (is(U : cBaseReflection))
								q = 
"\n"~tab~"\t"~r.join("\n"~tab~"\t,\n"~tab~"\t")~"\n"~tab;
							else
								q = "\n"~tab~"\t"~r.join(",\n"~tab~"\t")~"\n"~tab;
						res ~= tab~m~" = ["~q~"]\n";
					} else res ~= tab~m~" = []\n";
					
				}}
			}
		
		
		}
	}}


	return res;
}}











mReflect.d-mixin-207(207): Deprecation: 
`mModel.cDerived!int.cDerived.testField2` is not visible from 
module `mReflect`
mReflect.d-mixin-208(209): Deprecation: 
`mModel.cDerived!int.cDerived.testField2` is not visible from 
module `mReflect`
	Id = cDerived
	TypeName = cDerived!int
	FullName = mModel.cDerived!(int)
	ModuleName = mModel
	MangledName = C6mModel__T8cDerivedTiZQm
	Protection = public
	Body =
	Uses = []
	Attributes = [
		sAttributeReflection("fdsa", "string"),
		sAttributeReflection("8", "int")
	]
	IsNested = false
	IsInnerClass = false
	HasUnsharedAliasing = true
	HasNested = false
	HasIndirections = true
	HasElaborateDestructor = false
	HasElaborateCopyConstructor = false
	HasElaborateAssign = false
	HasAliasing = true
	TypeParameters = []
	AliasThis = [sTypeReflection("type", "cType")]
	NestedAggregates = []
	DerivedClasses = []
	Fields = [
		Id = type
		TypeName = cDerived!int.type
		FullName = mModel.cDerived!(int).type
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection = public
		Body =
		Uses = []
		Attributes = []
		DType = field
		,
		Id = testField1
		TypeName = cDerived!int.testField1
		FullName = mModel.cDerived!(int).testField1
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection = public
		Body =
		Uses = []
		Attributes = [sAttributeReflection("XXXRRERES", "string")]
		DType = field
		,
		Id = testField2
		TypeName = cDerived!int.testField2
		FullName = mModel.cDerived!(int).testField2
		ModuleName = mModel
		MangledName = C6mModel__T8cDerivedTiZQm
		Protection = private
		Body =
		Uses = []
		Attributes = [sAttributeReflection("XXXRRERES4", "string")]
		DType = field
	]
	Methods = [
		Id = foo
		TypeName = int()
		FullName = mModel.cDerived!(int).foo
		ModuleName = mModel
		MangledName = 6mModel__T8cDerivedTiZQm3foo
		Protection = public
		Body =
		Uses = []
		Attributes = [sAttributeReflection("A", "string")]
		Signature = int()
		NumArgs = 0
		Linkage = D
		ReturnType = int
		Parameters = []
		DType = delegate
		Overloads = []
		,
		Id = foo
		TypeName = pure nothrow  nogc  safe int(int, int)
		FullName = mModel.cDerived!(int).foo
		ModuleName = mModel
		MangledName = 6mModel__T8cDerivedTiZQm3foo
		Protection = public
		Body =
		Uses = []
		Attributes = [sAttributeReflection("A", "string")]
		Signature = pure nothrow  nogc  safe int(int, int)
		NumArgs = 2
		Linkage = D
		ReturnType = int
		Parameters = [
			sParameterReflection("", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false)),
			sParameterReflection("", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false))
		]
		DType = delegate
		Overloads = []
		,
		Id = ValueProp
		TypeName =  property int()
		FullName = mModel.cDerived!(int).ValueProp
		ModuleName = mModel
		MangledName = _D6mModel__T8cDerivedTiZQm9ValuePropMFNdZi
		Protection = public
		Body =
		Uses = []
		Attributes = [sAttributeReflection("VP att", "string")]
		Signature =  property int()
		NumArgs = 0
		Linkage = D
		ReturnType = int
		Parameters = []
		DType = delegate
		Overloads = []
	]
	InheritedInterfaces = [
		Id = iX
		TypeName = iX
		FullName = mModel.iX
		ModuleName = mModel
		MangledName = C6mModel2iX
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id =
			TypeName =
			FullName =
			ModuleName =
			MangledName =
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = []
		DType = interface
		InheritedInterfaces = []
		,
		Id = iY
		TypeName = iY
		FullName = mModel.iY
		ModuleName = mModel
		MangledName = C6mModel2iY
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id =
			TypeName =
			FullName =
			ModuleName =
			MangledName =
			Protection =
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = []
		DType = interface
		InheritedInterfaces = []
	]
	DType = class
	IsAbstract = false
	Alignment = 8
	InheritedClasses = [
		Id = cBase
		TypeName = cBase
		FullName = mModel.cBase
		ModuleName = mModel
		MangledName = C6mModel5cBase
		Protection = public
		Body =
		Uses = []
		Attributes = []
		IsNested = false
		IsInnerClass = false
		HasUnsharedAliasing = true
		HasNested = false
		HasIndirections = true
		HasElaborateDestructor = false
		HasElaborateCopyConstructor = false
		HasElaborateAssign = false
		HasAliasing = true
		TypeParameters = []
		AliasThis = []
		NestedAggregates = []
		DerivedClasses = []
		Fields = [
			Id = testField
			TypeName = cBase.testField
			FullName = mModel.cBase.testField
			ModuleName = mModel
			MangledName = C6mModel5cBase
			Protection = public
			Body =
			Uses = []
			Attributes = []
			DType = field
		]
		Methods = [
			Id = fooBase
			TypeName = cBase(iBase)
			FullName = mModel.cBase.fooBase
			ModuleName = mModel
			MangledName = _D6mModel5cBase7fooBaseMFCQy5iBaseZCQBiQBe
			Protection = public
			Body =
			Uses = []
			Attributes = []
			Signature = cBase(iBase)
			NumArgs = 1
			Linkage = D
			ReturnType = cBase
			Parameters = [
				sParameterReflection("", "iBase", "void", "void", 
sStorageClass(false, false, false, false, false, false))
			]
			DType = delegate
			Overloads = []
		]
		InheritedInterfaces = [
			Id = iBase
			TypeName = iBase
			FullName = mModel.iBase
			ModuleName = mModel
			MangledName = C6mModel5iBase
			Protection = public
			Body =
			Uses = []
			Attributes = [
				sAttributeReflection("fdsa", "string"),
				sAttributeReflection("4", "int")
			]
			IsNested = false
			IsInnerClass = false
			HasUnsharedAliasing = true
			HasNested = false
			HasIndirections = true
			HasElaborateDestructor = false
			HasElaborateCopyConstructor = false
			HasElaborateAssign = false
			HasAliasing = true
			TypeParameters = []
			AliasThis = []
			NestedAggregates = []
			DerivedClasses = []
			Fields = [
				Id =
				TypeName =
				FullName =
				ModuleName =
				MangledName =
				Protection =
				Body =
				Uses = []
				Attributes = []
				DType = field
			]
			Methods = [
				Id = fooBase
				TypeName = iBase(iBase)
				FullName = mModel.iBase.fooBase
				ModuleName = mModel
				MangledName = _D6mModel5iBase7fooBaseMFCQyQtZQg
				Protection = public
				Body =
				Uses = []
				Attributes = []
				Signature = iBase(iBase)
				NumArgs = 1
				Linkage = D
				ReturnType = iBase
				Parameters = [
					sParameterReflection("", "iBase", "void", "void", 
sStorageClass(false, false, false, false, false, false))
				]
				DType = delegate
				Overloads = []
			]
			DType = interface
			InheritedInterfaces = []
		]
		DType = class
		IsAbstract = false
		Alignment = 4
		InheritedClasses = [Object]
	]

	Id = foo
	TypeName =
	FullName = main.foo
	ModuleName = main
	MangledName = _D4main3fooFiKdfZv
	Protection = public
	Body =
	Uses = []
	Attributes = [
		sAttributeReflection("attr!string(\"test\", 432)", 
"attr!string"),
		sAttributeReflection("4", "int")
	]
	DType = function
	Signature = void(int x, ref double y, float z = 43234.3F)
	NumArgs = 3
	Linkage = D
	ReturnType = void
	Parameters = [
		sParameterReflection("x", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false)),
		sParameterReflection("y", "double", "void", "void", 
sStorageClass(false, false, false, true, false, false)),
		sParameterReflection("z", "float", "43234.3F", "float", 
sStorageClass(false, false, false, false, false, false))
	]

	Id = eEnum
	TypeName = eEnum
	FullName = mModel.eEnum
	ModuleName = mModel
	MangledName = E6mModel5eEnum
	Protection = public
	Body =
	Uses = []
	Attributes = [sAttributeReflection("4", "int")]
	DType = enum
	BaseType = long
	Values = [
		sValueReflection("X", "cast(eEnum)0L", []),
		sValueReflection("Y", "cast(eEnum)43L", []),
		sValueReflection("Z", "cast(eEnum)4L", 
[sAttributeReflection("sdfdsa", "string"), 
sAttributeReflection("3", "int")])
	]












If one looks at the overloads issue, we have:

Methods = [
		Id = foo
		TypeName = int()
		FullName = mModel.cDerived!(int).foo
		ModuleName = mModel
		MangledName = 6mModel__T8cDerivedTiZQm3foo
		Protection = public
		Attributes = [sAttributeReflection("A", "string")]
		Signature = int()
		NumArgs = 0
		Linkage = D
		ReturnType = int
		Parameters = []
		DType = delegate
		Overloads = []
		,
		Id = foo
		TypeName = pure nothrow  nogc  safe int(int, int)
		FullName = mModel.cDerived!(int).foo
		ModuleName = mModel
		MangledName = 6mModel__T8cDerivedTiZQm3foo
		Protection = public
		Attributes = [sAttributeReflection("A", "string")]
		Signature = pure nothrow  nogc  safe int(int, int)
		NumArgs = 2
		Linkage = D
		ReturnType = int
		Parameters = [
			sParameterReflection("", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false)),
			sParameterReflection("", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false))
		]
		DType = delegate
		Overloads = []
		,


and

Id = foo
	TypeName =
	FullName = main.foo
	ModuleName = main
	MangledName = _D4main3fooFiKdfZv
	Protection = public
	Attributes = [
		sAttributeReflection("attr!string(\"test\", 432)", 
"attr!string"),
		sAttributeReflection("4", "int")
	]
	DType = function
	Signature = void(int x, ref double y, float z = 43234.3F)
	NumArgs = 3
	Linkage = D
	ReturnType = void
	Parameters = [
		sParameterReflection("x", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false)),
		sParameterReflection("y", "double", "void", "void", 
sStorageClass(false, false, false, true, false, false)),
		sParameterReflection("z", "float", "43234.3F", "float", 
sStorageClass(false, false, false, false, false, false))
	]



I'm having to do a trick to make things work here by using the 
method to fill in some information and the overload to fill in 
the rest... But regardless the overloads are not returning 
Parameter names while the function does(yet it is the exact same 
code. This might be a bug in the compiler.

sParameterReflection("", "int", "void", "void", 
sStorageClass(false, false, false, false, false, false)),

The first element should be x but is empty. For the local 
function it gives "x" which is valid. Same code but fails for 
methods.


Another issue is that the protection is wrong for the overload... 
mainly because getting the protection of an overload fails.


I will mess with it some more later, it is at least not as bad as 
I thought but requires a bit of work to get everything to fall in 
place. Would be nice to know though what is going on with the 
overloads and why the protection is failing to return the correct 
value.


There might be other subtle issues but something is amiss. I have 
tried to generalize the code as much as possible and it is much 
closer than it as before. Many of the same problems still 
exist(protection warnings, field typing, etc).
Apr 03 2019
parent Alex <AJ gmail.com> writes:
I've created a project page so it will be easier than pasting:

https://github.com/IncipientDesigns/Dlang_Reflect
Apr 03 2019
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 4 April 2019 at 02:19:48 UTC, Alex wrote:
 But because there is no `T` for fields we can't do it so easy, 
 so we have to construct the name and use string mixins.
You should pass fields as an alias more often than not. It is a little tricky when you want to read/write it (the alias is not a value, so you need to pass a pointer or something separately), but for reflection you want to use the alias. UDAs do not exist on types per se, since they are tied to symbols.
 mixin(`static foreach(a;  __traits(getAttributes, 
 `~T.stringof~`.`~name~`))
This shouldn't use mixin at all, and even if you do use mixin, it shouldn't be using stringof; .stringof is a code smell and should almost never be used. This bypasses the local symbol. That is actually the reason why you get a visibility error. Just use T. more info: https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854
Apr 05 2019
parent Alex <AJ gmail.com> writes:
On Friday, 5 April 2019 at 16:00:26 UTC, Adam D. Ruppe wrote:
 On Thursday, 4 April 2019 at 02:19:48 UTC, Alex wrote:
 But because there is no `T` for fields we can't do it so easy, 
 so we have to construct the name and use string mixins.
You should pass fields as an alias more often than not. It is a little tricky when you want to read/write it (the alias is not a value, so you need to pass a pointer or something separately), but for reflection you want to use the alias. UDAs do not exist on types per se, since they are tied to symbols.
 mixin(`static foreach(a;  __traits(getAttributes, 
 `~T.stringof~`.`~name~`))
This shouldn't use mixin at all, and even if you do use mixin, it shouldn't be using stringof; .stringof is a code smell and should almost never be used. This bypasses the local symbol. That is actually the reason why you get a visibility error. Just use T. more info: https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854
Ok, I was able to remove all of them I believe. I got in the habit because I routinely switch the mixin to a pragma(msg and having the actual name their is far more informative than T, helps find out if the type if wrong). I don't know why it is code smell, since it should ultimately end up the same. Fixing this did not solve the `private` error. // Get the attributes for the field mixin(`Protection = __traits(getProtection, T.`~name~`);`); In any case, the code seems to be fundamentally working now, as I mentioned in a previous thread. I just have to finish dealing with all the other types, which shouldn't be a huge problem. ----------- As far as using an alias as fields, The code is this: // Get the fields alias n = FieldNameTuple!T; static foreach(k, f; std.traits.Fields!T) {{ Fields ~= (new cFieldReflection()).Reflect!(Alias!T, n[k]); }} I am passing the base type and the name then building it inside Reflect, I've tried passing various things so I could simplify the code, but I am unable to get anything to work. auto Reflect(alias T, string name)() { static if (name != "") { // There is no field type so must manually construct and duplicate code, else we could reflect directly mixin(`import `~moduleName!T~`;`); // mixin(`super.Reflect!(T.`~name~`);`); Id = name; static if (__traits(compiles, TypeName = T.stringof)) TypeName = T.stringof~"."~name; ModuleName = moduleName!(T); FullName = fullyQualifiedName!(T)~"."~name; MangledName = mangledName!T; // Get the attributes for the field mixin(`Protection = __traits(getProtection, T.`~name~`);`); mixin(`static if (__traits(compiles, __traits(getProtection, T.`~name~`))) static foreach(a; __traits(getAttributes, T.`~name~`)) Attributes ~= sAttributeReflection(to!string(a), typeof(a).stringof);`); } return this; } If I could call super.Reflect then it would fill in all the info and I could remove the Id = name ... MangledName lines and consolidate code. But I have not figure out how to get any of that code to work without constructing the symbols by hand.
Apr 06 2019