Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > polymorphism and binary operations on objects

Reply
Thread Tools

polymorphism and binary operations on objects

 
 
Leslaw Bieniasz
Guest
Posts: n/a
 
      09-20-2004

Cracow, 20.09.2004

Hello,

I need to implement a library containing a hierarchy of classes
together with some binary operations on objects. To fix attention,
let me assume that it is a hierarchy of algebraic matrices with the
addition operation. Thus, I want to have a virtual base class

class Matr;

and some derived classes:

class Matr1 : public Matr;
class Matr2 : public Matr;
class Matr3 : public Matr2;

etc.

I want to be able to write the code like the following one, which
intensively uses polymorphism:

Matr* A = new Matr1();
Matr* B = new Matr2();
Matr* C = new Matr3();
..
..
..
Add(A, B, C);

[or C->Add(A,B); ]

where the addition is supposed to be implemented either as a global function
that adds A to B and places the result into C, or as a virtual method
of a base class, which calculates the sum of A and B, and puts the result
into the object which calls the method.
In the first case the function(s) would have to be friends of the Matr1,
Matr2, etc. class(es). The same could be done using overloaded operators +,
but this is a secondary issue for my question, see below. I also abstract,
at the moment, from how exactly the binary operation is realised (for
example, one might use, or not use, expression templates).

The problem with the above is that each of the matrices
Matr1, Matr2, Matr3,... may require a different implementation
of the addition operation. For example, adding a diagonal matrix to a
dense matrix is possible, but is done differently from adding a dense
matrix to a dense matrix, or diagonal matrix to a diagonal matrix.
Thus, I may need to have a number of versions of Add():

Add(Matr1 *A, Matr1 *B, Matr1 *C);
Add(Matr2 *A, Matr2 *B, Matr2 *C);
Add(Matr3 *A, Matr3 *B, Matr3 *C);

but also

Add(Matr1 *A, Matr2 *B, Matr3 *C);
Add(Matr1 *A, Matr1 *B, Matr2 *C);

and a number of other combinations. This partially satisfies the design goal,
but one has to be very careful about casting the pointers prior to
calling Add(), in order to ensure that a suitable variant will be called.
Instead of writing

Add(A, B, C);

I would then have to write

Add( dynamic_cast<Matr1 *>(A),
dynamic_cast<Matr2 *>(B),
dynamic_cast<Matr3 *>(C) );

which is obviously inconvenient, and also invalidates the idea
of polymorphism, as the run type of the objects must be known
at compile time. In addition any extension of the library becomes
difficult, because if I define a new matrix type, together with
new Add() variants needed for it, the new Add() functions will
not be known as friends of the respective classes in the library.
Implementing Add() as a class method would also be difficult, because
there would have to be many overloaded variants in every class, the number
increasing with every new derived class.


A possible alternative might be to define one global function

void Add(Matr *A, Matr *B, Matr *C);

(or class method), and perform the run-time type checking within this
function. This would require having several if-else options within the
function. However this solution appears not very elegant, again somewhat
inconsistent with the idea of the polymorphism, and similarly hard to
extend on new matrix types (apart from being likely inefficient).


In view of the above, my question is:

What else can be done, using C++ capabilities, to allow me
just to write

Add(A, B, C);

and at the same time be able to extend the library
without having to make changes in the library?


Sincerely,

L.B.


*-------------------------------------------------------------------*
| Dr. Leslaw Bieniasz, |
| Institute of Physical Chemistry of the Polish Academy of Sciences,|
| Department of Electrochemical Oxidation of Gaseous Fuels, |
| ul. Zagrody 13, 30-318 Cracow, Poland. |
| tel./fax: +48 (12) 266-03-41 |
| E-mail: http://www.velocityreviews.com/forums/(E-Mail Removed) |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
 
Reply With Quote
 
 
 
 
Paul
Guest
Posts: n/a
 
      09-20-2004
Leslaw Bieniasz <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)-kr.edu.pl>...
> Cracow, 20.09.2004
>
> Hello,
>
> I need to implement a library containing a hierarchy of classes
> together with some binary operations on objects. To fix attention,
> let me assume that it is a hierarchy of algebraic matrices with the
> addition operation. Thus, I want to have a virtual base class
>
> class Matr;
>
> and some derived classes:
>
> class Matr1 : public Matr;
> class Matr2 : public Matr;
> class Matr3 : public Matr2;
>
> etc.
>
> I want to be able to write the code like the following one, which
> intensively uses polymorphism:
>
> Matr* A = new Matr1();
> Matr* B = new Matr2();
> Matr* C = new Matr3();
> .
> .
> .
> Add(A, B, C);
>
> [or C->Add(A,B); ]
>
> where the addition is supposed to be implemented either as a global function
> that adds A to B and places the result into C, or as a virtual method
> of a base class, which calculates the sum of A and B, and puts the result
> into the object which calls the method.
> In the first case the function(s) would have to be friends of the Matr1,
> Matr2, etc. class(es). The same could be done using overloaded operators +,
> but this is a secondary issue for my question, see below. I also abstract,
> at the moment, from how exactly the binary operation is realised (for
> example, one might use, or not use, expression templates).


You should consider a static method as an alternative to the global
method:
Matr::Add(A,B,C);
I prefer this as it makes things clearer.

Then you could experiment with different implementations of a static
method in different derived classes:
Matr1::Add(...);
Matr2::Add(...);


>
> The problem with the above is that each of the matrices
> Matr1, Matr2, Matr3,... may require a different implementation
> of the addition operation. For example, adding a diagonal matrix to a
> dense matrix is possible, but is done differently from adding a dense
> matrix to a dense matrix, or diagonal matrix to a diagonal matrix.
> Thus, I may need to have a number of versions of Add():
>
> Add(Matr1 *A, Matr1 *B, Matr1 *C);
> Add(Matr2 *A, Matr2 *B, Matr2 *C);
> Add(Matr3 *A, Matr3 *B, Matr3 *C);
>
> but also
>
> Add(Matr1 *A, Matr2 *B, Matr3 *C);
> Add(Matr1 *A, Matr1 *B, Matr2 *C);
>
> and a number of other combinations. This partially satisfies the design goal,
> but one has to be very careful about casting the pointers prior to
> calling Add(), in order to ensure that a suitable variant will be called.
> Instead of writing
>
> Add(A, B, C);
>
> I would then have to write
>
> Add( dynamic_cast<Matr1 *>(A),
> dynamic_cast<Matr2 *>(B),
> dynamic_cast<Matr3 *>(C) );
>

You don't need to do that there are better ways.
One problem is that I don't know how many different parameter
combinations you intend. Is it always going to be 2 or 3 parameters?
With 3 parameters max and say 16 different types of matrixes the
number of combinations becomes excessive for overloading.

What you might want to do is make all parameters basesphsuedo code)
Add(MatrBase*, MatrBase*);
now do you want to overload this to take a various numbers of
arguments?
Alternatively you could make it only take 2 arguments and nest
function calls, then you would need to return an object:
Add(Add(MatrBase*, MatrBase*), Add MatrBase*);
But this could be hidden inside a class so that you only call Add on 1
object.

Matr2Obj.Add( MatrBase*, MatrBase*)
with an implementation of:

Matr2::Add(MatrBase* arg1, MatrBase* arg2){
//done on two lines for clarity
tempobj =arg1->Add(arg2);
this->Add(tempObj);
}

so every Matrix class imlements and Add method which takes 1 parameter
and does its stuff accordingly then returns an object, which will be
of polymorphic type.

Matr& Matr::Add(Matr*){
//do add stuff
return *this;
//reutrn reference or pointer or temp obj?
//this depends on other aspects of your design.
//reference seems like a good candidate.
}

I think you should consider using a return value as most add
operations usually do, but you say you want to store result in a
particular object so this is also a grey area without understanding
more detail of your design.



> which is obviously inconvenient, and also invalidates the idea
> of polymorphism, as the run type of the objects must be known
> at compile time. In addition any extension of the library becomes
> difficult, because if I define a new matrix type, together with
> new Add() variants needed for it, the new Add() functions will
> not be known as friends of the respective classes in the library.

I agree that type casting is not a godd way, also templates are ruled
out.
Extensibility should and could be preserved using the method I tried
to describe above using polyorphism.

> Implementing Add() as a class method would also be difficult, because
> there would have to be many overloaded variants in every class, the number
> increasing with every new derived class.


Yes I see this, this rules out global and class methods.
>
>
> A possible alternative might be to define one global function
>
> void Add(Matr *A, Matr *B, Matr *C);
>
> (or class method), and perform the run-time type checking within this
> function. This would require having several if-else options within the
> function. However this solution appears not very elegant, again somewhat
> inconsistent with the idea of the polymorphism, and similarly hard to
> extend on new matrix types (apart from being likely inefficient).

RTTI doesn't sound like a good option.
>
>
> In view of the above, my question is:
>
> What else can be done, using C++ capabilities, to allow me
> just to write
>
> Add(A, B, C);
>
> and at the same time be able to extend the library
> without having to make changes in the library?

I'm not sure if I explained properly because I have a few grey areas
about your design goals as described above, best I can see at present
is to create a pure virtual Add() which takes one parameter and
returns an object. Then dependant on your design goals overload this
to accept more parameters with each overloaded version calling the
virtual single parameter implementation of each parameter bar the last
with the argument of the parameter in the parameter list.
Then perhaps this can be improved upon to remove the returned object
for design goal and/or efficiency reasons.
>
>
> Sincerely,
>
> L.B.
>
>
> *-------------------------------------------------------------------*
> | Dr. Leslaw Bieniasz, |
> | Institute of Physical Chemistry of the Polish Academy of Sciences,|
> | Department of Electrochemical Oxidation of Gaseous Fuels, |
> | ul. Zagrody 13, 30-318 Cracow, Poland. |
> | tel./fax: +48 (12) 266-03-41 |
> | E-mail: (E-Mail Removed) |
> *-------------------------------------------------------------------*
> | Interested in Computational Electrochemistry? |
> | Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
> *-------------------------------------------------------------------*


To sum up my patchy explanations will something like this help:

class Matr{
virtual Matr& Add(Matr&)=0{return *this}
};

class Matr1:Matr{
virtual Matr& Add(Matr*);
virtual Matr& Add(Matr*, Matr*);
virtual Matr& Add(Matr*, Matr*, Matr*);
};

Matr& Matr1::Add(Matr* arg){
//do add stuff
return *this;
}

Matr& Matr1::Add(Matr* arg1, Matr* arg2){
this->Add(arg1->Add(arg2));
return *this;
}

Matr& Matr1::Add(Matr* arg1, Matr* arg2, Matr* arg3){
this->Add(arg1->Add(arg2->Add(arg3)));
}

usage:
Matr* mp_array[5] = {new Matr1, new Matr1, new Matr1, new Matr1,new
Matr1};

mp_array[0]->Add(mp_array[1],mp_array[2],mp_array[3]);

etc etc.


HTH
Paul.
 
Reply With Quote
 
 
 
 
Paul
Guest
Posts: n/a
 
      09-20-2004
[snip]
Just to add that obviously the previous explanation I posted would
need to overload Add() to accept a reference type parameter:
Matr& Add(Matr&);

In fact reference tpyes should be the base design and it should be
overloaded for pointers IMO.

Paul.
 
Reply With Quote
 
Leslaw Bieniasz
Guest
Posts: n/a
 
      09-22-2004


Cracow, 22.09.2004

Hello,


On Mon, 20 Sep 2004, Paul wrote:

> Leslaw Bieniasz <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)-kr.edu.pl>...
> > I need to implement a library containing a hierarchy of classes
> > together with some binary operations on objects. To fix attention,
> > let me assume that it is a hierarchy of algebraic matrices with the
> > addition operation. Thus, I want to have a virtual base class
> >
> > class Matr;
> >
> > and some derived classes:
> >
> > class Matr1 : public Matr;
> > class Matr2 : public Matr;
> > class Matr3 : public Matr2;
> >
> > etc.
> >
> > I want to be able to write the code like the following one, which
> > intensively uses polymorphism:
> >
> > Matr* A = new Matr1();
> > Matr* B = new Matr2();
> > Matr* C = new Matr3();
> > .
> > .
> > .
> > Add(A, B, C);
> >
> > [or C->Add(A,B); ]
> >
> > where the addition is supposed to be implemented either as a global function
> > that adds A to B and places the result into C, or as a virtual method
> > of a base class, which calculates the sum of A and B, and puts the result
> > into the object which calls the method.
> > In the first case the function(s) would have to be friends of the Matr1,
> > Matr2, etc. class(es). The same could be done using overloaded operators +,
> > but this is a secondary issue for my question, see below. I also abstract,
> > at the moment, from how exactly the binary operation is realised (for
> > example, one might use, or not use, expression templates).

>
> You should consider a static method as an alternative to the global
> method:
> Matr::Add(A,B,C);
> I prefer this as it makes things clearer.
>
> Then you could experiment with different implementations of a static
> method in different derived classes:
> Matr1::Add(...);
> Matr2::Add(...);


--------------------------------
I don't see how this might be helpful, as this would require the
knowledge, at compile time, of which static function to call,
whereas my goal is that this issue be resolved at run time.
--------------------------------

> > The problem with the above is that each of the matrices
> > Matr1, Matr2, Matr3,... may require a different implementation
> > of the addition operation. For example, adding a diagonal matrix to a
> > dense matrix is possible, but is done differently from adding a dense
> > matrix to a dense matrix, or diagonal matrix to a diagonal matrix.
> > Thus, I may need to have a number of versions of Add():
> >
> > Add(Matr1 *A, Matr1 *B, Matr1 *C);
> > Add(Matr2 *A, Matr2 *B, Matr2 *C);
> > Add(Matr3 *A, Matr3 *B, Matr3 *C);
> >
> > but also
> >
> > Add(Matr1 *A, Matr2 *B, Matr3 *C);
> > Add(Matr1 *A, Matr1 *B, Matr2 *C);
> >
> > and a number of other combinations. This partially satisfies the design goal,
> > but one has to be very careful about casting the pointers prior to
> > calling Add(), in order to ensure that a suitable variant will be called.
> > Instead of writing
> >
> > Add(A, B, C);
> >
> > I would then have to write
> >
> > Add( dynamic_cast<Matr1 *>(A),
> > dynamic_cast<Matr2 *>(B),
> > dynamic_cast<Matr3 *>(C) );
> >

> You don't need to do that there are better ways.
> One problem is that I don't know how many different parameter
> combinations you intend. Is it always going to be 2 or 3 parameters?
> With 3 parameters max and say 16 different types of matrixes the
> number of combinations becomes excessive for overloading.




> What you might want to do is make all parameters basesphsuedo code)
> Add(MatrBase*, MatrBase*);
> now do you want to overload this to take a various numbers of
> arguments?
> Alternatively you could make it only take 2 arguments and nest
> function calls, then you would need to return an object:
> Add(Add(MatrBase*, MatrBase*), Add MatrBase*);
> But this could be hidden inside a class so that you only call Add on 1
> object.
>
> Matr2Obj.Add( MatrBase*, MatrBase*)
> with an implementation of:
>
> Matr2::Add(MatrBase* arg1, MatrBase* arg2){
> //done on two lines for clarity
> tempobj =arg1->Add(arg2);
> this->Add(tempObj);
> }
>
> so every Matrix class imlements and Add method which takes 1 parameter
> and does its stuff accordingly then returns an object, which will be
> of polymorphic type.
>
> Matr& Matr::Add(Matr*){
> //do add stuff
> return *this;
> //reutrn reference or pointer or temp obj?
> //this depends on other aspects of your design.
> //reference seems like a good candidate.
> }
>
> I think you should consider using a return value as most add
> operations usually do, but you say you want to store result in a
> particular object so this is also a grey area without understanding
> more detail of your design.


-----------------------------------
No, my problem is not that I may have a variable number of addends
(although this issue presents another difficulty).
In the problem that I described, I always have two rvalue arguments and
one lvalue argument:

C = A + B

but I can have multiple types of A, B, and C, so that the entire
(binary) operation is polymorphic.
------------------------------------

> > which is obviously inconvenient, and also invalidates the idea
> > of polymorphism, as the run type of the objects must be known
> > at compile time. In addition any extension of the library becomes
> > difficult, because if I define a new matrix type, together with
> > new Add() variants needed for it, the new Add() functions will
> > not be known as friends of the respective classes in the library.

> I agree that type casting is not a godd way, also templates are ruled
> out.
> Extensibility should and could be preserved using the method I tried
> to describe above using polyorphism.
>
> > Implementing Add() as a class method would also be difficult, because
> > there would have to be many overloaded variants in every class, the number
> > increasing with every new derived class.

>
> Yes I see this, this rules out global and class methods.
> >
> >
> > A possible alternative might be to define one global function
> >
> > void Add(Matr *A, Matr *B, Matr *C);
> >
> > (or class method), and perform the run-time type checking within this
> > function. This would require having several if-else options within the
> > function. However this solution appears not very elegant, again somewhat
> > inconsistent with the idea of the polymorphism, and similarly hard to
> > extend on new matrix types (apart from being likely inefficient).

> RTTI doesn't sound like a good option.
> >
> >
> > In view of the above, my question is:
> >
> > What else can be done, using C++ capabilities, to allow me
> > just to write
> >
> > Add(A, B, C);
> >
> > and at the same time be able to extend the library
> > without having to make changes in the library?

> I'm not sure if I explained properly because I have a few grey areas
> about your design goals as described above, best I can see at present
> is to create a pure virtual Add() which takes one parameter and
> returns an object. Then dependant on your design goals overload this
> to accept more parameters with each overloaded version calling the
> virtual single parameter implementation of each parameter bar the last
> with the argument of the parameter in the parameter list.
> Then perhaps this can be improved upon to remove the returned object
> for design goal and/or efficiency reasons.
> >

> To sum up my patchy explanations will something like this help:
>
> class Matr{
> virtual Matr& Add(Matr&)=0{return *this}
> };
>
> class Matr1:Matr{
> virtual Matr& Add(Matr*);
> virtual Matr& Add(Matr*, Matr*);
> virtual Matr& Add(Matr*, Matr*, Matr*);
> };
>
> Matr& Matr1::Add(Matr* arg){
> //do add stuff
> return *this;
> }
>
> Matr& Matr1::Add(Matr* arg1, Matr* arg2){
> this->Add(arg1->Add(arg2));
> return *this;
> }
>
> Matr& Matr1::Add(Matr* arg1, Matr* arg2, Matr* arg3){
> this->Add(arg1->Add(arg2->Add(arg3)));
> }
>
> usage:
> Matr* mp_array[5] = {new Matr1, new Matr1, new Matr1, new Matr1,new
> Matr1};
>
> mp_array[0]->Add(mp_array[1],mp_array[2],mp_array[3]);
>
> etc etc.
>


-------------------------------------------
To express my problem more rigorously, I now think this is an example
of the "multiple dispatch" problem, as described by
Scott Meyers in "More Effective C++", Addison-wesley, 1996, p. 228.
Unfortunately, he focused on the remedies for the unary operation
case, such as

C = A

whereas I need binary (and multi-operand, as you indicated, operations).
I also think that his proposal may not be very efficient, as the
lookup procedure involves std::map searches, which I expect to be rather
slow.

Hence, I can reformulate my question: where can I find descriptions
of how to handle the multiple dispatch problem efficiently and
effectively in the case of binary and multi-operand operations.


Sincerely,

L.B.


*-------------------------------------------------------------------*
| Dr. Leslaw Bieniasz, |
| Institute of Physical Chemistry of the Polish Academy of Sciences,|
| Department of Electrochemical Oxidation of Gaseous Fuels, |
| ul. Zagrody 13, 30-318 Cracow, Poland. |
| tel./fax: +48 (12) 266-03-41 |
| E-mail: (E-Mail Removed) |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
 
Reply With Quote
 
Paul
Guest
Posts: n/a
 
      09-22-2004
Leslaw Bieniasz <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)-kr.edu.pl>...
> Cracow, 22.09.2004


[snip]
>
> -----------------------------------
> No, my problem is not that I may have a variable number of addends
> (although this issue presents another difficulty).
> In the problem that I described, I always have two rvalue arguments and
> one lvalue argument:
>
> C = A + B
>
> but I can have multiple types of A, B, and C, so that the entire
> (binary) operation is polymorphic.
> ------------------------------------


It seems like will need a method for all additon combinations.
for example if your library started of with 3 types of matrix say:
DenseMatrix
DiamondMatrix
HybridMatrix

Lets call them A,B and C.
Then your going to need a method for each to be able to add each of
the others and itself thus allowing for commutativity:
A::Add(A);
A::Add(B);
A::Add(C);
B::Add(A);
B::Add(B); etc etc..

So then you can do:
C=A+B; C=C+C; C=B+A; B=C+A etc etc for all combinations;

Then when you later wish to add a Matrix type ,call it D, you'll need
to include an Add method for each A,B and C:
D::Add(A);
D::Add(B); etc .

So now you can do D=A+B+C; D+B; D+A
But what if A,B or C tries to add D, you cannot do A=B+D because there
is no method:
A::Add(D) (doesn't exist)

But what A can do is call D::Add(A) if A knows about D's Base class
and Ds' Add method is virtual.
It also has to know that it doesn't know about D, here's how you could
do this:

create an enumerator:
enum Matrix_types{A,B,C}

class A{
Matrix_types myType;
int known_types;
A():known_types(3),myType(A){//constructor}
};

do the same for all other classes.
When you want to add D you extend the enumerator to include D:
enum Matrix_types{A,B,C,D}

D():known_types(4), myType(D){//constructor}
Now D knows of 4 types but A only knows about 3.

Now back to the problem of A calling D's Add method.
When A gets called to Add(D) it knows that it can only add the first 3
enumerated types(A,B and C) so what it does is call D::Add(A). D can
handle A's so this is no problem.

This is a design that will work but it basically requires a function
for every combination of two addable Marix_types and I'm still not
entirely sure this is what you want.

If some of the functionality is duplicated across Methods you can
reduce the size of your lib.

Here is a rough example how you could use an array of function
pointers in each class to point at the functions:

//Compilable code , hopefully.

enum types{T1,T2,T3};
/*for example T1=DenseMatr T2=DiamondMatr T3=HybridMatr*/

class Matr{
public:
Matr(){}
virtual ~Matr(){}
virtual Matr& _add(Matr*,types)=0{return *this;}
virtual Matr& Add(Matr*){return *this;}
virtual Matr& Add(Matr*, Matr*) {return *this;}
};

/*function declarations*/
Matr* T1addT1(Matr* p1, Matr* p2);
Matr* T1addT2(Matr* p1, Matr* p2);
Matr* T1addT3(Matr* p1, Matr* p2);



typedef Matr* (*FP)(Matr*,Matr*);


class DenseMatrublic Matr{
public:
FP fp[3];
/*lib contains 3 types of Matrix at time of writing */
/*so DenseMatrix need to be able to add 3 types including itself? */
int f_count;
types T;
DenseMatr():T(T1),f_count(2){
fp[0]= T1addT1;
fp[1]= T1addT2;
fp[2]= T1addT3;
/*obviously critical that the correct function is assigned to FP when
writing code*/
}
virtual ~DenseMatr(){}
Matr& _add(Matr* arg, types t){
if(t<f_count)
fp[t](this,arg);
/*if this class knows about arg type*/
else
arg->Add(this);
/*if arg type is an afterthought it should*/
/*provide add functionality for this type*/
return *this;
}
};

/*bare class*/
class DiamondMatrublic Matr{
public:
FP fp;
types T;
DiamondMatr():T(T2){}
virtual ~DiamondMatr(){}
Matr& _add(Matr* arg, types T){
return *this;}
};

/*bare class*/
class HybridMatrublic Matr{
public:
FP fp;
types T;
HybridMatr():T(T3){}
virtual ~HybridMatr(){}
Matr& _add(Matr* arg, types T){
return *this;}
};


class Matr1ublic DenseMatr{
public:
Matr1(){}
~Matr1(){}
Matr1(const Matr1& rhs){}

Matr1& Add(Matr* arg){
arg->_add(this,T);
return *this;
}

Matr1& Add(Matr* p1, Matr* p2){
Add(&(p1->Add(p2)));
return *this;
}
};

Matr* T1addT1(Matr* p1, Matr* p2){
static_cast<DenseMatr*>(p1);
static_cast<DenseMatr*>(p2);
/*Do addition*/
return p1;
}

Matr* T1addT2(Matr* p1, Matr* p2){
static_cast<DenseMatr*>(p1);
static_cast<DiamondMatr*>(p2);
/*Do addition*/
return p1;
}
Matr* T1addT3(Matr* p1, Matr* p2){
static_cast<DenseMatr*>(p1);
static_cast<HybridMatr*>(p2);
/*Do addition*/
return p1;
}


int main(int argc, char *argv[])
{
Matr* m01 = new Matr1;
Matr* m02 = new Matr1;

m01->Add(m02,m01);

delete m01;
delete m02;
return 0;
}




I know it's rough but it's a bit complicated and needs a fair bit of
work to be made complete.

> > > which is obviously inconvenient, and also invalidates the idea
> > > of polymorphism, as the run type of the objects must be known
> > > at compile time. In addition any extension of the library becomes
> > > difficult, because if I define a new matrix type, together with
> > > new Add() variants needed for it, the new Add() functions will
> > > not be known as friends of the respective classes in the library.

> > I agree that type casting is not a godd way, also templates are ruled
> > out.
> > Extensibility should and could be preserved using the method I tried
> > to describe above using polyorphism.
> >
> > > Implementing Add() as a class method would also be difficult, because
> > > there would have to be many overloaded variants in every class, the number
> > > increasing with every new derived class.

> >
> > Yes I see this, this rules out global and class methods.
> > >
> > >
> > > A possible alternative might be to define one global function
> > >
> > > void Add(Matr *A, Matr *B, Matr *C);
> > >
> > > (or class method), and perform the run-time type checking within this
> > > function. This would require having several if-else options within the
> > > function. However this solution appears not very elegant, again somewhat
> > > inconsistent with the idea of the polymorphism, and similarly hard to
> > > extend on new matrix types (apart from being likely inefficient).

> RTTI doesn't sound like a good option.
> > >
> > >
> > > In view of the above, my question is:
> > >
> > > What else can be done, using C++ capabilities, to allow me
> > > just to write
> > >
> > > Add(A, B, C);
> > >
> > > and at the same time be able to extend the library
> > > without having to make changes in the library?

> > I'm not sure if I explained properly because I have a few grey areas
> > about your design goals as described above, best I can see at present
> > is to create a pure virtual Add() which takes one parameter and
> > returns an object. Then dependant on your design goals overload this
> > to accept more parameters with each overloaded version calling the
> > virtual single parameter implementation of each parameter bar the last
> > with the argument of the parameter in the parameter list.
> > Then perhaps this can be improved upon to remove the returned object
> > for design goal and/or efficiency reasons.
> > >

> > To sum up my patchy explanations will something like this help:
> >
> > class Matr{
> > virtual Matr& Add(Matr&)=0{return *this}
> > };
> >
> > class Matr1:Matr{
> > virtual Matr& Add(Matr*);
> > virtual Matr& Add(Matr*, Matr*);
> > virtual Matr& Add(Matr*, Matr*, Matr*);
> > };
> >
> > Matr& Matr1::Add(Matr* arg){
> > //do add stuff
> > return *this;
> > }
> >
> > Matr& Matr1::Add(Matr* arg1, Matr* arg2){
> > this->Add(arg1->Add(arg2));
> > return *this;
> > }
> >
> > Matr& Matr1::Add(Matr* arg1, Matr* arg2, Matr* arg3){
> > this->Add(arg1->Add(arg2->Add(arg3)));
> > }
> >
> > usage:
> > Matr* mp_array[5] = {new Matr1, new Matr1, new Matr1, new Matr1,new
> > Matr1};
> >
> > mp_array[0]->Add(mp_array[1],mp_array[2],mp_array[3]);
> >
> > etc etc.
> >

>
> -------------------------------------------
> To express my problem more rigorously, I now think this is an example
> of the "multiple dispatch" problem, as described by
> Scott Meyers in "More Effective C++", Addison-wesley, 1996, p. 228.
> Unfortunately, he focused on the remedies for the unary operation
> case, such as
>
> C = A
>
> whereas I need binary (and multi-operand, as you indicated, operations).
> I also think that his proposal may not be very efficient, as the
> lookup procedure involves std::map searches, which I expect to be rather
> slow.
>
> Hence, I can reformulate my question: where can I find descriptions
> of how to handle the multiple dispatch problem efficiently and
> effectively in the case of binary and multi-operand operations.
>
>
> Sincerely,
>
> L.B.
>
>
> *-------------------------------------------------------------------*
> | Dr. Leslaw Bieniasz, |
> | Institute of Physical Chemistry of the Polish Academy of Sciences,|
> | Department of Electrochemical Oxidation of Gaseous Fuels, |
> | ul. Zagrody 13, 30-318 Cracow, Poland. |
> | tel./fax: +48 (12) 266-03-41 |
> | E-mail: (E-Mail Removed) |
> *-------------------------------------------------------------------*
> | Interested in Computational Electrochemistry? |
> | Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
> *-------------------------------------------------------------------*


I have never read the book you mention above. Sorry can't help you
there.
Paul.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
inheritance, list of objects, polymorphism barbaros C++ 28 12-19-2009 02:18 PM
Dynamic polymorphism vs. Static polymorphism Krivenok Dmitry C++ 13 06-01-2006 09:49 AM
stand-alone JMS, other JDBC operations, and transactions ( ActiveMQ + JOTM + JDBC operations ) Jesus M. Salvo Jr. Java 2 02-11-2006 06:33 PM
Binary File Operations Jeremy Robbins Perl 0 07-14-2004 09:45 PM
Binary Operations MarcoGT Java 4 02-28-2004 11:06 AM



Advertisments