www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Implicit cast of int to a class

reply Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Hello,

I have a class called mpz_class which in particular implements the following:

1. this (int other) { ... }
2. opAssign (int other) { ... }

I now want to write templated code with T = int, T = long and T = mpz_class.
My mpz_class should work and feel like the orginal integers, which mostly works.

I'd like to know, how I can coax my class to implicitely convert integer
literals to instances of it. There are 2 cases which don't compile for me and
one which is semantically wrong:

// 1.
void func (T) (T input = 0) // error:  cannot implicitly convert expression (0)
of type int to gmp.mpz.mpz_class
{
  // ...
}

//...

// 2. 
T a = 1; // I guess, a points to 0x1 now and not to a result of
mpz_class.this(1)

// 3.
func !(T) (a); // works
func !(T) (1); // error: function func1!(mpz_class).func1 (mpz_class) does not
match parameter types (int)

Can someone help me here? 

For full sourcecode, see:

http://dsource.org/projects/gmp4d/browser/trunk/Test.d
Nov 03 2007
parent reply torhu <no spam.invalid> writes:
Matthias Walter wrote:
 Hello,
 
 I have a class called mpz_class which in particular implements the following:
 
 1. this (int other) { ... }
 2. opAssign (int other) { ... }
 
 I now want to write templated code with T = int, T = long and T = mpz_class.
 My mpz_class should work and feel like the orginal integers, which mostly
works.
 
 I'd like to know, how I can coax my class to implicitely convert integer
literals to instances of it. There are 2 cases which don't compile for me and
one which is semantically wrong:
 
 // 1.
 void func (T) (T input = 0) // error:  cannot implicitly convert expression
(0) of type int to gmp.mpz.mpz_class

I don't think it's possible to use defaults args like this. It seems that opAssign isn't used for default args. You need to remove the default, overload the function, specialize the template, or redesign the whole thing.
 // 2. 
 T a = 1; // I guess, a points to 0x1 now and not to a result of
mpz_class.this(1)

Even if it tried to call opAssign, there's no object allocated. It would just crash. You have to use 'new' somewhere. Maybe it's better to use a struct with static opCall overloads, instead of a class? A struct is a better fit if you want it to behave like ints and longs, for sure. Mixing built-in types with user-defined types in D needs to be more explicit than in C++, because there are no implicit conversions. Except for the very limited opAssign. One option is to find a single point in your code where you can convert ints and longs to mpz_class, and then the rest of the code uses that. Of course, if all you want is to make code that can work with both ints and longs, things would probably be a lot simpler. But I guess you're just trying out stuff? Your design seems a bit too complex for its own good, although I'm not sure what you're trying to achieve. :)
Nov 03 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
torhu wrote:
 Matthias Walter wrote:
 Hello,

 I have a class called mpz_class which in particular implements the 
 following:

 1. this (int other) { ... }
 2. opAssign (int other) { ... }

 I now want to write templated code with T = int, T = long and T = 
 mpz_class.
 My mpz_class should work and feel like the orginal integers, which 
 mostly works.

 I'd like to know, how I can coax my class to implicitely convert 
 integer literals to instances of it. There are 2 cases which don't 
 compile for me and one which is semantically wrong:

 // 1.
 void func (T) (T input = 0) // error:  cannot implicitly convert 
 expression (0) of type int to gmp.mpz.mpz_class


For structs, I think this should work, but it doesn't. I was going to file a bug about it, but never got around to it. In my particular case I was trying to make an array-like struct, and got bitten by the fact that I could use my opAssign to use "MyStruct foo=null" as a default argument. For classes, as torhu points out, there's no LHS object allocated, so it's not going to work without some bigger change to the language.
 I don't think it's possible to use defaults args like this.  It seems 
 that opAssign isn't used for default args.  You need to remove the 
 default, overload the function, specialize the template, or redesign the 
 whole thing.
 
 
 // 2. T a = 1; // I guess, a points to 0x1 now and not to a result of 
 mpz_class.this(1)

Even if it tried to call opAssign, there's no object allocated. It would just crash. You have to use 'new' somewhere. Maybe it's better to use a struct with static opCall overloads, instead of a class? A struct is a better fit if you want it to behave like ints and longs, for sure.

--bb
Nov 03 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
 // 1.
 void func (T) (T input = 0) // error:  cannot implicitly convert 
 expression (0) of type int to gmp.mpz.mpz_class


For structs, I think this should work, but it doesn't. I was going to file a bug about it, but never got around to it. In my particular case I was trying to make an array-like struct, and got bitten by the fact that I could use my opAssign to use "MyStruct foo=null" as a default

could *not* use it as a defaulted argument, that is... --bb
Nov 03 2007
prev sibling parent reply Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Bill Baxter Wrote:

 torhu wrote:
 Matthias Walter wrote:
 Hello,

 I have a class called mpz_class which in particular implements the 
 following:

 1. this (int other) { ... }
 2. opAssign (int other) { ... }

 I now want to write templated code with T = int, T = long and T = 
 mpz_class.
 My mpz_class should work and feel like the orginal integers, which 
 mostly works.

 I'd like to know, how I can coax my class to implicitely convert 
 integer literals to instances of it. There are 2 cases which don't 
 compile for me and one which is semantically wrong:

 // 1.
 void func (T) (T input = 0) // error:  cannot implicitly convert 
 expression (0) of type int to gmp.mpz.mpz_class


For structs, I think this should work, but it doesn't. I was going to file a bug about it, but never got around to it. In my particular case I was trying to make an array-like struct, and got bitten by the fact that I could use my opAssign to use "MyStruct foo=null" as a default argument. For classes, as torhu points out, there's no LHS object allocated, so it's not going to work without some bigger change to the language.
 I don't think it's possible to use defaults args like this.  It seems 
 that opAssign isn't used for default args.  You need to remove the 
 default, overload the function, specialize the template, or redesign the 
 whole thing.
 
 
 // 2. T a = 1; // I guess, a points to 0x1 now and not to a result of 
 mpz_class.this(1)

Even if it tried to call opAssign, there's no object allocated. It would just crash. You have to use 'new' somewhere. Maybe it's better to use a struct with static opCall overloads, instead of a class? A struct is a better fit if you want it to behave like ints and longs, for sure.

--bb

Okay, I found one solution for everything which at least works: a function num (T) (int n), which returns n if T is a native integer or "new T (n)" if not. This work's as follows: | void func (T) (T input = num !(T) (0)) | { | // ... | } | | T a = num !(T) (1); // is correctly initialized. | | func !(T) (); // works with default parameter 0 | func !(T) (num !(T) (2)); // works This is ugly typing (at least for a library like GMP, where one likes to do things like with normal integers!), so the problem could be solved, if int i = new int (1); would be semantically equivalent to int i = 1; So no allocation should happen! Although this isn't very nice syntax, because the reader might think that there happens allocation, but it would make work with GMP much easier! best regards Matthias
Nov 04 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Matthias Walter wrote:
 
 Okay, I found one solution for everything which at least works:
 
 a function num (T) (int n), which returns n if T is a native integer or "new T
(n)" if not.
 This work's as follows:
 
 | void func (T) (T input = num !(T) (0))
 | {
 |   // ...
 | }
 |
 | T a = num !(T) (1); // is correctly initialized.
 |
 | func !(T) (); // works with default parameter 0
 | func !(T) (num !(T) (2)); // works
 
 This is ugly typing (at least for a library like GMP, where one likes to do
things like with normal integers!), 

If you want things to behave like normal integers, then you really should be using a struct instead of a class. The class reference semantics mean that assignment will always act very unlike a normal integer. Is there some reason you can't make it a struct? Do you need inheritance?
 so the problem could be solved, if
 
 int i = new int (1);
 
 would be semantically equivalent to
 
 int i = 1;

Probably not going to happen. But I could maybe see Walter making initializers like that call this(int). In fact for structs I think something like that already tries to call static opCall(int) if it exists. Maybe that works for classes too? --bb
Nov 04 2007
parent Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Bill Baxter Wrote:

 Matthias Walter wrote:
 
 Okay, I found one solution for everything which at least works:
 
 a function num (T) (int n), which returns n if T is a native integer or "new T
(n)" if not.
 This work's as follows:
 
 | void func (T) (T input = num !(T) (0))
 | {
 |   // ...
 | }
 |
 | T a = num !(T) (1); // is correctly initialized.
 |
 | func !(T) (); // works with default parameter 0
 | func !(T) (num !(T) (2)); // works
 
 This is ugly typing (at least for a library like GMP, where one likes to do
things like with normal integers!), 

If you want things to behave like normal integers, then you really should be using a struct instead of a class. The class reference semantics mean that assignment will always act very unlike a normal integer. Is there some reason you can't make it a struct? Do you need inheritance?

Yes, indeed, there is a good reason: If you work with references, the use of temporaries is not a slowdown, because copying a reference is really cheap. If I'd use structs and thus call-by-value, there would be a real temporary for simple things like: a = b + c; To avoid this creation of temporaries, one has to make heavy use of templates like the GMP guys did for the C++ headers. (They encoded the expression type in the template type and resolved everything, including assignment operations via templates to achieve a minimal number of temporaries) They did this for all basic stuff and some functions like abs, cmp, etc. Currently, I only have my mpz_class, which, written in a straightforward way, already has 2k lines due to all those possible functions and operator combinations and unittests. With the template approach, I guess this would be increased by a factor of 4, making the code really unmaintainable. (If you like, you can inspect your personal /usr/include/gmpxx.h :) ) Additionally, some ideas from Ben Hinkle (Recycling of GMP variables to avoid allocations) cannot be easily implemented with the template implementation. Last but not least, I don't know, whether the template approach can be done in D at all - e.g. operators must belong to a class, which is not the case in GMP C++ headers, so I don't know, if this problem is feasible. Any ideas, how to handle this problem?
 
 so the problem could be solved, if
 
 int i = new int (1);
 
 would be semantically equivalent to
 
 int i = 1;

Probably not going to happen. But I could maybe see Walter making initializers like that call this(int). In fact for structs I think something like that already tries to call static opCall(int) if it exists. Maybe that works for classes too? --bb

I'm going to check this out, thanks! Matthias Walter
Nov 04 2007