-
-
Notifications
You must be signed in to change notification settings - Fork 416
Remove allocation when comparing type info objects #370
Conversation
-Comparing two TypeInfo objects no longer allocates and compares strings
switch(lhsType) | ||
{ | ||
case TypeInfo.Type.Struct: | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please dedent one level (and you probably don't need those braces either).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dedent what exactl? The entire case blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Usually we just leave out the braces and indent the code within by one level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yuck. Really? I'd argue that the cases need to be indented. It's the fact that the brace was further indented was the problem. I'd never not indent the cases and am not aware of anywhere in Phobos which doesn't (though it may be in there somewhere). And there's nothing wrong with using braces inside the case statements. I usually do and think that it's cleaner if you do (though unlike in C, it's not actually needed for scoping). They just need to line up with the case statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is this is right:
switch (foo)
{
case bar:
baz();
}
Or this, if you really want:
switch (foo)
{
case bar:
{
baz();
}
}
But not this:
switch (foo)
{
case bar:
{
baz();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That, I agree with, but that means that he needs to change it again, because he misunderstood you and made the cases line up with the switch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. Yeah, that's not what I meant. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird. I've always seen cases outdented because they're labels. Indenting the makes the flow less clear IMO. I think outdenting is consistent with the classic style layouts as well. But as long as the code is consistently formatted I'm not going to quibble about spacing.
@@ -67,6 +67,51 @@ struct OffsetTypeInfo | |||
|
|||
class TypeInfo | |||
{ | |||
enum Type | |||
{ | |||
Info, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention is lower case for enum members.
For names that conflict with keywords, we append a _
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we rellay want this in this case? Almost all of these are keywords and would end up with a underscore at the end. Will look quite ugly when using these in code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. That's what we want. That's the coding style. It's what we've done elsewhere when we've needed symbol names which match keywords (e.g. std.traits does it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to mention, most code won't be using these anyway, so even if you think they're a bit ugly, they'll affect a rather small number of programs. Regardless though, it is the agreed upon style for enums.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, camelCase and append underscores if necessary please
This change breaks Phobos unittests. Could you post a fix-up pull for Phobos? |
I didn't have time to check what exactly breaks within the phobos unittests. But I will investigate and fix this, one way or the other. |
ping? |
I'm having exams at the moment, this will have to wait for 3 weeks. |
good luck! |
Conflicts: src/object.di
…us other minor issues.
I have a question about some implementation detail. Please see and discuss: http://forum.dlang.org/thread/kg1pcr$197c$1@digitalmars.com |
Anyone? Should I just choose whatever I find best? |
cc @andralex |
This pull grafts tagged variants onto the TypeInfo, rather than use virtual functions. Why not use virtual functions? That also avoids the whole issue of that .next returns. (I know that the compiler itself uses tagging sometimes! But I'm not sure that is a good idea there, either.) |
Because I designed it with writing vararg functions in mind. With tagging you can write vararg functions by using switch case statements to decide what to do for a certain type. |
Sounds like you could as well compare the TypeInfo.classinfo ptr. Could you please provide a code example of what you intend to do here. |
auto lhsTypedef = cast(const(TypeInfo_Typedef))cast(void*)lhs; | ||
auto rhsTypedef = cast(const(TypeInfo_Typedef))cast(void*)rhs; | ||
return lhsTypedef.name == rhsTypedef.name; | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can you ensure that every unhandled Type has a valid TypeInfo.next field.
You should instead list all TypeInfos with a valid .next
field explicitly.
It's also fairly confusing to read that TypeInfo.Type.double_ is handled by a recursive call with .next is null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They don't need a valid next field. Next may return null which will terminate the (tail) recursion. If TypeInfo.next would not return a const TypeInfo the tail recursion could be replaced by a loop.
Sorry but I don't see a TypeInfo.Type.double_ in the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If TypeInfo.next would not return a const TypeInfo the tail recursion could be replaced by a loop.
This has been resolved with #359, please use a loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kind of bad that we have a const
system that basically requires tail recursion in cases like this one (if you don't want to break the type system), and yet we are (for some reason??) allergic to recursion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an isolated issue with const object references and there is Rebindable to work around it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the problem with tail recursion? D supports functional programming, so why not use it?
Also I'm going to wait until there is agreement that this fix is actually wanted before investing even more time into it which might be wasted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stop that discussion here. I'm basically nitpicking on the readability of this method but it's not a big deal.
No I can not compare the type info ptr because then it will not work across dll boundaries. |
That's an ODR violation and does not need a fix in TypeInfo, see #142. |
Well but until this ODR violation gets fixed a lot of time may pass.
|
OK, that's partly correct, e.g. for TypeInfo_Class of template classes. As it stands the best solution is to handle this differences in opEquals.
What's the problem of replacing switch (ti.type)
{
case TypeInfo.Type.UByte: format(va_arg!ubyte(argptr)); break;
case TypeInfo.Type.UShort: format(va_arg!ushort(argptr)); break;
case TypeInfo.Type.UInt: format(va_arg!uint(argptr)); break;
default: break;
} with if (ti == typeid(ubyte)) format(va_arg!ubyte(argptr));
else if (ti == typeid(ushort)) format(va_arg!ushort(argptr));
else if (ti == typeid(uint)) format(va_arg!uint(argptr)); ? Note that the virtual opEquals will correctly handle comparison. |
BTW I just thought we should create a TypeInfo_Template which is emitted together with a TypeInfo_Class/Struct/... into EVERY object filed using the template instantiation. It's only purpose would be to specialize opEquals for multiple instances. |
I'm beginning to think that this would be much simpler if the test for equality was just a comparison of the pointers, and if the pointers are not equal, then the mangled type string is compared. The compiler can be adjusted to emit the mangled string for each typeinfo instance. |
Your way has the complexity O(N). My way has O(1). Even std.format does not use your way. std.format partially parses the the mangeled type string and does a switch on that. Its also currently not easily possible to unqualify a type info object. If you find out that it is a const T, you can't get the T because calling next() will jump both the const and the T type info object. Just try implementeing a full printf wrapper with the current TypeInfo system. I'm my eyes its unneccessarily difficult to make it work for all types.
But this would compare manageld type strings in most cases, because the common case is that the type info pointers don't match. With my implementation it will just compare two ints in most cases.
AFAIK we already pretend working dll support?
I disagree. I very recently wanted exactly that. I loaded a updated version of the class and wanted it to be the same type so I can make changes to code without restarting the application. See: http://3d.benjamin-thaut.de/?p=25 |
I'll get more into the TypeInfo problems and we will find an appropriate solution but I'll have to defer this until next week. |
You can switch to template vararg functions for performance and I doubt that replacing N pointer comparisions with 1 table lookup would get you a much faster printf wrapper. |
Two distinct TypeInfo_Class instances with different vtables and init[] data should not compare equal. |
In case that two shared libraries instantiate the same template class we get two weak TypeInfo instances. NB: |
That would potentially consume a lot of space. AFAIU we only need mangle comparison for weak TypeInfos. So we should use a dedicated wrapper that forwards all methods similar to TypeInfo_Typedef. class TypeInfo_Weak : TypeInfo
{
override bool opEquals(Object o)
{
if (this is o)
return true;
else if (o.classinfo is TypeInfo_Weak.classinfo)
{
auto c = *cast(const TypeInfo_Weak*)&o;
return this.base == c.base &&
this.mangledName == c.mangledName;
}
return false;
}
// other overrides like TypeInfo_Typedef
TypeInfo base;
string mangledName;
} |
I don't see a very convincing argument to add tagged classes. Most TypeInfo comparison are simple pointer comparisons and weak TypeInfo can get a mangledName field as fallback. It's true that cross-DLL TypeInfo comparison currently fails as well as cross-DLL exception handling. |
@dawgfoto what's the status of this? |
I don't think faster vararg functions are a compelling enough use cases to make TypeInfo a tagged variant. For now replacing the tag comparisons with |
This will still break in case of dlls until a shared druntime is implemented (which will not happen on all plattforms in the near future in my opinion). But if you don't think this is worth it I don't care anymore. |
I opened a Bugzilla 10048 for the issue.
Because it uses |
As request by Andrei Alexandrescu on the newsgroup some time ago, here a pull request for my changes to type info comparison. It has several advantages over the current approach:
-Fixes next() for TypeInfo_Const, TypeInfo_Typedef and TypeInfo_Vector
-Comparing two type info objects will no longer allocate two strings and compare them. This is one step needed to get a non leaking d-runtime when working without a GC.
-Variadic functions can be implemented more easily and efficiently. For a example see: https://github.com/Ingrater/thBase/blob/master/src/thBase/format.d#L257
-It is a lot easier to unqualify type info objects across dll boundaries. For a example see https://github.com/Ingrater/thBase/blob/master/src/thBase/format.d#L13