www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Make enum auto castable

reply Mike B Johnson <Mikey Ikes.com> writes:
I am dealing with some COM stuff and some functions use VARIANT, 
which can hold an enum.

Instead of having to manually convert the enum(and for that 
matter, other things) to VARIANT, is it possible to have them 
automatically converted?

This is because an enum is always convertible to a VARIANT but 
not the other way around. So, when a enum is passed to a function 
accepting a VARIANT, it should just work. Overloading is not an 
option.
Jun 04
next sibling parent Laeeth Isharc <laeethnospam nospam.laeeth.com> writes:
On Sunday, 4 June 2017 at 22:52:55 UTC, Mike B Johnson wrote:
 I am dealing with some COM stuff and some functions use 
 VARIANT, which can hold an enum.

 Instead of having to manually convert the enum(and for that 
 matter, other things) to VARIANT, is it possible to have them 
 automatically converted?

 This is because an enum is always convertible to a VARIANT but 
 not the other way around. So, when a enum is passed to a 
 function accepting a VARIANT, it should just work. Overloading 
 is not an option.
Not sure if this breaks your requirement but you could generate overloads or rather templated version of your variant accepting function with a mixin that introspects on functions with a certain uda in the module. See excel-d for an example.
Jun 04
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, June 04, 2017 22:52:55 Mike B Johnson via Digitalmars-d-learn 
wrote:
 I am dealing with some COM stuff and some functions use VARIANT,
 which can hold an enum.

 Instead of having to manually convert the enum(and for that
 matter, other things) to VARIANT, is it possible to have them
 automatically converted?

 This is because an enum is always convertible to a VARIANT but
 not the other way around. So, when a enum is passed to a function
 accepting a VARIANT, it should just work. Overloading is not an
 option.
Aside from whatever implicit conversions are built into the language, the only way to define an implicit conversion in D is via alias this on a user-defined type, which then tells that type that it can convert to whatever type the result of the alias this is. e.g. if you have struct S { int x; alias x this; } then S can implicitly convert to int, and when it does, the value of x is used (alternatively, a member function could be used, in which case, the result of the member function would be used as the result of the conversion). So, that allows you to tell how to convert a type _to_ another type, but it does not allow you to convert _from_ another type. So, if you're implicitly converting from type A to type B, it will work if type A has been told how to convert to B, but there is no way for B to say that an A can be converted to a B. Only A can define the conversion. So, if you have control of the definition of the base type of the enum, then you can define an alias this on it to convert to a VARIANT, but if you don't have control over the definition of the base type of the enum (e.g. if it's int, as is the default), then you're out of luck. And if you're dealing with COM, then I'm guessing that your enum has a base type of int, and you obviously don't control the definition of int, so you can't add an alias this to it. So, if you can't change the enum, and you can't change the function that you're calling, then you'll have to insert something in between - be it a conversion function, an explicit construction of VARIANT with the enum, or some kind of wrapper function around the one that you're calling. But unless the enum itself knows how to implicitly convert to a VARIANT thanks to alias this, there is no implicit conversion. std.typecons.Nullable has this sort of problem, which has historically forced folks to do stuff like Nullable!int(42) when passing the actual type to a function that accepts a Nullable wrapper around the type, which is pretty annoying. Recently, a nullable function was added to std.typecons which uses IFTI to infer the type of the Nullable auto nullable(T)(T t) { return Nullable!T(t); } and then you could pass nullable(42) instead of Nullable!int(42), so it's more user-friendly, but it still requires an explicit conversion unless you overload the function. It's a downside to D's stricter approach to implicit conversions. - Jonathan M Davis
Jun 04
parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Sunday, 4 June 2017 at 23:39:11 UTC, Jonathan M Davis wrote:
 On Sunday, June 04, 2017 22:52:55 Mike B Johnson via 
 Digitalmars-d-learn wrote:
 [...]
Aside from whatever implicit conversions are built into the language, the only way to define an implicit conversion in D is via alias this on a user-defined type, which then tells that type that it can convert to whatever type the result of the alias this is. e.g. if you have [...]
I might be able to change the enum, I assume you mean something like enum myenum : S { } where S is the struct that implicitly converts?
Jun 04
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, June 05, 2017 00:16:15 Mike B Johnson via Digitalmars-d-learn 
wrote:
 On Sunday, 4 June 2017 at 23:39:11 UTC, Jonathan M Davis wrote:
 On Sunday, June 04, 2017 22:52:55 Mike B Johnson via

 Digitalmars-d-learn wrote:
 [...]
Aside from whatever implicit conversions are built into the language, the only way to define an implicit conversion in D is via alias this on a user-defined type, which then tells that type that it can convert to whatever type the result of the alias this is. e.g. if you have [...]
I might be able to change the enum, I assume you mean something like enum myenum : S { } where S is the struct that implicitly converts?
Yes. However, be aware that you can currently only define one alias this per type. So, the rest of the code will then need to be able to deal with the fact that the enum is a struct that implicitly converts to VARIANT and does not implicitly convert to int. So, if you're just passing the enums around and comparing them, you're fine, but if you need to treat them as ints somewhere, then you'll need to provide an explicit conversion (via overloading opCast or by creating a specific function for it or just exposing a member with the int value or whatever), and that could get annoying in the same way that you're annoyed about the VARIANT issue right now. But if you don't actually need to treat the enum as an int, and you can make it a struct that implicitly converts to VARIANT instead, then that will fix your VARIANT conversion problem. - Jonathan M Davis
Jun 04
next sibling parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Monday, 5 June 2017 at 00:51:15 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 00:16:15 Mike B Johnson via 
 Digitalmars-d-learn wrote:
 On Sunday, 4 June 2017 at 23:39:11 UTC, Jonathan M Davis wrote:
 On Sunday, June 04, 2017 22:52:55 Mike B Johnson via

 Digitalmars-d-learn wrote:
 [...]
Aside from whatever implicit conversions are built into the language, the only way to define an implicit conversion in D is via alias this on a user-defined type, which then tells that type that it can convert to whatever type the result of the alias this is. e.g. if you have [...]
I might be able to change the enum, I assume you mean something like enum myenum : S { } where S is the struct that implicitly converts?
Yes. However, be aware that you can currently only define one alias this per type. So, the rest of the code will then need to be able to deal with the fact that the enum is a struct that implicitly converts to VARIANT and does not implicitly convert to int. So, if you're just passing the enums around and comparing them, you're fine, but if you need to treat them as ints somewhere, then you'll need to provide an explicit conversion (via overloading opCast or by creating a specific function for it or just exposing a member with the int value or whatever), and that could get annoying in the same way that you're annoyed about the VARIANT issue right now. But if you don't actually need to treat the enum as an int, and you can make it a struct that implicitly converts to VARIANT instead, then that will fix your VARIANT conversion problem. - Jonathan M Davis
Well, I do need to to treat it as an int at times and opCast only works with cast. While I could set it up to do a cast(VARIANT), which is better than nothing, I'd get same result as to!VARIANT, which is shorter and effectively the same. When will we get multiple alias this? all I need is two? Hell, why not make alias this an "overloadable" function similar to opCast and such?
Jun 04
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, June 05, 2017 01:12:20 Mike B Johnson via Digitalmars-d-learn 
wrote:
 Well, I do need to to treat it as an int at times and opCast only
 works with cast. While I could set it up to do a cast(VARIANT),
 which is better than nothing, I'd get same result as to!VARIANT,
 which is shorter and effectively the same.

 When will we get multiple alias this? all I need is two?

 Hell, why not make alias this an "overloadable" function similar
 to opCast and such?
I don't know when we're getting multiple alias thises. TDPL talks about it being a thing, and the spec claims that it's a thing, but until someone implements it, we won't have it, and it hasn't been a priority for Walter. IIRC, someone tried to implement it at one point, but I don't know where that went. There was a thread in the main newsgroup a couple of months ago about how to simulate multiple alias thises, so you might want to check that out: https://forum.dlang.org/post/uniyvmvjopeyyxmphfso forum.dlang.org - Jonathan M Davis
Jun 04
prev sibling parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Monday, 5 June 2017 at 00:51:15 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 00:16:15 Mike B Johnson via 
 Digitalmars-d-learn wrote:
 On Sunday, 4 June 2017 at 23:39:11 UTC, Jonathan M Davis wrote:
 [...]
I might be able to change the enum, I assume you mean something like enum myenum : S { } where S is the struct that implicitly converts?
Yes. However, be aware that you can currently only define one alias this per type. So, the rest of the code will then need to be able to deal with the fact that the enum is a struct that implicitly converts to VARIANT and does not implicitly convert to int. So, if you're just passing the enums around and comparing them, you're fine, but if you need to treat them as ints somewhere, then you'll need to provide an explicit conversion (via overloading opCast or by creating a specific function for it or just exposing a member with the int value or whatever), and that could get annoying in the same way that you're annoyed about the VARIANT issue right now. But if you don't actually need to treat the enum as an int, and you can make it a struct that implicitly converts to VARIANT instead, then that will fix your VARIANT conversion problem. - Jonathan M Davis
enum X : EnumX { a = 1, } struct EnumX { int x; alias x this; void opAssign(int y) { x = y; } double opCall() { return x; } } doesn't work because "1" is not castable to EnumX, even though EnumX is aliased to an int, and hence it should work fine. Seems like a bug to me.
Jun 04
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, June 05, 2017 01:30:47 Mike B Johnson via Digitalmars-d-learn 
wrote:
 enum X : EnumX
 {
     a = 1,
 }


 struct EnumX
 {
   int x;
   alias x this;
   void opAssign(int y)
   {
       x = y;
   }
   double opCall()
   {
              return x;
   }
 }

 doesn't work because "1" is not castable to EnumX, even though
 EnumX is aliased to an int, and hence it should work fine.

 Seems like a bug to me.
It's not a bug. The alias this conversion only goes one way. It provides a way to convert _from_ the type that it's declared on to another type, not from the other type to the type that it's declared on. There is no way in D to declare an implicit conversion in the direction you're trying. So, if you have a struct that wraps an int like this, and you want to assign it an int, you're going to need to explicitly construct the struct - e.g. EnumX(1). - Jonathan M Davis
Jun 04
parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Monday, 5 June 2017 at 01:42:55 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 01:30:47 Mike B Johnson via 
 Digitalmars-d-learn wrote:
 [...]
It's not a bug. The alias this conversion only goes one way. It provides a way to convert _from_ the type that it's declared on to another type, not from the other type to the type that it's declared on. There is no way in D to declare an implicit conversion in the direction you're trying. So, if you have a struct that wraps an int like this, and you want to assign it an int, you're going to need to explicitly construct the struct - e.g. EnumX(1). - Jonathan M Davis
That's pretty crappy! Defeats the whole point!
Jun 04
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, June 05, 2017 02:14:14 Mike B Johnson via Digitalmars-d-learn 
wrote:
 On Monday, 5 June 2017 at 01:42:55 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 01:30:47 Mike B Johnson via

 Digitalmars-d-learn wrote:
 [...]
It's not a bug. The alias this conversion only goes one way. It provides a way to convert _from_ the type that it's declared on to another type, not from the other type to the type that it's declared on. There is no way in D to declare an implicit conversion in the direction you're trying. So, if you have a struct that wraps an int like this, and you want to assign it an int, you're going to need to explicitly construct the struct - e.g. EnumX(1). - Jonathan M Davis
That's pretty crappy! Defeats the whole point!
Well, implicit conversions tend to cause a lot of bugs in C++ code, so Walter decided to restrict them quite a bit more in D than they are in C++. This therefore prevents a number of bugs in D, but it also prevents implicit conversions from being used in a number of situations where they're useful. Whether the end result is better or worse, I don't know and certainly YMMV. As it is, some of the implicit conversions that we still allow have a tendency to cause bugs (e.g. conversions between integral values and character types tend to be error-prone - especially when arrays are involved - and using implicit conversions with templated code is almost always a bad idea). So, at times, I tend to think that all implicit conversions should be banned. But at other times, it would be really nice to be able to have more than we do (e.g. it would be a lot more user-friendly if you could pass a T to a function that takes a Nullable!T). In any case, I think that the reality of the matter with D is that you tend to have to use explicit conversions rather than implicit ones, and if you're looking to do much with implicit conversions, you're usually going to be frustrated, and obviously, that sucks, but at least you're less likely to have bugs related to implicit conversions. - Jonathan M Davis
Jun 04
parent Mike B Johnson <Mikey Ikes.com> writes:
On Monday, 5 June 2017 at 03:15:46 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 02:14:14 Mike B Johnson via 
 Digitalmars-d-learn wrote:
 On Monday, 5 June 2017 at 01:42:55 UTC, Jonathan M Davis wrote:
 On Monday, June 05, 2017 01:30:47 Mike B Johnson via

 Digitalmars-d-learn wrote:
 [...]
It's not a bug. The alias this conversion only goes one way. It provides a way to convert _from_ the type that it's declared on to another type, not from the other type to the type that it's declared on. There is no way in D to declare an implicit conversion in the direction you're trying. So, if you have a struct that wraps an int like this, and you want to assign it an int, you're going to need to explicitly construct the struct - e.g. EnumX(1). - Jonathan M Davis
That's pretty crappy! Defeats the whole point!
Well, implicit conversions tend to cause a lot of bugs in C++ code, so Walter decided to restrict them quite a bit more in D than they are in C++. This therefore prevents a number of bugs in D, but it also prevents implicit conversions from being used in a number of situations where they're useful. Whether the end result is better or worse, I don't know and certainly YMMV. As it is, some of the implicit conversions that we still allow have a tendency to cause bugs (e.g. conversions between integral values and character types tend to be error-prone - especially when arrays are involved - and using implicit conversions with templated code is almost always a bad idea). So, at times, I tend to think that all implicit conversions should be banned. But at other times, it would be really nice to be able to have more than we do (e.g. it would be a lot more user-friendly if you could pass a T to a function that takes a Nullable!T). In any case, I think that the reality of the matter with D is that you tend to have to use explicit conversions rather than implicit ones, and if you're looking to do much with implicit conversions, you're usually going to be frustrated, and obviously, that sucks, but at least you're less likely to have bugs related to implicit conversions. - Jonathan M Davis
The problem is bugs are only a possibility. If you remove a feature from a language the feature does not exist, period. So, throwing the baby out with the bath water because he pooped in it is not a solution. Instead, if there is a problem with implicit autonomous conversion or construction, then the solution is not to remove all implicit conversions or constructions but to remove the autonomous part. e.g., make the user specify the implicit conversion explicitly. This way, if bugs do creep in, it is due to the user. It is much easier to diagnose and fix then.
Jun 04
prev sibling parent via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sun, Jun 04, 2017 at 08:15:46PM -0700, Jonathan M Davis via
Digitalmars-d-learn wrote:
 Well, implicit conversions tend to cause a lot of bugs in C++ code, so
 Walter decided to restrict them quite a bit more in D than they are in C++.
So D has plenty of implicit conversion. It is implicit *construction* that it lacks. And alas, C++'s mistake is implicit is default, so it gets used in places it wasn't intended. We should have just had explicit default with implicit as an option.
Jun 04