Open CASCADE Handles. Let's handle'em. Part 1
Let me start the first article with something simple yet important if you want to develop on Open CASCADE.
You likely noticed that many classes inherit Standard_Transient, directly or via other ancestors, e.g. Geom_Surface or AIS_InteractiveObject, and that in the code that are used as Handle(Geom_Surface).
Open CASCADE tries to never use pointers (at least in API). Whenever it needs a shared object it uses a handle.
Handle is a well known concept often referred to as a smart pointer. The Boost library (www.boost.org) , for instance, has several classes for smart point and, intrusive_ptr seems to be the closest equivalent to OCC’s Handle. Qt (www.trolltech.com) has QPointer.
Handle provides you with a mechanism to automatically refer to an object without a headache of destruction. The underlying object (Standard_Transient descendant) will get destroyed when the last handle pointing to it is destroyed.
In addition to that handle provides safe type recognition and downcasting. Read Foundation Classes User’s Guide for more details.
Unlike Boost or Qt which define smart pointers with templates, Open CASCADE uses two parallel explicit class hierarchies: one deriving from Standard_Transient and another from Handle_Standard_Transient. When CDL extractor generates a header file, or when you are using a macro DEFINE_STANDARD_HANDLE, then you end up with a new handle class. I believe a choice in favor of hierarchy of Handles was done for historical reason when old compilers in 1990es did not support templates well. Presumably, for the same reason TCollection classes are also based on #defines (until in 2002 there appeared NCollection which is template-based).
For the sake of truth, I must note that there is yet another parallel hierarchy – for so called persistent classes inheriting Standard_Persistent. But as they are almost never used directly we can omit them in this article. But everything said here, applies to those classes as well.
To create your handle class you need to use the following macros defined in Standard_DefineHandle.hxx:
DECLARE_STANDARD_HANDLE(class_name,ancestor_name)
DEFINE_STANDARD_RTTI(class_name)
IMPLEMENT_STANDARD_HANDLE(class_name,ancestor_name)
IMPLEMENT_STANDARD_RTTIEXT(class_name,ancestor_name)
For instance :
DEFINE_STANDARD_HANDLE(OCC_UT_Id,Standard_Transient)
class OCC_UT_Id : public Standard_Transient
{
public:
OCC_UT_Id() : myId (0) {}
OCC_UT_Id(const Standard_Integer theId) : myId (theId) {}
Standard_Integer Id() const { return myId; }
private:
Standard_Integer myId;
public:
DEFINE_STANDARD_RTTI(OCC_UT_Id)
};
IMPLEMENT_STANDARD_HANDLE(OCC_UT_Id,Standard_Transient)
IMPLEMENT_STANDARD_RTTIEXT(OCC_UT_Id,Standard_Transient)
Most curios folks might have noticed that DEFINE_STANDARD_RTTI does not really need an argument as it translates into method declaration inside the class, but it’s kept for better consistency ;-).
Until 6.3.0, the Open CASCADE CDL extractor generated explicit code for handle (e.g. Handle_Geom_Surface.hxx). I have recently sent a modified version thereof to the folks in the company so that it now generates macros-using header making code cleaner. May it eat own food.
Another note. On the forum I saw people using macros IMPLEMENT_STANDARD_RTTI along with IMPLEMENT_STANDARD_TYPE, IMPLEMENT_STANDARD_SUPERTYPE,
IMPLEMENT_STANDARD_SUPERTYPE_ARRAY, etc trying to imitate CDL extractor (what it puts into drv subdirectory). Don’t bother with that, folks. IMPLEMENT_STANDARD_RTTIEXT will do all the work for you, and you code will be cleaner.
(to be continued)
5 comments
Could someone teach me how to insert spaces into the code snippets ? html tags don't work :-|
ReplyDeleteI've had similar problems in wordpress..
ReplyDeleteI don't know if this will work for you.
In wordpress weird things happen when your inserting C++ in the HTML mode. It gets confused by the <> in includes among other things. I found that I use my text editor and past in my stuff in the visual mode it seems to display the way I want it.
It's sort of a work around but it works..
Btw...
Thank you so much for your willingness to share your knowledge.
I'm still a newbie at C++ and one of goals is to use opencascade...
JT
Thanks JT.
ReplyDeleteYes, I also have to use plain text editor (Notepad) in between, before pasting into here. This works so far. Funny things that some html tags (to add spaces) seem to not work here. Too bad.
Really, IMPLEMENT_STANDARD_RTTI macro allows to do one thing that is impossible with IMPLEMENT_STANDARD_RTTIEXT. It is preserving of the full hierarchy of ancestors of the generated class. The list of ancestors is maintained in the class that represents the Type Info of the generated class, and it can be traversed using Standard_AncestorIterator. In the case of usage of a simple macro IMPLEMENT_STANDARD_RTTIEXT this list will contain only one item - the direct father.
ReplyDeleteBut, I agree with you, Roman, that in most cases this piece of functionality is not needed.
Michael
HI Michael,
ReplyDeleteThat's true but that only proves that Standard_AncestorIterator is out of use ;-). Nonetheless, if it's needed, it can be rewritten to use an ancestor object for iteration (e.g. just like Standard_Type::SubType() is implemented).