c++ooppolymorphismdouble-dispatch

How to call function dynamically with polymorphism argument


How can I dinamcally call function of the form:childA.function(childB) while their static types are both of the parents?

and with more details:

I have a physics project where I need to calculate the potential of 2 molecules. But I have 2 types of molecules, LC and Col, and each type has its own parameters and stuff, however I want to be able to call the potential of each molecule dynamically.

so I tried this:

#include <iostream>
#include <typeinfo>
#include <stdio.h>

using namespace std;
class Col;
class LC;

class Molecule
{
    public:
    /// more data and functions should be here regarding the molecule
    double someBulshit;
    virtual double potential(const Molecule * mol){}
    virtual double potential(const Col * mol){}
    virtual double potential(const LC * mol){}
};

class LC : public Molecule
{
    public:
    /// more data and functions should be here regarding the LC molecule
    virtual double potential(const Molecule * mol) {return 1;}
    virtual double potential(const LC * mol) {return 2;}
    virtual double potential(const Col * mol) {return 3;}
};
class Col : public Molecule
{
    public:
    /// more data and function should be here regarding the Col molecule
    virtual double potential(const Molecule * mol) {return 4;}
    virtual double potential(const LC * mol) {return 5;}
    virtual double potential(const Col * mol) {return 6;}
};

int main(int argc, char* argv[])
{
    Molecule * mol1 = new Col();
    Molecule * mol2 = new LC();

    double my_potential = mol1->potential(mol2);
    printf ("%f",my_potential);
}

But I get result of 4, as it turns out that the function are called statically, which means that since the static type of mol1 is Molecule the function called is:

virtual double potential(const Molecule * mol) {return 4;}

and not

virtual double potential(const LC * mol) {return 5;}

Is there anyway to call the function dynamically (in OOP design) without the use of typeid in it?

full answer:

After investigation with Peter advice this turn out to be classical double-dispatch problem the full solution is just to call another virtual inside the virtual like this:

#include <iostream>
#include <typeinfo>
#include <stdio.h>


using namespace std;
class Col;
class LC;

class Molecule
{
    public:
    /// more data and functions should be here regarding the molecule
    double someBulshit;
    virtual double potential(const Molecule * mol) const = 0;
    virtual double potential(const Col * mol) const = 0;
    virtual double potential(const LC * mol) const = 0;
};

class LC : public Molecule
{
    public:
    /// more data and functions should be here regarding the LC molecule
    virtual double potential(const Molecule * mol) const {return mol->potential(this);}
    virtual double potential(const LC * mol) const {return 2;}
    virtual double potential(const Col * mol) const {return 3;}
};
class Col : public Molecule
{
    public:
    /// more data and function should be here regarding the Col molecule
    virtual double potential(const Molecule * mol) const {return mol->potential(this);}
    virtual double potential(const LC * mol) const {return 5;}
    virtual double potential(const Col * mol) const {return 6;}
};

int main(int argc, char* argv[])
{
    Molecule * mol1 = new Col();
    Molecule * mol2 = new LC();

    double my_potential = mol1->potential(mol2);
    printf ("%f",my_potential);
}

and this indeed return 3 as desired.


Solution

  • You are always calling potential(Molecule*), since this is the type of the argument you pass.

    If you want to do it dynamically (i.e. make a runtime decision), you will have to do a dynamic cast to decide which type you actially have and then call the right function. You could e.g. implement the function with the base-type argument only in the base class, check the actual type and then call the overloaded function with the right type. The code could then look like this:

    #include <iostream>
    #include <typeinfo>
    #include <stdio.h>
    
    using namespace std;
    class Col;
    class LC;
    
    class Molecule
    {
      public:
      virtual ~Molecule() {}
      /// more data and functions should be here regarding the molecule
      double someBulshit;
      double potential(const Molecule * mol);
      virtual double potential(const Col * mol) = 0;
      virtual double potential(const LC * mol) = 0;
    };
    
    class LC : public Molecule
    {
      public:
      virtual ~LC() {}
      /// more data and functions should be here regarding the LC molecule
      virtual double potential(const LC * mol) {return 2;}
      virtual double potential(const Col * mol) {return 3;}
    };
    class Col : public Molecule
    {
      public:
      virtual ~Col() {}
      /// more data and function should be here regarding the Col molecule
      virtual double potential(const LC * mol) {return 5;}
      virtual double potential(const Col * mol) {return 6;}
    };
    
    double Molecule::potential(const Molecule * mol) {
      const Col *mol_as_Col = dynamic_cast<const Col*>(mol);
      const LC *mol_as_LC = dynamic_cast<const LC*>(mol);
      if(mol_as_Col) {
        return potential(mol_as_Col);
      }
      else if(mol_as_LC) {
        return potential(mol_as_LC);
      }
      else {
        throw;
      }
    }
    
    
    int main(int argc, char* argv[])
    {
      Molecule * mol1 = new Col();
      Molecule * mol2 = new LC();
    
      double my_potential = mol1->potential(mol2);
      printf ("%f",my_potential);
    }
    

    The result will be 5 and not 6 as you wrote in your question, since you put in a type LC and make the call on Col. I also have added the missing virtual destructors, since polymorphic classes should always have it.

    Alternatively, you can try implementing this with template code and avoid using runtime polymorphism in the first place (i.e. never hold pointers of the type Molecule*). If you are interested, I can work out an example, but since you specifically ask for a dynamic solution it does not seem to be the answer to this question.

    EDIT: Here follows a template demonstration. It does not really make sense without the context. I added a function printPotential() which demonstrates how you would write the rest of the code:

    #include <iostream>
    #include <typeinfo>
    #include <stdio.h>
    
    using namespace std;
    
    template<typename MOLECULE1, typename MOLECULE2>
    void printPotential(MOLECULE1 *mol1, MOLECULE2 *mol2) {
      double my_potential = mol1->potential(mol2);
      printf("%f",my_potential);
    }
    
    class Col;
    
    class LC
    {
      public:
      /// more data and functions should be here regarding the LC molecule
      double potential(const LC * mol) {return 2;}
      double potential(const Col * mol) {return 3;}
    };
    class Col
    {
      public:
      /// more data and function should be here regarding the Col molecule
      double potential(const LC * mol) {return 5;}
      double potential(const Col * mol) {return 6;}
    };
    
    
    int main(int argc, char* argv[])
    {
      Col *mol1 = new Col();
      LC *mol2 = new LC();
    
      printPotential(mol1, mol2);
    }
    

    Actually, while writing this I think there might be a third (and actually preferred) method: You need to find an abstracted algorithm how to compute the potential between two molecules by only using common information which can be part of the base class (or obtained through virtual function calls of the base class). In that case you can have one single implementation of potential() (either as member function of the base class with a single argument of the type Molecule* or as a non-member function with two arguments of the type Molecule*). If I assume the potential between the two molecules is the difference of two "absolute" potentials (just as a dumb example), it could look like this:

    #include <iostream>
    #include <typeinfo>
    #include <stdio.h>
    
    using namespace std;
    class Col;
    class LC;
    
    class Molecule
    {
      public:
      virtual ~Molecule() {}
      /// more data and functions should be here regarding the molecule
      double potential(const Molecule * mol) {
        return mol->getAbsolutePotential() - getAbsolutePotential();
      }
      virtual double getAbsolutePotential() const = 0;
    };
    
    class LC : public Molecule
    {
      public:
      virtual ~LC() {}
      /// more data and functions should be here regarding the LC molecule
      double getAbsolutePotential() const {return 42.0;}
    };
    class Col : public Molecule
    {
      public:
      virtual ~Col() {}
      /// more data and function should be here regarding the Col molecule
      double getAbsolutePotential() const {return 120.0;}
    };
    
    
    int main(int argc, char* argv[])
    {
      Molecule * mol1 = new Col();
      Molecule * mol2 = new LC();
    
      double my_potential = mol1->potential(mol2);
      printf ("%f",my_potential);
    }