## digitalmars.D - Proposal: Implicit type conversion for structs

• Jaak (58/58) Oct 25 2006 Hello
• Bill Baxter (29/122) Oct 25 2006 Maybe you can try to explain this once more in a format of
• Jaak (32/62) Oct 25 2006 The main idea is achieving transparent interoperability of types.
• Kristian (49/54) Oct 26 2006 an =
• Jaak (21/82) Oct 27 2006 One could possibly require that the shortest conversion chain is always
Jaak <jpdt telkomsa.net> writes:
```Hello

I propose the following extension to structs:

One can define methods for struct T of the type:

T opImplicitConvert(T2 t)

which allows the implicit conversion of variables of
type T2 to type T.
(the name itself is not important, others possibilities:
opImpConvFrom, opICast_r, etc.)

A conversion does no make sense if the types T2 and T
are the same, as that can cause an infinite chain
of conversions.

i.e.   T opImplicitConvert(T t)  is illegal

We now get opAssign semantics without breaking
or changing the way assignments currently work.

Example code:

struct Vector {
float[] elems;

...

Vector opImplicitConvert(float[] a)
{
Vector v;
v.elems = a.dup;
return v;
}
}

...

Vector u, v;

// Current system
u = v;  // Currently OK
u = [1f 2f 3f]; // ERROR

// Proposed system
u = v;  // Still OK
v = [1f 2f 3f]; // OK, as Vector.opImplicitConvert(float[]) exists

Some possible enhancements:

1. A smarter compiler may be able to figure out conversions chains if
a direct conversion is not possible.
I.e.

MyVector a;
YourVector b;
HisVector c;

// uses Vector.opImpConv(YourVector.opImpConv(HisVector)) if
// Vector.opImpConv(HisVector) does not exist
a = c;

2. If property syntax can be used to add methods to structs, one can do
the following:

Vector opImplicitConvert(Vector, OtherVector w) {
Vector t;
t.elems = w.elems;
return t;
}

// Then, variables of type OtherVector can be implicitly
// converted to type Vector. This is especially useful if
// the source implementing the struct Vector is not available.

OtherVector w;
v = w; // expands to v = v.opImplicitConvert(w);

-
Jaak
```
Oct 25 2006
Bill Baxter <dnewsgroup billbaxter.com> writes:
```Maybe you can try to explain this once more in a format of
1. Here's a problem currently faced by developers
2. here's a suggestion for how to fix it
3. heres how my suggestion resolves that problem
4. here's why I think this is the best/only way to resolve the problem.

solve to begin with, so it's hard to get excited about the solution.
It's also difficult for others to offer you alternative solutions to the
problem, not knowing what the problem is to begin with.

That said, the proposal sounds reminiscent of C++'s overloadable
(implicit) conversion operators, which have caused me more trouble than
benefit.  E.g.
operator float() const { return aFloat; }

Item 5 from Scott Meyer's Effective C++:
"Be wary of user-defined conversion functions
...you usually don't want to provide type conversion functions of *any*
ilk.  The fundamental problem is that such functions often end up being
called when you neither want nor expect them to be.  The result can be
incorrect and unintuitive program behavior that is maddeningly difficult
to diagnose.  ... In general, the more experience C++ programmers have,
the more likely they are to eschew type conversion operators."

In C++ you can now label these conversion functions as 'explicit',
meaning the above would require and explicit cast (i.e. (float)anObject)
to invoke it, but at that point you might as well avoid the magic syntax
and just provide a toFloat() method that will be both more obvious to
readers of the code and easier to find for users of your API trying to
find that functionality.

--bb

Jaak wrote:
Hello

I propose the following extension to structs:

One can define methods for struct T of the type:

T opImplicitConvert(T2 t)

which allows the implicit conversion of variables of
type T2 to type T.
(the name itself is not important, others possibilities:
opImpConvFrom, opICast_r, etc.)

A conversion does no make sense if the types T2 and T
are the same, as that can cause an infinite chain
of conversions.

i.e.   T opImplicitConvert(T t)  is illegal

We now get opAssign semantics without breaking
or changing the way assignments currently work.

Example code:

struct Vector {
float[] elems;

...

Vector opImplicitConvert(float[] a)
{
Vector v;
v.elems = a.dup;
return v;
}
}

...

Vector u, v;

// Current system
u = v;  // Currently OK
u = [1f 2f 3f]; // ERROR

// Proposed system
u = v;  // Still OK
v = [1f 2f 3f]; // OK, as Vector.opImplicitConvert(float[]) exists

Some possible enhancements:

1. A smarter compiler may be able to figure out conversions chains if
a direct conversion is not possible.
I.e.

MyVector a;
YourVector b;
HisVector c;

// uses Vector.opImpConv(YourVector.opImpConv(HisVector)) if
// Vector.opImpConv(HisVector) does not exist
a = c;

2. If property syntax can be used to add methods to structs, one can do
the following:

Vector opImplicitConvert(Vector, OtherVector w) {
Vector t;
t.elems = w.elems;
return t;
}

// Then, variables of type OtherVector can be implicitly
// converted to type Vector. This is especially useful if
// the source implementing the struct Vector is not available.

OtherVector w;
v = w; // expands to v = v.opImplicitConvert(w);

-
Jaak

```
Oct 25 2006
Jaak <jpdt telkomsa.net> writes:
```Bill Baxter wrote:
Maybe you can try to explain this once more in a format of
1. Here's a problem currently faced by developers

The main idea is achieving transparent interoperability of types.
When two types are compatible (defined by the existence of suitable
conversion functions) one can use them as if they are the same type and
all the magic happens in the background.
For example:

VectorTypeA a;
VectorTypeB b1, b2;
a = b1 + b2;

And as a side effect one gets effects similar to having an overloadable
opAssign operators without changing assignments that currently compile.
i.e. Vector v = [1f, 2f, 3f]

2. here's a suggestion for how to fix it
3. heres how my suggestion resolves that problem

Its, basically a way of specifying implicit conversions between types.
Structs (or arrays using property notation) define a function which
converts from one type to another:
TO opIConvert(FROM what){...}

If a suitable function exist, it gets called in cases when type TO is
expected and type FROM is given.

(more details in parent post)

4. here's why I think this is the best/only way to resolve the problem.

Well... I was hoping someone in here can point me to a better solution
if one exists.

solve to begin with, so it's hard to get excited about the solution.
It's also difficult for others to offer you alternative solutions to the
problem, not knowing what the problem is to begin with.

Aah sorry, I hope this post clarifies things a bit.

That said, the proposal sounds reminiscent of C++'s overloadable
(implicit) conversion operators, which have caused me more trouble than
benefit.  E.g.
operator float() const { return aFloat; }

It my seems like my proposal is a reversed version of this. I.e. the
conversion operators are defined in the target type instead of the

Item 5 from Scott Meyer's Effective C++:
"Be wary of user-defined conversion functions
....you usually don't want to provide type conversion functions of *any*
ilk.  The fundamental problem is that such functions often end up being
called when you neither want nor expect them to be.  The result can be
incorrect and unintuitive program behavior that is maddeningly difficult
to diagnose.  ... In general, the more experience C++ programmers have,
the more likely they are to eschew type conversion operators."

I have no experience of this, but i can see how automatic invisible
function calls all over the place can complicate debugging.
But.... it would still be nice to have the option.

In C++ you can now label these conversion functions as 'explicit',
meaning the above would require and explicit cast (i.e. (float)anObject)
to invoke it, but at that point you might as well avoid the magic syntax
and just provide a toFloat() method that will be both more obvious to
readers of the code and easier to find for users of your API trying to
find that functionality.

Any explicit declaration of intent would sort of defeat the purpose of
the proposal. The aim of which is mainly to make using different types
together less verbose (and get opAssign functionality). Anyway, its not
a dealbreaker, just on my wishlist.

--bb

```
Oct 25 2006
Kristian <kjkilpi gmail.com> writes:
```On Thu, 26 Oct 2006 06:15:11 +0300, Jaak <jpdt telkomsa.net> wrote:
Bill Baxter wrote:

[snip]
That said, the proposal sounds reminiscent of C++'s overloadable  =

(implicit) conversion operators, which have caused me more trouble th=

an  =

benefit.  E.g.
operator float() const { return aFloat; }

Hmm, I have to say that the both sides have good points. Implicit type  =

casting can cause trouble. But in the other hand, it can be also very  =

useful in some situations, i.e. when the types are indeed compatible.

In C++ I have had problems with signed/unsigned type casting. I can't ju=
st  =

now recall what they were, but they were annoying, and the compiler shou=
ld  =

have got them right.

I think implicit type conversion can be made a lot safer (than it's in  =

C++ and in any other language I know of). It would require type concersi=
on  =

rules. That is, more than simply telling the compiler that here are all =
=

the conversion operators, use them as you wish.

For example, in C++:

class C;

class A {
A &operator =3D(int);
A &operator =3D(const C &);
};

class B {
operator int() const;
operator C() const;
};

void func() {
A a;
B b;

a =3D b;
}

Should 'b' to converted to 'int' or 'C'? There is no way of knowing that=
.

However, if one could define that 'C' is prefered over 'int', there woul=
d  =

be no problems.
For example, the order in which the operator are defined would tell the =
=

preference order (or you could use some kind of numeric value, etc):

class B {
operator C() const;    //1.
operator int() const;  //2.
};

There could be some other rules as well. I mean, human can always tell  =

that what conversion should be used, if possible. Why not tell that to  =

compiler also?

Is it possible to create such rules in compact way that would apply  =

(almost) all the cases? Is the preference ordering enough?
```
Oct 26 2006
Jaak <jpdt telkomsa.net> writes:
```One could possibly require that the shortest conversion chain is always
used, and it is an error if a conversion is required and more than one
conversion chains of the same length exists.

In your example below A and B are implicitly compatible by two possible
chains of conversions:
B => int => C
B => C => A

This could be flagged as ambigious. Making the compatibility explicit
would  remove the error.
In the below example:

Add to class A a conversion from B:
A &operator =(const B &);

Add to class B a conversion to B:
operator A() const;

Its a tradeoff: no need for preference ordering, at the cost of having
to define extra conversion functions.

The shortest conversion chain is then simply: B => A

Kristian wrote:
On Thu, 26 Oct 2006 06:15:11 +0300, Jaak <jpdt telkomsa.net> wrote:
Bill Baxter wrote:

[snip]
That said, the proposal sounds reminiscent of C++'s overloadable
(implicit) conversion operators, which have caused me more trouble
than benefit.  E.g.
operator float() const { return aFloat; }

Hmm, I have to say that the both sides have good points. Implicit type
casting can cause trouble. But in the other hand, it can be also very
useful in some situations, i.e. when the types are indeed compatible.

In C++ I have had problems with signed/unsigned type casting. I can't
just now recall what they were, but they were annoying, and the compiler
should have got them right.

I've been bitten by that in D as well:

if (array.length-5>0) // is always true as length property is unsigned

But this is  luckily not a mistake one keeps on making.

I think implicit type conversion can be made a lot safer (than it's in
C++ and in any other language I know of). It would require type
concersion rules. That is, more than simply telling the compiler that
here are all the conversion operators, use them as you wish.

For example, in C++:

class C;

class A {
A &operator =(int);
A &operator =(const C &);
};

class B {
operator int() const;
operator C() const;
};

void func() {
A a;
B b;

a = b;
}

Should 'b' to converted to 'int' or 'C'? There is no way of knowing that..

However, if one could define that 'C' is prefered over 'int', there
would be no problems.
For example, the order in which the operator are defined would tell the
preference order (or you could use some kind of numeric value, etc):

class B {
operator C() const;    //1.
operator int() const;  //2.
};

There could be some other rules as well. I mean, human can always tell
that what conversion should be used, if possible. Why not tell that to
compiler also?

Is it possible to create such rules in compact way that would apply
(almost) all the cases? Is the preference ordering enough?

```
Oct 27 2006