Jump to page: 1 2
Thread overview
I give up! I tried to create a reflection library but D's traits are just too screwed up!
Apr 03, 2019
Alex
Apr 03, 2019
Atila Neves
Apr 03, 2019
Alex
Apr 03, 2019
drug
Apr 03, 2019
FeepingCreature
Apr 03, 2019
drug
Apr 03, 2019
Alex
Apr 03, 2019
Atila Neves
Apr 04, 2019
Alex
Apr 04, 2019
Alex
Apr 04, 2019
Alex
Apr 05, 2019
Adam D. Ruppe
Apr 06, 2019
Alex
April 03, 2019
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
{

}





April 03, 2019
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.



April 03, 2019
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


April 03, 2019
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.
April 03, 2019
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 { } }?


April 03, 2019
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.

April 03, 2019
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.
April 03, 2019
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.

April 03, 2019
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!
April 04, 2019
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.













« First   ‹ Prev
1 2