D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 5719 - [patch] std.conv.to should support structs with custom converters in addition to objects
Summary: [patch] std.conv.to should support structs with custom converters in addition...
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: Other Windows
: P3 enhancement
Assignee: No Owner
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2011-03-07 21:50 UTC by Rob Jacques
Modified: 2017-09-18 00:06 UTC (History)
4 users (show)

See Also:


Attachments
Reduced test case for weird cast behavior. (272 bytes, application/octet-stream)
2012-05-19 04:14 UTC, meh.
Details

Note You need to log in before you can comment on or make changes to this issue.
Description Rob Jacques 2011-03-07 21:50:57 UTC
Currently std.conv.to allows classes to define custom conversion routines to non-class types, but doesn't provide structs with the same functionality. This functionality can be extended to structs by a minor change in one of the toImpl template constraints from
is(S : Object) && !is(T : Object)
to 
((is(S : Object) && !is(T : Object)) || is(S == struct))

The particular toImpl is currently located at Line 687 in git (line 680 in DMD 2.051) and has been reproduced in full below with the patch, along with an updated unit test. The patch passes the unit tests below and the unit tests in my current fork of std.variant and std.json, but I haven't tested it against the entire phobos test suite.

/**
Object-_to-non-object conversions look for a method "to" of the source
object.

Example:
----
class Date
{
    T to(T)() if(is(T == long))
    {
        return timestamp;
    }
    ...
}

unittest
{
    debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
    auto d = new Date;
    auto ts = to!long(d); // same as d.to!long()
}
----
 */
T toImpl(T, S)(S value) if ( ((is(S : Object) && !is(T : Object)) || is(S == struct)) && !isSomeString!T
        && is(typeof(S.init.to!(T)()) : T))
{
    return value.to!T();
}

unittest
{
    debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
    class B { T to(T)() { return 43; } }
    auto b = new B;
    assert(to!int(b) == 43);
    struct C { T to(T)() { return 43; } }
    C c;
    assert(to!int(c) == 43);
}
Comment 1 Rob Jacques 2011-07-01 13:04:20 UTC
I've noticed that with generic code that

Target toImpl(Target, Source)(Source value)
if (implicitlyConverts!(Source, Target))

can cause an error via multiple template matches with

T toImpl(T, S)(S value) if (is(S : Object) && is(T : Object))

and

T toImpl(T, S)(S value)
    if (((is(S : Object) && !is(T : Object)) || is(S == struct))
    && !isSomeString!T && is(typeof(S.init.to!(T)()) : T))

Both issues can be simply fixed by adding an additional template constraint:
    && !implicitlyConverts!(S,T)
Comment 2 Jonathan M Davis 2011-07-01 13:12:48 UTC
There are several pull requests affect std.conv.to at the moment which will change the situation and which will likely be merged in soon. Among other things, they get rid of the member function to conversion and make std.conv.to work with overloaded opCast.

https://github.com/D-Programming-Language/phobos/pull/118
https://github.com/D-Programming-Language/phobos/pull/119
https://github.com/D-Programming-Language/phobos/pull/122
Comment 3 Kenji Hara 2011-10-09 03:42:53 UTC
(In reply to comment #2)
> https://github.com/D-Programming-Language/phobos/pull/118
> https://github.com/D-Programming-Language/phobos/pull/119
> https://github.com/D-Programming-Language/phobos/pull/122

The three pull requests are already merged, and now std.conv.to supports both two kinds of custom conversions:

1) object to non-object conversion with opCast
   to!T(s)
   --> s.opCast!T()

2) non-object to object conversion with construction
   to!T(s)
   --> new T(s) // if T is class
       T(s)     // if T is struct

Today, is this a closable issue?
Comment 4 Rob Jacques 2011-10-09 10:06:45 UTC
(In reply to comment #3)
> (In reply to comment #2)
> > https://github.com/D-Programming-Language/phobos/pull/118
> > https://github.com/D-Programming-Language/phobos/pull/119
> > https://github.com/D-Programming-Language/phobos/pull/122
> 
> The three pull requests are already merged, and now std.conv.to supports both
> two kinds of custom conversions:
> 
> 1) object to non-object conversion with opCast
>    to!T(s)
>    --> s.opCast!T()
> 
> 2) non-object to object conversion with construction
>    to!T(s)
>    --> new T(s) // if T is class
>        T(s)     // if T is struct
> 
> Today, is this a closable issue?

By 'object' do you mean both classes and structs? Specifically, these conversions seem to be missing:

3) non-object to non-object conversion with opCast
    to!T(s)
    --> s.opCast!T()

4) non-object to non-object conversion with construction
   to!T(s)
       T(s)     // if T is struct

But this might be a case of semantics, not implementation. (I generally don't refer to structs as 'objects')
Comment 5 Jonathan M Davis 2011-10-09 14:25:50 UTC
Structs are very much objects. Whether something is an object or not has nothing to do with whether it's a reference type or a value type. Take C++ for example. In C++, classes are generally value types rather than reference types, but they're very much objects. The same goes for whether an object is polymorphic or not. It's an object regardless of whether it's polymorphic. The concept of an object in OO programming has nothing to do with the difference between a struct and a class in D.

You're going to confuse people and generally be confused by others if you refer to instances of structs as if they weren't objects. If you want to be clear about the differences between structs and classes, you need to refer to structs and classes and not try and use the term object to differentiate them. Instances of both are objects.
Comment 6 Brad Roberts 2011-10-09 16:34:26 UTC
Careful, given that the name of the entity at the root of the hierarchy of classes is called 'Object', which structs have no relationship to, there is an ambiguity in terminology.
Comment 7 Jonathan M Davis 2011-10-09 16:48:59 UTC
True, there's a difference between something which is an instance of Object and something which is an object. But D structs are still very much OO and are definitely objects.
Comment 8 meh. 2012-05-19 04:14:25 UTC
Created attachment 1106 [details]
Reduced test case for weird cast behavior.
Comment 9 meh. 2012-05-19 04:17:51 UTC
Sorry for that, bugzilla did something funny, I don't know why it attached that to this bug.
Comment 10 Simen Kjaeraas 2017-09-18 00:06:53 UTC
Examples given by Rob Jacques work in D today.