Quick post. The addressed issue may seem quite obvious for professional C++ developers yet it's often overlooked.
Case1. When you need to downcast a handle to superclass to one to subclass bets are you are following Open CASCADE conventions:
Handle(Geom_Curve) aCurve = ...;
...
Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast (aCurve);
gp_Ax1 anAx1 = aLine->Position();
...
Case2. When you have a function returning a const Handle& you likely often write:
const Handle(MyClass)& GetMyClassInstance();
Handle(MyClass) anInstance = GetMyClassInstance();
//though you could write const Handle(MyClass)& anInstance
Both are totally valid code. But have you ever thought about what happens inside ? Remember that a Handle is not just simple type (like pointer), there is certain overhead inside. This overhead is to maintain a reference counter (increment, decrement) any time you copy a handle. If you use debugger and follow step-by-step instructions you will go through Handle_Standard_Transient::BeginScope(), EndScope(), copy constructor, assignment operator, etc.
This overhead is quite negligible when you deal with a few (hundreds ?) objects. However it may become noticeable when you are making performance-critical computations or deal with dozens of hundreds of objects. For instance, I did notice this when translating huge ACIS-SAT files with CAD Exchanger. Surprisingly BeginScope() and EndScope() appeared among top 5 hotspots.
Bad news is that such issue may often be hidden by other types, not necessarily handles themselves. The most frequent case, which Open CASCADE itself is vastly contaminated with is TopoDS_Shape. As you remember, TopoDS_Shape contains a field myTShape of the TopoDS_TShape type (subclass of Handle_Standard_Transient). So whenever you use something like:
1). TopoDS_Edge anEdge = TopoDS::Edge (aShape);
//instead of const TopoDS_Edge& anEdge = TopoDS::Edge (aShape);
or
2). const TopoDS_Shape& GetShape();
TopoDS_Shape aShape = GetShape();
//instead of const TopoDS_Shape& aShape = GetShape();
or
3). have a class returning TopoDS_Shape instead of const TopoDS_Shape& while it could have (e.g. when returning its own field)
class MyClass
{
public:
...
TopoDS_Shape Child() const { return myChild; }
//instead of const TopoDS_Shape& Child();
private:
TopoDS_Shape myChild;
};
You always assume a penalty of copy constructors and reference counters. Beware and pay attention to that !
Here are some quick recommendations on how to avoid this overhead:
const Handle(MyClass)& aMyClass = * static_cast (&aTarget);
We touched c) in a very first post here. I'm currently thinking to extend Handle class with such a method to cast to const Handle&. Thus, Handle could cast the same as two C++ operators dynamic_cast and static_cast.
Any thoughts on this ?
Case1. When you need to downcast a handle to superclass to one to subclass bets are you are following Open CASCADE conventions:
Handle(Geom_Curve) aCurve = ...;
...
Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast (aCurve);
gp_Ax1 anAx1 = aLine->Position();
...
Case2. When you have a function returning a const Handle& you likely often write:
const Handle(MyClass)& GetMyClassInstance();
Handle(MyClass) anInstance = GetMyClassInstance();
//though you could write const Handle(MyClass)& anInstance
Both are totally valid code. But have you ever thought about what happens inside ? Remember that a Handle is not just simple type (like pointer), there is certain overhead inside. This overhead is to maintain a reference counter (increment, decrement) any time you copy a handle. If you use debugger and follow step-by-step instructions you will go through Handle_Standard_Transient::BeginScope(), EndScope(), copy constructor, assignment operator, etc.
This overhead is quite negligible when you deal with a few (hundreds ?) objects. However it may become noticeable when you are making performance-critical computations or deal with dozens of hundreds of objects. For instance, I did notice this when translating huge ACIS-SAT files with CAD Exchanger. Surprisingly BeginScope() and EndScope() appeared among top 5 hotspots.
Bad news is that such issue may often be hidden by other types, not necessarily handles themselves. The most frequent case, which Open CASCADE itself is vastly contaminated with is TopoDS_Shape. As you remember, TopoDS_Shape contains a field myTShape of the TopoDS_TShape type (subclass of Handle_Standard_Transient). So whenever you use something like:
1). TopoDS_Edge anEdge = TopoDS::Edge (aShape);
//instead of const TopoDS_Edge& anEdge = TopoDS::Edge (aShape);
or
2). const TopoDS_Shape& GetShape();
TopoDS_Shape aShape = GetShape();
//instead of const TopoDS_Shape& aShape = GetShape();
or
3). have a class returning TopoDS_Shape instead of const TopoDS_Shape& while it could have (e.g. when returning its own field)
class MyClass
{
public:
...
TopoDS_Shape Child() const { return myChild; }
//instead of const TopoDS_Shape& Child();
private:
TopoDS_Shape myChild;
};
You always assume a penalty of copy constructors and reference counters. Beware and pay attention to that !
Here are some quick recommendations on how to avoid this overhead:
- a). use const Handle()& (or any other type) as return type whenever possible;
- b). use const& as local variables whenever possible;
- c). substitute Handle(MyClass)::DownCast() with direct cast (but only if you are certain of the type!):
const Handle(MyClass)& aMyClass = * static_cast
We touched c) in a very first post here. I'm currently thinking to extend Handle class with such a method to cast to const Handle&. Thus, Handle could cast the same as two C++ operators dynamic_cast and static_cast.
Any thoughts on this ?