DIP66 - Multiple alias this
IgorStepanov via Digitalmars-d
digitalmars-d at puremagic.com
Sun Oct 12 04:45:55 PDT 2014
On Sunday, 12 October 2014 at 08:36:05 UTC, Marc Schütz wrote:
> On Sunday, 12 October 2014 at 04:31:22 UTC, Walter Bright wrote:
>> On 10/11/2014 7:23 AM, IgorStepanov wrote:
>>> class A
>>> {
>>> int i;
>>> alias i this;
>>> }
>>>
>>> class B
>>> {
>>> int i;
>>> alias i this;
>>> }
>>>
>>> class C
>>> {
>>> A a;
>>> B b;
>>> alias a this;
>>> alias b this;
>>> }
>>>
>>> void foo(T)(T arg) if(is(T : int))
>>> {
>>> ...
>>> }
>>>
>>> foo(C()); //Should it pass or not?
>>
>> There's a rule with imports that if the same symbol is
>> reachable via multiple paths through the imports, that it is
>> not an ambiguity error. Here, the same type is reachable
>> through multiple alias this paths, so by analogy it shouldn't
>> be an error.
>
> It's the same type, but different symbols; actual accesses
> would be ambiguous. `is(T : int)` shouldn't evaluate to true if
> `int a = T.init;` would fail.
I found an example of a situation that is bothering me.
Let we have a persistence framework, which provides a storing D
object in some persistence storage: DB, file et c.
In introduces paired functions store/load and special type
PersistenceObject.
If stored type is subtype of PersistenceObject it converts to
PersistenceObject and PersistenceObject.load(stream) called for
loading object (and PersistenceObject.store(stream) for storing).
Otherwice if object can't be converted to PersistenceObject it
should be serialized via "serialize" function (or deserialized
via "deserialize").
struct PersistenceFramework
{
void store(T)(T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.store(stream);
}
void store(T)(T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
store(serialize(arg));
}
void load(T)(ref T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.load(stream);
}
void load(T)(ref T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
load(serialize(arg));
}
Stream stream;
}
/****************************************************************
And we have the next types which we want to store and load
*****************************************************************/
struct Role
{
...
}
struct User
{
Role role;
PersistenceObject po;
//...
alias role this;
alias po this;
}
/*****************************************************************/
User u;
persistenceFramework.load(u)
//...
persistenceFramework.store(u);
/******************************************************************/
Role is not subtype of PersistenceObject thus all works ok.
We can store User via User.po and load it again;
Some time later, Role designer decided that Role should be
subtype of PersistenceObject and changed Role definition:
struct Role
{
...
PersistenceObject po;
alias po this;
}
Now, User can not be converted to PersistenceObject because there
are two path to convert: User.po and User.role.po;
Storing code after this change will be copiled successfully (if
we follow your "is" rule), however object will be tried to load
via "void load(T)(ref T arg) if (!is(T : PersistenceObject))".
Because object was saved via "void store(T)(T arg) if (is(T :
PersistenceObject))" at the previous program run, user will not
be loaded succesfully. Moreover, you will get an strange
unexpected program behaviour and will be hard to find real error
cause.
/*****************************************************************/
And finally, if you want to check, if you Type _really_ can be
converted to AnotherType, you can use the next check:
void foo(Type)(Type arg) if (is(typeof({AnotherType x =
Type.init;})))
{
}
More information about the Digitalmars-d
mailing list