polymorphism and binary operations on objects

Discussion in 'C++' started by Leslaw Bieniasz, Sep 20, 2004.

  1. 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: |
    *-------------------------------------------------------------------*
    | Interested in Computational Electrochemistry? |
    | Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
    *-------------------------------------------------------------------*
     
    Leslaw Bieniasz, Sep 20, 2004
    #1
    1. Advertising

  2. Leslaw Bieniasz

    Paul Guest

    Leslaw Bieniasz <> wrote in message news:<-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: |
    > *-------------------------------------------------------------------*
    > | 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.
     
    Paul, Sep 20, 2004
    #2
    1. Advertising

  3. Leslaw Bieniasz

    Paul Guest

    [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.
     
    Paul, Sep 20, 2004
    #3
  4. Re: polymorphism and binary operations on objects (or multipledispatch problem)

    Cracow, 22.09.2004

    Hello,


    On Mon, 20 Sep 2004, Paul wrote:

    > Leslaw Bieniasz <> wrote in message news:<-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: |
    *-------------------------------------------------------------------*
    | Interested in Computational Electrochemistry? |
    | Visit my web site: http://www.cyf-kr.edu.pl/~nbbienia |
    *-------------------------------------------------------------------*
     
    Leslaw Bieniasz, Sep 22, 2004
    #4
  5. Leslaw Bieniasz

    Paul Guest

    Re: polymorphism and binary operations on objects (or multiple dispatch problem)

    Leslaw Bieniasz <> wrote in message news:<-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: |
    > *-------------------------------------------------------------------*
    > | 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.
     
    Paul, Sep 23, 2004
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jeremy Robbins

    Binary File Operations

    Jeremy Robbins, Jul 14, 2004, in forum: Perl
    Replies:
    0
    Views:
    505
    Jeremy Robbins
    Jul 14, 2004
  2. MarcoGT

    Binary Operations

    MarcoGT, Feb 27, 2004, in forum: Java
    Replies:
    4
    Views:
    648
    Christophe Vanfleteren
    Feb 28, 2004
  3. Jesus M. Salvo Jr.
    Replies:
    2
    Views:
    4,254
    robert
    Feb 11, 2006
  4. Krivenok Dmitry
    Replies:
    13
    Views:
    1,444
    Axter
    Jun 1, 2006
  5. barbaros
    Replies:
    28
    Views:
    802
    tanix
    Dec 19, 2009
Loading...

Share This Page