L
Leslaw Bieniasz
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: (e-mail address removed) |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*
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: (e-mail address removed) |
*-------------------------------------------------------------------*
| Interested in Computational Electrochemistry? |
| Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
*-------------------------------------------------------------------*