www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - opCast using in template struct

reply "Oleg" <code.viator gmail.com> writes:
Hello. How to cast template struct to itself?

struct vec(string S,T=double)
{
     T[S.length] data;
     auto opCast(string K,E)()
         if( S.length == K.length &&
              is( T : E ) )
     {
         vec!(K,E) ret;
         foreach( i, ref m; ret.data )
             m = data[i];
         return ret;
     }
}

unittest
{
     alias vec!"xyz" dvec3;
     alias vec!("rbg",float) fcol3;
     auto a = dvec3([1,2,3]);
     auto b = fcol3();
     b = a; #1
     assert( b.data == [1,2,3] );
}

#1 Error: cannot implicitly convert expression (a)
of type vec!("xyz") to vec!("rbg",float)
Oct 18 2012
next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-10-18, 16:54, Oleg wrote:

 Hello. How to cast template struct to itself?

 struct vec(string S,T=double)
 {
      T[S.length] data;
      auto opCast(string K,E)()
          if( S.length == K.length &&
               is( T : E ) )
      {
          vec!(K,E) ret;
          foreach( i, ref m; ret.data )
              m = data[i];
          return ret;
      }
 }

 unittest
 {
      alias vec!"xyz" dvec3;
      alias vec!("rbg",float) fcol3;
      auto a = dvec3([1,2,3]);
      auto b = fcol3();
      b = a; #1
      assert( b.data == [1,2,3] );
 }

 #1 Error: cannot implicitly convert expression (a)
 of type vec!("xyz") to vec!("rbg",float)

I don't see a cast there. Do you? What you want is to overload the assignment operator, opAssign: struct vec(string S,T=double) { T[S.length] data; void opAssign(string K,E)(vec!(K,E) value) if( S.length == K.length && is( E : T ) ) { foreach( i, ref m; value.data ) data[i] = m; } } unittest { alias vec!"xyz" dvec3; alias vec!("rbg",float) fcol3; auto a = dvec3([1,2,3]); auto b = fcol3(); b = a; #1 assert( b.data == [1,2,3] ); } -- Simen
Oct 18 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/18/2012 11:45 PM, bearophile wrote:
 Era Scarecrow:

 It's an easy mistake to make. Maybe the compiler should issue a
 warning when opAssign attempts and fails and opOpBinary is defined.


This would have to be implemented very carefully. There are enough hard to reproduce symbol resolution bugs already. It does not get better by introducing hidden and unnecessary lookups.
 If you have strong feelings about this, then add a Bugzilla entry.

 There are other cases. Generally the D compiler should add some warnings
 that help against operator overloading mistakes.

 Bye,
 bearophile

I don't think that operator overloading gives rise to distinct mistakes. For example, better error messages that just specify the expected name, as in other cases of undefined identifiers, would already fix this.
Oct 18 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/19/2012 01:05 AM, Era Scarecrow wrote:
 On Thursday, 18 October 2012 at 22:07:55 UTC, Timon Gehr wrote:
 On 10/18/2012 11:45 PM, bearophile wrote:
 There are other cases. Generally the D compiler should add some
 warnings that help against operator overloading mistakes.

I don't think that operator overloading gives rise to distinct mistakes. For example, better error messages that just specify the expected name, as in other cases of undefined identifiers, would already fix this.

Inside a function a badly named identifier becomes obvious since it outright tells you. But with regards to opOpAssign and other operator overloading I see the error message and I see the call but I don't see why it fails.

My suggestion was something like: error: expression 'e' of type 'S' is not of arithmetic type and it does not define opOpAssign!"+". The compiler should indicate exactly why it fails. If this is not enough, then the programmer certainly deserves the headache.
 Then I'll try adjusting the signature on my function, not
 realizing it never sees it in the first place due to misspelling.

If the issue _is_ with the signature, then the compiler should tell you. That is the (secondary) job of the compiler.
   Maybe.. A general warning when something starts with 'op(Op)?[A-Z]'

'op[A-Z]'
 but doesn't actually qualify as any of the override-able operators? That
 seems sensible...

That is a lot better, but what if the typo is within the first 3 characters? :o)
Oct 18 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/19/2012 01:23 AM, bearophile wrote:
 Era Scarecrow:

  Maybe.. A general warning when something starts with 'op(Op)?[A-Z]'
 but doesn't actually qualify as any of the override-able operators?
 That seems sensible...

Regarding operator overloading there are several situations worth warning the programmer of. The D compilers should be improved on this. Bye, bearophile

What situations?
Oct 18 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/19/2012 02:12 AM, bearophile wrote:
 Timon Gehr:

 What situations?

This thread has already shown two possible cases worth discussing about. This report shows two more cases: http://d.puremagic.com/issues/show_bug.cgi?id=8844

How is that bug-prone? Are there keyboards where : and = are close?
 Two more cases:

 Foo opBinary(string op="/\")(Foo f) {}

That is not even going to pass the lexing stage.
 Foo opBinary(string op)(Foo f) if (op == " ") {}

What is the issue?
 And several other situations.

 Bye,
 bearophile

Oct 18 2012
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/19/2012 06:38 AM, Oleg wrote:
 Problem solved partially.

 http://dpaste.dzfl.pl/e7871a01

 in structs I use fix length arrays declarations, and alias its to
 structs, but not allowed casting to fix length arrays.
 I want check array length in compile time

 auto opBinary(string op,E)( E[DLen] b ) // fix length array

E[DLen] is good above. You can also consider taking by 'const ref' if DLen is too large. Otherwise, being value-types, fixed-length arrays normally get copied to functions.
 if( ( op == "+" || op == "-" ) && is( E : T ) )
 {
 // without this checking in runtime
 //if( DLen != b.length )
 // throw new Exception("bad length");

Yes, that is not needed anymore because now it is known that b.length is always DLen.
 auto res = VecT(this);
 foreach( i, ref m; mixin( "res." ~ DName ) )
 mixin( "m " ~ op ~ "= b[i];" );
 return res;
 }

 if I write E[DLen] I have errors like this

 Error: template
 opop.vec!("xyz").vec.arrayMath!("data",3LU,double,vec!("xyz")).opAssign
 does not match any function template declaration

 Error: template
 

 if (is(E : T)) cannot deduce template function from argument types
 !()(int[])

The problem is that array literals are slices, not fixed-length arrays. pragma(msg, typeof([1,2,3])); prints int[]
 but declaration like this allowed
 int[4] f = [1,2,3,4];

Because in that case the type of f is explicitly int[4]. The following would not be: auto f = [1,2,3]; The type of f is int[]. Of course it is possible to support both arrays and slices, and avoid the check by something like isStaticArray, but your callers will have to be aware of the fact that array literals are slices. Ali
Dec 08 2012
prev sibling next sibling parent "Oleg" <code.viator gmail.com> writes:
Sorry. My problem more complex and my simplification is not 
correct.
I want use mixin for math operations.

mixin template vectOp( string DataName, int DataLen, T, vecType )
{
     mixin( "alias " ~ DataName ~ " this;" );
....
     auto opBinary(string op,E)( E[DataLen] b ) ....
     auto opBinary(string op,E)( E[] b ) ....
     auto opOpBinary(string op,E)( E[] b ) ....
     auto opOpBinary(string op,E)( E[DataLen] b ) ....
....
}

struct vec(string S,T=double)
{
     T[S.length] data;
     mixin vectOp( "data", S.length, T, vec!(S,T) );
}

unittest{
    vec!"xyz" a;
    vec!"xyz" b;
    a += b;
}

and it isn't work

Error: 'a += b' is not a scalar, it is a vec!("xyz")
Error: 'a._data' is not of arithmetic type, it is a double[3LU]
Error: 'b._data' is not of arithmetic type, it is a double[3LU]
Oct 18 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-10-18, 17:45, Oleg wrote:

 Sorry. My problem more complex and my simplification is not correct.
 I want use mixin for math operations.

 mixin template vectOp( string DataName, int DataLen, T, vecType )
 {
      mixin( "alias " ~ DataName ~ " this;" );
 ....
      auto opBinary(string op,E)( E[DataLen] b ) ....
      auto opBinary(string op,E)( E[] b ) ....
      auto opOpBinary(string op,E)( E[] b ) ....
      auto opOpBinary(string op,E)( E[DataLen] b ) ....
 ....
 }

 struct vec(string S,T=double)
 {
      T[S.length] data;
      mixin vectOp( "data", S.length, T, vec!(S,T) );
 }

 unittest{
     vec!"xyz" a;
     vec!"xyz" b;
     a += b;
 }

 and it isn't work

 Error: 'a += b' is not a scalar, it is a vec!("xyz")
 Error: 'a._data' is not of arithmetic type, it is a double[3LU]
 Error: 'b._data' is not of arithmetic type, it is a double[3LU]

I see you have opOpBinary there - should those be opOpAssign? -- Simen
Oct 18 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Thursday, 18 October 2012 at 18:12:49 UTC, Simen Kjaeraas 
wrote:

 I see you have opOpBinary there - should those be opOpAssign?

Probably. It's an easy mistake to make. Maybe the compiler should issue a warning when opAssign attempts and fails and opOpBinary is defined.
Oct 18 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Era Scarecrow:

 It's an easy mistake to make. Maybe the compiler should issue a 
 warning when opAssign attempts and fails and opOpBinary is 
 defined.

If you have strong feelings about this, then add a Bugzilla entry. There are other cases. Generally the D compiler should add some warnings that help against operator overloading mistakes. Bye, bearophile
Oct 18 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Thursday, 18 October 2012 at 22:07:55 UTC, Timon Gehr wrote:
 On 10/18/2012 11:45 PM, bearophile wrote:
 There are other cases. Generally the D compiler should add 
 some warnings that help against operator overloading mistakes.

I don't think that operator overloading gives rise to distinct mistakes. For example, better error messages that just specify the expected name, as in other cases of undefined identifiers, would already fix this.

Inside a function a badly named identifier becomes obvious since it outright tells you. But with regards to opOpAssign and other operator overloading I see the error message and I see the call but I don't see why it fails. Then I'll try adjusting the signature on my function, not realizing it never sees it in the first place due to misspelling. Maybe.. A general warning when something starts with 'op(Op)?[A-Z]' but doesn't actually qualify as any of the override-able operators? That seems sensible...
Oct 18 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Era Scarecrow:

  Maybe.. A general warning when something starts with 
 'op(Op)?[A-Z]' but doesn't actually qualify as any of the 
 override-able operators? That seems sensible...

Regarding operator overloading there are several situations worth warning the programmer of. The D compilers should be improved on this. Bye, bearophile
Oct 18 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 What situations?

This thread has already shown two possible cases worth discussing about. This report shows two more cases: http://d.puremagic.com/issues/show_bug.cgi?id=8844 Two more cases: Foo opBinary(string op="/\")(Foo f) {} Foo opBinary(string op)(Foo f) if (op == " ") {} And several other situations. Bye, bearophile
Oct 18 2012
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Thursday, 18 October 2012 at 23:51:44 UTC, Timon Gehr wrote:
If the issue _is_ with the signature, then the compiler should 
tell you. That is the (secondary) job of the compiler.

But not everything is parsed/compiled if it doesn't match the constraints, especially template functions. Sometimes I wish I had more information of what signatures it generated, what it compared against, and why each of them were disqualified; But that's regarding more complex stuff.
 That is a lot better, but what if the typo is within the first 
 3 characters? :o)

If the beginning doesn't match 'op(Op)?[A-Z]', then you can't safely guess it was ever intended to override operators. If it has the keyword override then you know in a class it's polymorphic, however in a struct.... Hmmm... i don't know. Maybe a small suite list of tests that go through a few dozen templates and tells you what a struct qualifies for, like ranges, random access, forward/reverse, infinite. Etc. Might be more informational but if you expect you struct to do something and it doesn't qualify then you have a better idea at least of what is wrong.
Oct 18 2012
prev sibling parent "Oleg" <code.viator gmail.com> writes:
Problem solved partially.

http://dpaste.dzfl.pl/e7871a01

in structs I use fix length arrays declarations, and alias its to 
structs, but not allowed casting to fix length arrays.
I want check array length in compile time

     auto opBinary(string op,E)( E[DLen] b ) // fix length array
         if( ( op == "+" || op == "-" ) && is( E : T ) )
     {
         // without this checking in runtime
         //if( DLen != b.length )
         //    throw new Exception("bad length");
         auto res = VecT(this);
         foreach( i, ref m; mixin( "res." ~ DName ) )
             mixin( "m " ~ op ~ "= b[i];" );
         return res;
     }

if I write E[DLen] I have errors like this

  Error: template 
opop.vec!("xyz").vec.arrayMath!("data",3LU,double,vec!("xyz")).opAssign 
does not match any function template declaration

  Error: template 
opop.vec!("xyz").vec.arrayMath!("data",3LU,double,vec!("xyz")).opAssign(E) 
if (is(E : T)) cannot deduce template function from argument 
types !()(int[])

but declaration like this allowed
int[4] f = [1,2,3,4];

also checked in compile time assigning like this
int[4] f;
int[5] g;
g = f; // -> Error: mismatched array lengths, 5 and 4

I think in my situation checking in compile time must be 
possibly, because all of template params known in compile time, 
but I unknown how to use it...
Oct 19 2012