Search code examples
c++14c++17opencascade

Using this in a seperate class methods crashes the program


I am having a challenge calling methods from a direct parent Base class but I can call methods in the Base class 's parent with ease. To clarify what I mean here is the code:

First the structure of the opencascade library classes:

class TopoDS_Shape 
{
public:
   //..... a lot of methods like Closed(), Oriantable(), etc
};

class TopoDS_Face : public TopoDS_Shape
{
   TopoDS_Face(); // implementation is like TopoDS_Face::TopoDS_Face(){}
}

In my code there are two types of topological faces Plane faces (ModelFace class) and NON-Planar faces (ModelBend class). These two faces share 6 attributes defined in the MFace class but only the ModelBend class has additional attributes of its own so I designed the system as follows

MFace class:

class MFace : public TopoDS_Face 
{
 FaceID mFaceID;
 PlaneType mPlaneType;
 FaceType mFaceType;
 gp_Pnt mFaceNormal;

 public:
  void ModelFace::extractEdges()
  {
    for (TopExp_Explorer edgeEx((*this), TopAbs_EDGE); edgeEx.More(); edgeEx.Next())
    {
      TopoDS_Edge edge = TopoDS::Edge(edgeEx.Current());
      ModelEdge edgex(edge);

      addEdge(edgex);
    }
  }

  // Setters and getters

  // The OpenCascade Lib has a non-template method similar to this for converting a  
  // TopoDS_Shape to a face/edge/vertex/wire
  template<typename T>
  static T& toFace(TopoDS_Face& shape)
  {
    return *(T*) &shape;
   }
};

ModelFace class:

 class ModelFace : public MFace
 {
   ModelFace(); // implementation is like ModelFace::ModelFace(){}

   void ModelFace::init(FaceID faceID) // WORKS LIKE A CHARM!!!
   { 
     setFaceId(faceID);

     std::cout << "ID : " << getFaceId() << '\n';

     if (Closed()) {
      std::cout << "Orr : " << Orientable() << '\n';
     }
   }

 };

ModelBend class:

class ModelBend : public MFace
 {
   // Bend attributes : angles, radius, etc
   ModelBend(); // implementation is like ModelBend::ModelBend(){}

   // Setters and getters
   // methods for computations

};

Usage : There is a Model class that represents a CAD model and stores all its topological ModelFace/ModelBend data. This class gets topological data in the form of a TopoDS_Shape and classifies it in the assignAttributes() as shown below :

  void Model::assignFaceAttributes(const FaceID faceID, TopoDS_Shape& aShape)
  {
    // TODO : set the face attributes
    TopoDS_Face pTopoDSFace = TopoDS::Face(aShape);
    Standard_Real curvature = computeCurvature(pTopoDSFace);

    if (curvature == 0.0){

      std::cout << "Face" << '\n';

      // Convert TopoDS_Face to ModelFace
      ModelFace& pModelFace = MFace::toFace<ModelFace>(pTopoDSFace);

      // This code work well : calls Orientable() in the TopoDS_Shape class
      std::cout << "Orientable? : " << pModelFace.Orientable() << '\n';

      // Works well
      pModelFace.init(faceID);

      // **PROGRAM CRASHES HERE!!!!!!!!!!!!!!**
      pModelFace.extractEdges();

      //...

    } else {
      // .....
      std::cout << "Bend" << '\n';

      // Convert TopoDS_Face to ModelBend
      ModelBend& pModelFace = MFace::toFace<ModelBend>(pTopoDSFace);

      //...
    }

    addFace(&pModelFace);

  }

When I run the program it crashes on the call to pModelFace.extractEdges() in the assignAttributes() but when I copy the for loop in the extractEdges() method into the init() method it works fine.

My OOD/OOP ain't that good. May you please help me solve the problem and the why's of this behaviour. Thanks in advance.


Solution

  • This looks like a duplicate to your own other question: Assigning a TopoDS_Face object to its child object compiles with no errors but I have 3 valgrind errors

    TopoDS_Shape has no virtual methods (including no virtual destructor) and managed within OCCT by copy (e.g. not dynamically allocated). Adding class fields like you do in MFace will work only if you store object exactly like MFace - otherwise you are just casting a smaller in memory object TopoDS_Shape to a larger MFace leading to reading/writing to an uninitialized memory and crash.

    The main difference between your MFace and TopoDS_Face is that TopoDS_Face defines NO new class fields nor virtual methods, which allows aliasing TopoDS_Shape to TopoDS_Face without side effects.

    For instance:

    void parseMFace (TopoDS_Shape& theFace)
    {
      MFace* anMFace = (MFace* )&theFace;
      anMFace->doSomething();
    }
    
    int main()
    {
      MFace anMFace;
      parseMFace (anMFace); // unsafe, but should work
      TopoDS_Face aTFace;
      parseMFace (aTFace); // will crash
      TopoDS_Compound aComp;
      BRep_Builder().MakeCompound (aComp);
      BRep_Builder().Add (aComp, anMFace); // here MFace will be truncated to TopoDS_Shape
      for (TopoDS_Iterator aFaceIter (aComp); aFaceIter.More(); aFaceIter.Next()
      {
        TopoDS_Face& aTFace2 = TopoDS::Face (aFaceIter.Value());
        parseMFace (aTFace2); // will crash, because TopoDS_Compound does not store MFace 
      }
      std::vector<MFace> anMFaces;
      anMFaces.push_back (anMFace);
      parseMFace (anMFaces[0]); // OK, but unsafe
    }