Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
February 06, 2007 Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Attachments: | 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. |
February 06, 2007 Re: Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kyle Furlong | 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.
|
February 06, 2007 Re: Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kyle Furlong | Bugzilla'd: http://d.puremagic.com/issues/show_bug.cgi?id=932 |
February 06, 2007 Re: Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | 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.
|
February 06, 2007 Re: Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | Frits van Bommel wrote:
> Bugzilla'd: http://d.puremagic.com/issues/show_bug.cgi?id=932
Thanks for doing it.
|
February 06, 2007 Re: Template Codegen + Tupleof | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kyle Furlong Attachments: | 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...
|
Copyright © 1999-2021 by the D Language Foundation