Thread overview
type promotion (on overloaded operators)
Oct 10, 2004
wondermail
Oct 10, 2004
Sjoerd van Leent
Type promotion Again
Oct 10, 2004
Juraj Onderik
Oct 11, 2004
Ben Hinkle
Oct 10, 2004
Ben Hinkle
October 10, 2004
Hi there,
I've just read the spec - great D, but can I somehow do the "type promotion"
e.g. for matrices

class CMatrix(T, int M, int N)
{
T data[M][N];
}

CMatrix!(float,3,3) mf;
CMatrix!(double,3,3) md;

mf + md; // float matrix + double matrix = double matrix

// or

CMatrix!(float,1,3) v3;
CMatrix!(float,3,3) m33;

v3 * m33; // and this is really necessary to me


-----------
In C++ I did something like this:

template <class X, class Y> TAdd {};

template <> TAdd<float,double> { typedef double RET; }; // float + double = double

template <class X, class Y, int M, int N> CMatrix<typename TAdd<X,Y>::RET,M,N> operator + (CMatrix<T,M,N> const &a, CMatrix<T,M,N> const &b) { /*...*/ }

thanks Juraj Onderik
(as I understand templates in D now - it's not really superset of C++ templates,
or is it? :)

Juraj Onderik
October 10, 2004
wondermail@szm.sk wrote:
> Hi there,
> I've just read the spec - great D, but can I somehow do the "type promotion"
> e.g. for matrices
> 
> class CMatrix(T, int M, int N)
> {
> T data[M][N];
> }
> 
> CMatrix!(float,3,3) mf;
> CMatrix!(double,3,3) md;
> 
> mf + md; // float matrix + double matrix = double matrix
> 
> // or
> 
> CMatrix!(float,1,3) v3;
> CMatrix!(float,3,3) m33;
> 
> v3 * m33; // and this is really necessary to me
> 
> 
> Juraj Onderik

use a bare template to perform bold action. Example:


class Matrix(T, int M, int N)
{
	T data[M][N];
}

// bare template
template MatrixWorker(Ta, Tb, int Dx, int Dy, To) {
    Matrix!(To, Dx, Dy) dotProduct(
                                    in Matrix!(Ta, Dx, Dy) first,
                                    in Matrix!(Tb, Dx, Dy) second) {
		
        Matrix!(To, Dx, Dy) result = new Matrix!(To, Dx, Dy);
        for(uint i; i < Dx; i++) {
            for(uint j; j < Dy; j++) {
                result.data[i][j] =
                    cast(To)first.data[i][j] *
                    cast(To)second.data[i][j];
            }
        }
        return result;
    }

    Matrix!(To, Dx, Dy) sum(
                            in Matrix!(Ta, Dx, Dy) first,
                            in Matrix!(Tb, Dx, Dy) second) {
		
        Matrix!(To, Dx, Dy) result = new Matrix!(To, Dx, Dy);
        for(uint i; i < Dx; i++) {
            for(uint j; j < Dy; j++) {
                result.data[i][j] =
                    cast(To)first.data[i][j] +
                    cast(To)second.data[i][j];
            }
        }
        return result;
    }
}

int main(char[][] args) {
    Matrix!(float,3,3) mf = new Matrix!(float,3,3)(/*Matrix init!*/);
    Matrix!(double,3,3) md = new Matrix!(double,3,3)(/*Matrix init!*/);

    Matrix!(double,3,3) mo;
    mo = MatrixWorker!(float, double, 3, 3, double).dotProduct(mf, md);
    // or
    mo = MatrixWorker!(float, double, 3, 3, double).sum(mf, md);
	
    return 0;
}

I think this should help. Of course, you can now write specific operators to perform it less painful ;-) But this should give you a kickstart.

Regards,
Sjoerd

N.B. This is D, not MFC, so leave out the C prefix, it's unwanted by the community and especially the big boss.
October 10, 2004
sure I know this :) Ok I meant it little bit different. Can I do "template function" :

C++ :
template <class X, class Y> void foo(X x, Y y) {} // general code
// specializations - ...

foo(1.2,3);
foo("one", "two");
//

D:
template Foo(X,Y)
{
void foo(X x, Y y) {}
}

// a,b - some objects , you have specialized Foo for them

Foo!(typeof(a),typeof(b)).foo(a,b); // OK
foo(a,b); // bad - you must provide "template scope"

So the "problem" is that compiler can't deduce template parameters from the
function parameters - what c++ actually does. Sure it's not wrong, cause
templates in D are new scopes.
Anyway, sorry, but I still can't see, how to do it with operators, bacause I
cannot parametrize it's argument's:

template TM(X, int M, int N)
{
class Matrix
{
template NewScope(Y) { void opAdd(TM!(Y,M,N).Matrix m) { /*..*/ } }
void opAdd(Matrix m) { /*...*/ } // m is TM!(T,M,N).Matrix
}
}

void main ()
{
TM!(float,3,3).Matrix a;
TM!(double,3,3).Matrix b;

a.opAdd(b); // this (float,3,3) + (float,3,3)
a.NewScope!(double).opAdd(b); // this is NOT + operator

}


>use a bare template to perform bold action. Example:
>     mo = MatrixWorker!(float, double, 3, 3, double).dotProduct(mf, md);


dot(a,b) = scalar - not a matrix :) Ok just for fun.


>I think this should help. Of course, you can now write specific operators to perform it less painful ;-) But this should give you a kickstart.
>
>N.B. This is D, not MFC, so leave out the C prefix, it's unwanted by the community and especially the big boss.

well I dont use MFC at all, but the C prefix means:
class CFoo {}
struct SFoo {}
enum EFoo {}
union UFoo {}
but if the BIG boss is against I dont mind :)



October 10, 2004
wondermail@szm.sk wrote:

> Hi there,
> I've just read the spec - great D, but can I somehow do the "type
> promotion" e.g. for matrices
> 
> class CMatrix(T, int M, int N)
> {
> T data[M][N];
> }
> 
> CMatrix!(float,3,3) mf;
> CMatrix!(double,3,3) md;
> 
> mf + md; // float matrix + double matrix = double matrix
> 
> // or
> 
> CMatrix!(float,1,3) v3;
> CMatrix!(float,3,3) m33;
> 
> v3 * m33; // and this is really necessary to me
> 

You could try something like

template GenericMatrixOps(Matrix,int M, int N) {
  Matrix opAdd(Matrix y) {
    // define addition Matrix+Matrix
  }
}

class Matrix(T:double, int M, int N) {
  T[M][N] data;

  mixin GenericMatrixOps!(Matrix,M,N) genericOps;
  alias genericOps.opAdd opAdd; // for overloading, maybe

  Matrix opAdd(Matrix!(float,M,N) y) {
    // define what to do with double+float matrices
  }
}

I haven't actually tried writing something out and compiling it but the general idea is to use mixins to share code and template specialization to define the special cases for mixed type arithmetic.

> -----------
> In C++ I did something like this:
> 
> template <class X, class Y> TAdd {};
> 
> template <> TAdd<float,double> { typedef double RET; }; // float + double = double
> 
> template <class X, class Y, int M, int N> CMatrix<typename TAdd<X,Y>::RET,M,N> operator + (CMatrix<T,M,N> const &a, CMatrix<T,M,N> const &b) { /*...*/ }
> 
> thanks Juraj Onderik
> (as I understand templates in D now - it's not really superset of C++
> templates, or is it? :)
> 
> Juraj Onderik

D doesn't have implicit template instantiation so using "pure templates" will probably end up being more verbose than in C++. If you use member functions then you don't have to worry about instantiating the templates.

-Ben
October 11, 2004
The following should help illustrate my earlier post. It probably wouldn't be hard to share more code than I do below but you get the idea.

import std.stdio;
template GenericVectorOps(Vector,T,int N) {
  // arithmetic with the same underlying data types
  Vector opAdd(Vector y) {
    Vector res;
    for(int k=0;k<N;k++) {
      res.data[k] = data[k]+y.data[k];
    }
    return res;
  }
  // print out a Vector
  void print() {
    foreach(T val; data) writef(val," ");
    writefln();
  }
}
struct Vector(T,int N) {
  T[N] data;
  mixin GenericVectorOps!(Vector,T,N) gops;
  alias gops.opAdd opAdd;
}
struct Vector(T:double,int N) {
  T[N] data;
  mixin GenericVectorOps!(Vector,T,N) gops;
  alias gops.opAdd opAdd;
  // now define mixed type arithmetic with double and float
  Vector opAdd(.Vector!(float,N) y) {
    Vector res;
    for(int k=0;k<N;k++) {
      res.data[k] = data[k]+y.data[k];
    }
    return res;
  }
}
int main() {
  Vector!(double,3) x;
  x.data[0] = 1;
  x.data[1] = 2;
  x.data[2] = 3;
  Vector!(float,3) y;
  y.data[0] = 10;
  y.data[1] = 20;
  y.data[2] = 30;
  Vector!(double,3) z = x+y;
  Vector!(double,3) w = z+x;
  x.print();
  y.print();
  z.print();
  w.print();
  return 0;
}