Search code examples
c++ooppolymorphismtypeid

How to avoid typeid when the type is argument


I'm working on a physics project simulation where I need to calculate the potential of 2 molecules.

and this is part of the code that I thought to write:

class Molecule
{
    public:
    double someBulshit;
    virutal double Potential(const Molecule & mol);
}

class LC : public Molecule
{
    public:
    virtual double Potential(const Molecule & mol)
    {
        if(typeid(mol) ==typeid(LC))
            return 1;// for the example
        return 3;
    }

}
class Col : public Molecule
{
    public:
    virtual double Potential(Molecule mol)
    {
        if (typeid(mol) == typeid(Col))
            return 2;
        return 3;
    }
}

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

        double my_potential = mol1.Potential(mol2);
        printf ("%f",my_potential); 
    }

I heard that using typeid is bad, but I cant find another way to do so without using it. This is also performance sensitive and typeid I understood that typeid is not recommended with it.

I tried to split to different functions:

double Potential(const LC & mol);
double Potential(const Col & mol);

But then I cant call them polymorphic..


Solution

  • You need some kind of double dispatch. The usual recommendation here is the Visitor Pattern. However, I don't recommend it in this case.

    I think, that you should keep the Molecule base, and the derived classes. You should add an ID into the Molecule class. And implement double dispatch with a 2D table, indexed by the 2 objects' ID. Like this:

    class Molecule {
      private:
        int m_id;
      public:
        Molecule(int id) : m_id(id) { }
    
        int id() const {
          return m_id;
        }
    };
    
    class LC: public Molecule {
      private:
        // members here
      public:
        LC() : Molecule(0) { }
    };
    
    class Col: public Molecule {
      private:
        // members here
      public:
        Col() : Molecule(1) { }
    };
    
    double potential_lc_vs_lc(const Molecule &a, const Molecule &b) {
      const LC &lc_a = static_cast<LC &>(a);
      const LC &lc_b = static_cast<LC &>(b);
      // calculate potential LC (lc_a) vs LC (lc_b) here
      return ...;
    }
    
    // all the potential_XX_vs_XX functions come here
    
    const double (*potentialCalculatorTable[2][2])(const Molecule &, const Molecule &) = { { potential_lc_vs_lc, potential_lc_vs_col }, ... };
    
    double calculatePotential(const Molecule &a, const Molecule &b) {
      return (*potentialCalculatorTable[a.id()][b.id()])(a, b);
    }
    

    This needs some manual management, but the solution is clear (in my opinion), and fast.