473,320 Members | 1,865 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

polymorphism and binary operations on objects


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: nb******@cyf-kr.edu.pl |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
Jul 22 '05 #1
4 2375
Leslaw Bieniasz <nb******@cyf-kr.edu.pl> wrote in message news:<Pi*******************************@kinga.cyf-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 bases:(phsuedo 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: nb******@cyf-kr.edu.pl |
*-------------------------------------------------------------------*
| 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.
Jul 22 '05 #2
[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.
Jul 22 '05 #3


Cracow, 22.09.2004

Hello,
On Mon, 20 Sep 2004, Paul wrote:
Leslaw Bieniasz <nb******@cyf-kr.edu.pl> wrote in message news:<Pi*******************************@kinga.cyf-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 bases:(phsuedo 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: nb******@cyf-kr.edu.pl |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
Jul 22 '05 #4
Leslaw Bieniasz <nb******@cyf-kr.edu.pl> wrote in message news:<Pi******************************@kinga.cyf-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 DenseMatr:public 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 DiamondMatr:public Matr{
public:
FP fp;
types T;
DiamondMatr():T(T2){}
virtual ~DiamondMatr(){}
Matr& _add(Matr* arg, types T){
return *this;}
};

/*bare class*/
class HybridMatr:public Matr{
public:
FP fp;
types T;
HybridMatr():T(T3){}
virtual ~HybridMatr(){}
Matr& _add(Matr* arg, types T){
return *this;}
};
class Matr1:public 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: nb******@cyf-kr.edu.pl |
*-------------------------------------------------------------------*
| 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.
Jul 22 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: rashkatsa | last post by:
Hi all ! I have written a little module called 'polymorph' that allows to call different methods of a class that have the same name but that have not the same number of arguments. *args are...
37
by: Mike Meng | last post by:
hi all, I'm a newbie Python programmer with a C++ brain inside. I have a lightweight framework in which I design a base class and expect user to extend. In other part of the framework, I heavily...
10
by: Myster Ious | last post by:
Polymorphism replaces switch statements, making the code more compact/readable/maintainable/OO whatever, fine! What I understand, that needs to be done at the programming level, is this: a...
103
by: Steven T. Hatton | last post by:
§27.4.2.1.4 Type ios_base::openmode Says this about the std::ios::binary openmode flag: *binary*: perform input and output in binary mode (as opposed to text mode) And that is basically _all_ it...
21
by: nicolasg | last post by:
does anyone know a module or something to convert numbers like integer to binary format ? for example I want to convert number 7 to 0111 so I can make some bitwise operations... Thanks
18
by: Seigfried | last post by:
I have to write a paper about object oriented programming and I'm doing some reading to make sure I understand it. In a book I'm reading, however, polymorphism is defined as: "the ability of two...
2
by: sarathy | last post by:
Hi all, I need a small clarification reg. Templates and Polymorphism. I believe templates is really a good feature, which can be used to implement generic functions and classes. But i doubt...
1
weaknessforcats
by: weaknessforcats | last post by:
Introduction Polymorphism is the official term for Object-Oriented Programming (OOP). Polymorphism is implemented in C++ by virtual functions. This article uses a simple example hierarchy which...
11
by: Litvinov Sergey | last post by:
My problem is the following one. I have a huge number of objects of base class: class Base { public: virtual void method(); } And a derived class:
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.