www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - Template Codegen + Tupleof

reply Kyle Furlong <kylefurlong gmail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

So I've been trying to write a function that will yield an array of 
structs containing the types and offsets of each member of a UDT. 
Naturally, I looked to use tupleof and templating to pull this off.

In the process, I think I've come across a bug. Although, since I don't 
regularly program in D, I'm not sure if what I'm trying to do is the 
Right Way. Its also why I'm not posting a Bugzilla on this just quite yet.

The attachment is a bit lengthy because I couldnt reduce it any more 
than that. Let me know what you think.
Feb 06 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Kyle Furlong wrote:
 So I've been trying to write a function that will yield an array of 
 structs containing the types and offsets of each member of a UDT. 
 Naturally, I looked to use tupleof and templating to pull this off.
 
 In the process, I think I've come across a bug. Although, since I don't 
 regularly program in D, I'm not sure if what I'm trying to do is the 
 Right Way. Its also why I'm not posting a Bugzilla on this just quite yet.
 
 The attachment is a bit lengthy because I couldnt reduce it any more 
 than that. Let me know what you think.

That's a definite bug. Wrong code generation for the second template instantiation. It seems to think your second struct has the same member types as the first one. The tupleof reports the right tuple, though, so the bug seems to be in the code that implements the static foreach. More reduced version: ----- void Fields(C)() { foreach(i, a; typeof(C.tupleof)) { // fails for second instantiation: // "test.d(7): static assert (is(int == real)) is false" static assert(is(typeof(a) == typeof(C.tupleof)[i])); } } struct MyStruct1 { int afield; } struct MyStruct2 { real afield; } void main() { Fields!(MyStruct1); Fields!(MyStruct2); } ----- P.S. is there any particular reason you're computing offsets manually when you have the type as a template parameter and could just use "C.tupleof[i].offsetof" :P ? That would save you from having to worry about alignment issues, interface vtable pointers and more. Your code also doesn't take base classes into account, but that may just be because you reduced it? If not, be warned that tupleof doesn't seem to return base class members. Use of "static if(is(C Base == super))" and recursion should be able to help you here.
Feb 06 2007
parent reply Kyle Furlong <kylefurlong gmail.com> writes:
Frits van Bommel wrote:
 Kyle Furlong wrote:
 So I've been trying to write a function that will yield an array of 
 structs containing the types and offsets of each member of a UDT. 
 Naturally, I looked to use tupleof and templating to pull this off.

 In the process, I think I've come across a bug. Although, since I 
 don't regularly program in D, I'm not sure if what I'm trying to do is 
 the Right Way. Its also why I'm not posting a Bugzilla on this just 
 quite yet.

 The attachment is a bit lengthy because I couldnt reduce it any more 
 than that. Let me know what you think.

That's a definite bug. Wrong code generation for the second template instantiation. It seems to think your second struct has the same member types as the first one. The tupleof reports the right tuple, though, so the bug seems to be in the code that implements the static foreach. More reduced version: ----- void Fields(C)() { foreach(i, a; typeof(C.tupleof)) { // fails for second instantiation: // "test.d(7): static assert (is(int == real)) is false" static assert(is(typeof(a) == typeof(C.tupleof)[i])); } } struct MyStruct1 { int afield; } struct MyStruct2 { real afield; } void main() { Fields!(MyStruct1); Fields!(MyStruct2); } ----- P.S. is there any particular reason you're computing offsets manually when you have the type as a template parameter and could just use "C.tupleof[i].offsetof" :P ? That would save you from having to worry about alignment issues, interface vtable pointers and more. Your code also doesn't take base classes into account, but that may just be because you reduced it? If not, be warned that tupleof doesn't seem to return base class members. Use of "static if(is(C Base == super))" and recursion should be able to help you here.

Yes, thanks for the verification of the bug. But yeah, my code is not very sophisticated yet due to frustration with the bug. Once this is fixed I can move on to a complete correct solution.
Feb 06 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Kyle Furlong wrote:
 Yes, thanks for the verification of the bug. But yeah, my code is not 
 very sophisticated yet due to frustration with the bug. Once this is 
 fixed I can move on to a complete correct solution.

By the way, I've got something similar working pretty well (code attached). I'm not using a function though, just a raw template that aliases to a struct instance with type information. That type information includes pointers to recursively generated type information for any members. The current public interface is basically: ---- /// Represents a type. At most one of fields and base will be non-null. struct RttiData { /** The TypeInfo of the represented type. * * Note: Class references use the TypeInfo of the class, their bodies are * represented by the TypeInfo of ClassBody!(ClassName). */ const TypeInfo typeinfo; /** The type of the data pointed to if this instance represents a pointer * or class reference. */ const RttiData* base; /** The fields of the type. * * If the type is a class body, struct or union, this contains the * _fields of the type. * For dynamic arrays, this contains their length and ptr, * for static arrays their elements, and * for delegates their context pointer and function pointer. */ const FieldData[] fields; // Plus implementations of toString, opEquals, opCmp and toHash } /// Represents a field in a class body, struct or union. struct FieldData { /// A pointer to the type data of the type of the field. const RttiData* rtti; /// The _offset of this field from the start of the data block. const size_t offset; // Plus toString, opEquals, opCmp and toHash } /// Aliases to a const RttiData instance describing T. template Rtti(T); /** RttiData instances for class bodies use the TypeInfo of ClassBody, * templated on the class, as their type. * (the class' own TypeInfo is used for the reference) * * For instance, Rtti!(Object).base.typeinfo == typeid(ClassBody!(Object)). * * This type does not have any members, and its only value is the fact that it * has TypeInfo data associated with it. */ struct ClassBody(T) {} ----- Some notes: * Classes are treated as pointers, their bodies as structs. * Delegates and dynamic arrays are considered equivalent to structs (with void*/void function() or size_t/T* members, respectively). Similarly, associative arrays are treated as void*s. * It requires a static constructor for each aggregate type, and for each of their fields. (Though fields of the same type and offset from different aggregates should be folded together by DMD) * When compiled with version=ShowHiddenClassFields the generated class metadata monitor and vtable pointers (including those for interface vtables). Unportabilities and bugs :(: * It assumes that static constructors of alias member parameters have been run before that of the template itself. Fortunately this seems to be exactly what DMD does. * Unions are used to avoid some extra static constructors. (Unless disabled by removing version=UnionHack, but that of course introduces extra static constructors) These assume that a dynamic array has the exact layout of a struct { size_t length; void* ptr; }. * Because of DMD bug #902 you can't instantiate the templates (including internal ones) from more than one module without getting 'multiple definition' errors. * Generated information 'unfolds' typedefs, aliasing to the information generated for the base type. I haven't found any way around this without using a static constructor, but I don't think it's worth it. * The current version requires Tango, but that can easily be changed. * Assumes the current DMD layout for dynamic arrays and delegates. * The extra data generated with version=ShowHiddenClassFields seems to be correct for DMD, but I have no idea if (a) it is correct in all cases and (b) if it's correct for GDC at all. Another "problem" is that I've yet to find a good way to handle dynamic arrays. Currently they're identical to a struct { size_t length; void* ptr; }, but in this case it's known (at least at run-time) to how many elements that pointer points. I have yet to find a way to express this in my data. Unless you count 'If RttiData.typeinfo.toString() (or toUtf8(), for Tango users) ends in "[]", it's a dynamic array'. *phew* long post. Hope I didn't forget anything...
Feb 06 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bugzilla'd: http://d.puremagic.com/issues/show_bug.cgi?id=932
Feb 06 2007
parent Kyle Furlong <kylefurlong gmail.com> writes:
Frits van Bommel wrote:
 Bugzilla'd: http://d.puremagic.com/issues/show_bug.cgi?id=932

Thanks for doing it.
Feb 06 2007