Bug 20117 – std.typecons.Typedef has opCmp when base type does not

Status
NEW
Severity
normal
Priority
P3
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2019-08-08T16:51:54Z
Last change time
2024-12-01T16:35:24Z
Assigned to
No Owner
Creator
Atila Neves
Moved to GitHub: phobos#10382 →

Comments

Comment #0 by atila.neves — 2019-08-08T16:51:54Z
---------------------------------- import std.traits; import std.typecons; alias MyInt = Typedef!int; pragma(msg, hasMember!(int, "opCmp")); pragma(msg, hasMember!(MyInt, "opCmp")); ---------------------------------- This prints false then true. It doesn't work very well in generic code.
Comment #1 by simen.kjaras — 2019-08-09T07:14:08Z
This would perhaps more correctly be filed as 'int does not have opCmp'. Since int does support the <, <=, >=, > operators it kinda, sorta has opCmp, but no member by that name. That's not really an option for Typedef, as it's not a magic type like int. At the same time, we absolutely expect MyInt.init < MyInt.init to compile. There may be a good argument for adding opCmp and friends to built-in types to make generic code more generic, but removing them from Typedef is no solution.
Comment #2 by simen.kjaras — 2019-08-09T07:51:39Z
Alternatively, Typedef should contain only a single member and use aliases for the rest. Something like this: struct Typedef(T, T init = T.init, string cookie=null) { // Renaming the current Typedef: TypedefImpl!(T, init, cookie) _payload; alias _payload this; static foreach (e; __traits(allMembers, T)) mixin("alias "~e~" = _payload."~e~";"); }
Comment #3 by atila.neves — 2019-08-09T08:21:20Z
But `int` *doesn't* have `opCmp`. I found this by trying to wrap a generic type for Python and implementing the comparison. std.typecons.Typedef completely broke things and I have to special-case for it, because `hasMember!(Typedef, "opCmp")` returns true, but then one can't actually forward to it because it doesn't actually exist. Incidentally, the reason the bug happens is because `Typedef` uses `Proxy` in its implementation, and `Proxy` defines `opCmp`.
Comment #4 by simen.kjaras — 2019-08-09T11:37:22Z
But `int` *is* comparable using <, <=, >=, and >, so any (non-built-in) type pretending to be an int needs to have `opCmp`. What you're asking for is basically to cripple `Typedef` because you need to write some workaround code. Alternatively, you're asking for what I said in comment 2, but have elected to ignore that. Actually, even with my `alias this` solution in comment 2, you get two members: `_payload`, and `opAssign`. The former we've defined, the latter the compiler makes for us. `int` does not have `opAssign`, so we can't forward to that, thus again causing possible issues. In fact, `int` doesn't have any members at all, if you ask `__traits(allMembers)`. Thus, any member whatsoever in `Typedef!int` could be problematic in generic code. I hope we can agree that `Typedef!int` needs to have at least one member. Actually, we can get rid of all fields by (ab)using `align` and some creative casting: template Typedef(T) { align ((T.sizeof + 1) & -2) // powers of 2 only struct Typedef { ref auto _payload() { import std.typecons : TypedefImpl = Typedef; return *cast(TypedefImpl!T*)&this; } alias _payload this; // alias all members of the wrapped type static if (__traits(compiles, __traits(allMembers, T))) static foreach (e; __traits(allMembers, T)) mixin("alias _payload."~e~" "~e~";"); } } This still leaves us with a single method (`_payload()`) that I haven't found a way to get rid of yet. Also, it's hella ugly.
Comment #5 by robert.schadek — 2024-12-01T16:35:24Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/phobos/issues/10382 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB