Revamp the OCCT Handle

Forums: 

Hello everyone,

My name is FrГ©dГ©ric and I am in charge of the OCCT refactoring project.

We started to look at solutions to replace the Handle classes with a more modern implementation.

The mantis issue for this task is #24023

The different approaches we considered are:
- re-write the current implementation by a template-based one,
- use a third-party smart-pointer such as the ones provided by boost,
- use the standard C++ shared pointer.

Since we do not wish to implement an OCCT-specific solution, for now, we exclude the first approach.

The second solution has the disadvantage to create a dependency to a third-party product, so we also exclude this approach for the moment.

Given that the C++ shared pointer is now standard C++ and supported by at least two major compilers – GCC (which implementation is based on boost) and Visual Studio –, we are going to explore this solution.

The Handle revamping must not impact the existing code. As the C++ shared pointer does not support exactly the same services than the OCCT Handle (e.g. Nullify, Downcast), we could subclass the C++ shared pointer for implementing the OCCT-specific services.
Let’s call this OCCT shared pointer "handle", with lower-case h. It comes to make the current Handle macro equivalent to this handle template class.

For example:
typedef handle<Geom_Point> Handle(Geom_Point);
where the template class handle is a subclass of std::shared_ptr.
As such, by a simple recompilation, the existing OCCT code would be based on the standard C++ shared pointer.

Also, in new code, we can write either
handle<Geom_Point> p;
or
std::shared_ptr<Geom_Point> p;
whether we need a pointer compatible or not with the current OCCT Handle.

This new class will be added to a new namespace "occ".

Although it seems not recommended to subclass the C++ shared pointer, we intend to prototype this solution.

Has anyone experienced the C++ shared pointer and/or subclassing it?
Any other remarks are welcomed.

FrГ©dГ©ric

Aaron Michalk's picture

I vote for using the standard C++ shared pointer.

Isn't std::shared_ptr::reset() equivalent to Nullify()?

Isn't std::dynamic_pointer_cast() equivalent to Downcast()?

Aaron

Frédéric Pons's picture

Hello everyone,

Here is the status on the Handle Revamping task's.

As explained before we decided to go with the C++11 implementation.
In the same process, I started to remove the persistent classes.

Note that I'm working on Linux with gcc version 4.4.1.

1) I defined a class handle which inherits from std::shared_ptr<T>.
In this class, I implemented the methods specific to Handle_Standard_Transient, like IsNull, by using equivalent methods of std::shared_ptr<T>.
I also added constructors and operators to be able to convert a handle<B> to handle<A>, when B inherits from A.

------
#include <tr1/memory>

template <class T>
class handle : public std::tr1::shared_ptr<T>
{
handle() : std::shared_ptr<T>() {}
template<class U> handle(const handle<U>& h) : std::shared_ptr<T>((T*)h.get()) {}
handle(const T* t) : std::shared_ptr<T>((T*)t) {}

template------

This handle class being defined into the new occ namespace, the Handle() macro is redefined as follows:
------
#define Handle(ClassName) occ::handle<ClassName>
------

2) I then redefined some macros in Standard_DefineHandle.hxx as follows:
Replacement of DEFINE_STANDARD_HANDLE, which defines the full Handle class, by a simple typedef.
Nullification of the IMPLEMENT_DOWNCAST macro, which was implementing the DownCast method - it is no more usefull since the method is fully implemented in the class.

------
DEFINE_STANDARD_HANDLE(C1,C2)
class C1; \
Standard_EXPORT const Handle(Standard_Type)& STANDARD_TYPE(C1); \
typedef handle<C1> Handle_##C1;

#define IMPLEMENT_DOWNCAST(C1,BC)
------

3) Based on this implementation, some changes in existing sources are required:
* I had to fix some typo in the usage of the Handle macro (due to the new definition)

Example:
------
Handle(Draw_Drawable3D()) --> Handle(Draw_Drawable3D)()
const Handle(Geom_Surface&) --> const Handle(Geom_Surface)&
Handle(Geom_Surface::DownCast()) --> Handle(Geom_Surface)::DownCast()
------

* The forward declarations of Handle classes also had to be removed or just replaced by a forward declaration of the class.

Example:
------
class Handle_Geom_Surface; // Handle_Geom_Surface is a typedef
class Handle(Geom_Surface); // Handle(Geom_Surface) is a macro for occ::handle<>
------

* The main issue is with out parameter of handle type.
When the type of a out parameter is a handle the compiler produces an error.

Example:
------
Class A {};
Class B : public A {}

void foo(Handle(A)& outParam)
{
outParam = new B();
}

void bar()
{
Handle(B) b;
foo(b); // compilation error
}
------

There are different ways to fix this.
First, in some cases, we can use a Handle(A) instead of a Handle(B) because, in the rest of the code, nothing about the specialization of B is needed.

Another way, is to cast the Handle(B) into a Handle(A).
This can be done like this:
------
foo(*(Handle(A)*)&b)

// or with a macro
#define super_cast(CL) *(Handle_##CL*)&
foo(super_cast(A)b);
------

This concerns few methods but some of them are widely used, such as:
------
TDF_Label::FindAttribute(..., Handle(TDF_Attribute)&)
IGESData_ParamReader::ReadEntity(..., Handle(IGESData_IGESEntity)&, ...)
------

Which are usually called with derived classes parameters.

4) Currently I built most of the packages and the remaining ones have more to do with the removal of the persistent classes.
I hope to be able to build DRAWEXE soon, so that I can launch the test suits.

If the tests go well, the next step will be to change the way WOK generates the files to get rid of the Handle_X.hxx files.

FrГ©dГ©ric

Denis's picture

Hello,

Very interesting, not much experience to share about that though.
My comment is about this new namespace "occ". Could you please consider adding an "occ/ namespace" for headers too? Ie. replace #include <foo.hxx> by #include <occ/foo.hxx>. The rationale is exactly the same as with namespace.

Frédéric Pons's picture

Yes, having "occ/" before the headers is a good idea that we can consider.

Gerhard Hofmann's picture

Hello FrГ©dГ©ric,
it looks like you are going to eliminate the CDL files. We use these files as an input to our automatic generation of .NET wrapper files (C++ and C#). I know we could also get this information from the header files or even better using swig, but we now have this tool and we would like to keep it running. So please don't get rid of CDL. It is also a good source of documentation.
Or do you plan to deliver a .NET wrapper (or make the classe COM compatible)?
Thanks,
Gerhard

Davide Bazzi's picture

Hello FrГ©dГ©ric,
We are also interested on open cascade support of microsoft .net framework.
Best regards,
Davide

Shockwave's picture

I'm very interested if you'll add Net support for OpenCascade library without needing to make a custom wrapper.

Frédéric Pons's picture

No there is no plan to have .Net support for OpenCascade without using a wrapper.

Getting rid of CDL is also not an immediate plan, and if it is needed to generate wrapper class, we will keep it or find a suitable replacement solution.

diane wilkinson's picture

I'm very interested if you'll add Net support for OpenCascade library without needing to make a custom wrapper.

liuming's picture

please do not use shared_point,or any other similar techonolgy.
as a c++ programmer, pointer is as natural as breathing.The semantic of a pointer is obvious, it's naturally shared, all c++ programmer know this feature.so why conceal this concept,then hardly search for some solution?
As for self-deleting feature, I think it's something bring more confusing than it's soundlike good. accompany self-deleting, you must understand the new concept of ownership share, ownership transfer, ownership copy,etc. so, does it make simpler?

please do not invent new concept at the language level.

István Csanády's picture

Handles are very much like smart pointers. They do reference counting, just like shared_ptr does. Using reference counting is a natural choice for memory management nowadays. Almost without any overhead it provides easy-to-use memory management. The aim of this refactoring project is to use a modern approach, not to introduce reference counting. It has been already done, 20 years ago.

williamdave's picture

The static function it represents a particular problem: it should run on C ++ code. This is (important) the reason for the existence of additional files with the extension ixx DRV subfolder. <...> It is very convenient to get rid of this need as this would eliminate these unnecessary files.