www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How to define opCast from native types

reply "Cherry" <cherry dream.land> writes:
Greetings

I need to cast from long to Integer (see the code below). But it 
seems D wants opCast to be a method and not a global template 
function. I know that I could define a constructor in Integer 
that takes a long as argument, but I do not want that to happen 
since it will enable silent conversion.

Can somebody tell me what is possible here? Also throw some light 
is to! template is looked at for cast operation. Could not find 
any relevant documentation. Please help.

Thanks and Regards
- Cherry

struct Integer {

   int _int;

   this(int that) {
     _int = that;
   }

   // Do not want to define a regular constructor for long
   // This would enable silent conversion

   // this(long that) {
   //   _int = cast(int) that;
   // }

}


// Does not work
T opCast(T, F)(F f) if( is(T == Integer) && is(F == long)) {
   Integer a;
   return a;
  }

void main()
{
   Integer foo;
   long ll;

   // works -- UFCS
   foo = ll.opCast!(Integer);

   foo = cast(Integer) ll;	// Does not compile

   // Integer bar = ll;          // Do not want this -- explicit 
cast is required

}
Feb 07 2014
next sibling parent "w0rp" <devw0rp gmail.com> writes:
D only lets you define operator overloads in methods, unlike C++ 
which lets you define overloads as functions. If you want such an 
operator overload, you will have to define it as a method. 
However, I suggest that most of the time, writing an opCast 
overload is usually the wrong solution. It's often better to 
write functions for type conversions instead, which can easily be 
defined outside of the struct definition and used with UFCS.

So you can have say this...

     ll.toBoxedInteger

... instead of this

     cast(Integer) ll

However I would suggest that even in this case converting from 
long to int is error prone, so I would just force users of your 
struct to cast first from long to int, then wrap the number in 
your boxed struct.

     Integer(cast(int) ll)

You are much more likely to get warnings from D compilers about 
the cast in the right places now. However you can go one step 
further and ask yourself, do you need boxed integers at all? D 
allows you to create collections of primitive types and so on, so 
you might not even need a boxed type like Integer, however there 
may be some use cases.
Feb 07 2014
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 02/07/2014 03:06 AM, Cherry wrote:

 struct Integer {

    int _int;

    this(int that) {
      _int = that;
    }

    // Do not want to define a regular constructor for long
    // This would enable silent conversion

That's not true.
    // this(long that) {
    //   _int = cast(int) that;
    // }

 }


 // Does not work
 T opCast(T, F)(F f) if( is(T == Integer) && is(F == long)) {
    Integer a;
    return a;
   }

 void main()
 {
    Integer foo;
    long ll;

    // works -- UFCS
    foo = ll.opCast!(Integer);

    foo = cast(Integer) ll;    // Does not compile

    // Integer bar = ll;          // Do not want this -- explicit cast is
 required

The line above is an explicit object construction. That's why D allows that syntax. Unlike C++, there is no implicit conversion when passing to a function: void func(Integer i) {} func(1L); // Does not call the constructor
 }

To add to what w0rp said, I've experimented with overloading std.conv.to for my types: import std.conv; Integer to(T = Integer)(long l) { return Integer(l); } func(1L.to!Integer); // Compiles I am not sure how good an idea it is because my overload is not a part of std.conv. Ali P.S. There is also the D.learn newsgroup.
Feb 07 2014
prev sibling next sibling parent "Cheery" <cherry dream.land> writes:
 The line above is an explicit object construction. That's why D 
 allows that syntax. Unlike C++, there is no implicit conversion 
 when passing to a function:

 void func(Integer i)
 {}

   func(1L);  // Does not call the constructor

Ali Sorry for not being clear in my first port. I was not referring to implicit vs explicit conversion. I would also like to avoid explicit call to constructors. For example I would like to avoid. long ll; Integer foo = ll; But I want something like this to be enabled. long ll; Integer foo = cast(foo) ll; Unfortunately for casting of native types, D seems to rely only on constructor functions. Regards - Cherry
Feb 07 2014
prev sibling next sibling parent "Cherry" <cherry dream.land> writes:
Greetings

 D only lets you define operator overloads in methods, unlike 
 C++ which lets you define overloads as functions. If you want 
 such an operator overload, you will have to define it as a 
 method.

That is all right, since for binary operators there is opBinary and opBinrayRight. Correspondingly, I am missing opCastRight.
 However, I suggest that most of the time, writing an opCast 
 overload is usually the wrong solution. It's often better to 
 write functions for type conversions instead, which can easily 
 be defined outside of the struct definition and used with UFCS.

 So you can have say this...

     ll.toBoxedInteger

 ... instead of this

     cast(Integer) ll

In general it seems ".to" conversion is not meant to *cast* away information. For example, the following code gives me runtime error for trying to convert a long into an int. void main() { import std.conv; int ii = 8589934592L.to!int; }
 However I would suggest that even in this case converting from 
 long to int is error prone, so I would just force users of your 
 struct to cast first from long to int, then wrap the number in 
 your boxed struct.

     Integer(cast(int) ll)

 You are much more likely to get warnings from D compilers about 
 the cast in the right places now. However you can go one step 
 further and ask yourself, do you need boxed integers at all? D 
 allows you to create collections of primitive types and so on, 
 so you might not even need a boxed type like Integer, however 
 there may be some use cases.

I just created Integer for the purpose of illustration. I am working on a BitVector module. And since BitVector is a template (with bitnum parameter), forcing the users to cast into int does not work. Consider the case when bitnum is 48 bits. alias BitVector!48 bitv48; long ll; bitv48 foo = cast(bitv48) ll; // I need cast here Regards - Cherry
Feb 07 2014
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Cherry:

 alias BitVector!48 bitv48;
 long ll;
 bitv48 foo = cast(bitv48) ll;    // I need cast here

In D types like bitv48 usually (should) start with an uppercase, and the alias syntax with = is now preferred. Is this good enough? alias Bitv48 = BitVector!48; ulong ull; auto foo = ull.toBitV!Bitv48; Where toBitV is a little function of yours. Bye, bearophile
Feb 07 2014
prev sibling next sibling parent "Cherry" <cherry dream.land> writes:
 Is this good enough?

 alias Bitv48 = BitVector!48;
 ulong ull;
 auto foo = ull.toBitV!Bitv48;

 Where toBitV is a little function of yours.

Hello Bearophile As I put in the earlier post, in general the functioning of "to" in std.conv seems to be that it will not cast away bits. ull.to!int actually throws an exception: std.conv.ConvOverflowException /../../src/phobos/std/conv.d(1432): Conversion positive overflow And so I do not want to code a toBitV that casts away bits. What I am doing at the moment is: alias BitV48 = BitVector!48; ulong ull; BitV48 bits = cast(Bit48) ull.toBitVector; Here toBitVector converts any long to BitVector!64, and int to BitVector!32 and so on. And since an explicit cast is required, the user knows that he is *cast*ing away bits. Still my point is that I would like BitVector!48 to behave as much native as it could be. And I miss a provision to cast directly from long. If we could have opCastRight (actually opCastLeft), such issues could be taken care of. One of the strengths of DLang is that it is a systems programming language. But at places I find that D does lack some capabilities to create native-like value types. Regards - Cherry
Feb 07 2014
prev sibling parent "Cherry" <cherry dream.land> writes:
 alias BitV48 = BitVector!48;
 ulong ull;
 BitV48 bits = cast(Bit48) ull.toBitVector;

I find it necessary to make the user *cast* explicitly. I just wanted to add that though I am comfortable with this syntax, it is not the most efficient way since two methods toBitVector and opCast are being invoked. That would not be the case if D had provision for opCastLeft. And the syntax would have been smoother too. BTW I still have a ull.toBitVector!N, but aligned with std.conv:to functionality it makes a runtime check and raises ConvOverflowException if required. regards - Cherry
Feb 07 2014