e7195ab4 |
1 | // Copyright (c) 2014 OPEN CASCADE SAS |
2 | // |
3 | // This file is part of Open CASCADE Technology software library. |
4 | // |
5 | // This library is free software; you can redistribute it and/or modify it under |
6 | // the terms of the GNU Lesser General Public License version 2.1 as published |
7 | // by the Free Software Foundation, with special exception defined in the file |
8 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
9 | // distribution for complete text of the license and disclaimer of any warranty. |
10 | // |
11 | // Alternatively, this file may be used under the terms of Open CASCADE |
12 | // commercial license or contractual agreement. |
13 | |
14 | #ifndef _Standard_Handle_HeaderFile |
15 | #define _Standard_Handle_HeaderFile |
16 | |
17 | #include <Standard_Address.hxx> |
18 | #include <Standard_Stream.hxx> |
19 | #include <Standard_Transient.hxx> |
20 | |
21 | #include <type_traits> |
22 | |
23 | class Standard_Transient; |
24 | |
25 | namespace opencascade { |
26 | |
4796758e |
27 | //! Trait yielding true if class T1 is base of T2 but not the same |
28 | template <class T1, class T2, class Dummy = void> |
29 | struct is_base_but_not_same : std::is_base_of <T1, T2> {}; |
30 | |
31 | //! Explicit specialization of is_base_of trait to workaround the |
32 | //! requirement of type to be complete when T1 and T2 are the same. |
33 | template <class T1, class T2> |
34 | struct is_base_but_not_same <T1, T2, typename std::enable_if <std::is_same <T1, T2>::value>::type> : std::false_type {}; |
35 | |
e7195ab4 |
36 | //! Intrusive smart pointer for use with Standard_Transient class and its descendants. |
37 | //! |
38 | //! This class is similar to boost::intrusive_ptr<>, with additional |
39 | //! feature historically supported by Handles in OCCT: |
40 | //! it has type conversion to const reference to handle to the base types, |
41 | //! which allows it to be passed by reference |
4796758e |
42 | //! in functions accepting reference to handle to base class. |
43 | //! |
44 | //! These casts (potentially unsafe) can be disabled by defining macro |
45 | //! OCCT_HANDLE_NOCAST; if it is defined, generalized copy constructor |
46 | //! and assignment operators are defined allowing to initialize handle |
47 | //! of base type from handle to derived type. |
e7195ab4 |
48 | template <class T> |
49 | class handle |
50 | { |
51 | public: |
52 | //! STL-compliant typedef of contained type |
53 | typedef T element_type; |
54 | |
55 | public: |
56 | |
57 | //! Empty constructor |
58 | handle () : entity(0) {} |
59 | |
60 | //! Constructor from pointer to new object |
61 | handle (const T *thePtr) : entity(const_cast<T*>(thePtr)) |
62 | { |
63 | BeginScope(); |
64 | } |
4796758e |
65 | |
e7195ab4 |
66 | //! Copy constructor |
67 | handle (const handle& theHandle) : entity(theHandle.entity) |
68 | { |
69 | BeginScope(); |
70 | } |
71 | |
5d351a08 |
72 | //! Move constructor |
73 | handle (handle&& theHandle) : entity(theHandle.entity) |
74 | { |
75 | theHandle.entity = 0; |
76 | } |
77 | |
e7195ab4 |
78 | //! Destructor |
79 | ~handle () |
80 | { |
81 | EndScope(); |
82 | } |
83 | |
84 | //! Nullify the handle |
85 | void Nullify() |
86 | { |
87 | EndScope(); |
88 | } |
89 | |
90 | //! Check for being null |
91 | bool IsNull() const { return entity == 0; } |
92 | |
93 | //! Reset by new pointer |
94 | void reset (T* thePtr) |
95 | { |
96 | Assign (thePtr); |
97 | } |
98 | |
99 | //! Assignment operator |
100 | handle& operator= (const handle& theHandle) |
101 | { |
102 | Assign (theHandle.entity); |
103 | return *this; |
104 | } |
105 | |
106 | //! Assignment to pointer |
107 | handle& operator= (const T* thePtr) |
108 | { |
109 | Assign (const_cast<T*>(thePtr)); |
110 | return *this; |
111 | } |
4796758e |
112 | |
5d351a08 |
113 | //! Move operator |
114 | handle& operator= (handle&& theHandle) |
115 | { |
116 | std::swap (this->entity, theHandle.entity); |
117 | return *this; |
118 | } |
119 | |
9016c8bd |
120 | //! STL-like cast to pointer to referred object (note non-const). |
121 | //! @sa std::shared_ptr::get() |
122 | T* get() const { return static_cast<T*>(this->entity); } |
e7195ab4 |
123 | |
124 | //! Member access operator (note non-const) |
125 | T* operator-> () const { return static_cast<T*>(this->entity); } |
126 | |
9016c8bd |
127 | //! Dereferencing operator (note non-const) |
128 | T& operator* () const { return *get(); } |
e7195ab4 |
129 | |
130 | //! Check for equality |
131 | template <class T2> |
132 | bool operator== (const handle<T2>& theHandle) const |
133 | { |
e8862cf4 |
134 | return get() == theHandle.get(); |
e7195ab4 |
135 | } |
136 | |
137 | //! Check for equality |
e8862cf4 |
138 | template <class T2> |
139 | bool operator== (const T2 *thePtr) const |
e7195ab4 |
140 | { |
e8862cf4 |
141 | return get() == thePtr; |
e7195ab4 |
142 | } |
143 | |
144 | //! Check for equality |
e8862cf4 |
145 | template <class T2> |
146 | friend bool operator== (const T2 *left, const handle& right) |
e7195ab4 |
147 | { |
e8862cf4 |
148 | return left == right.get(); |
e7195ab4 |
149 | } |
150 | |
151 | //! Check for inequality |
152 | template <class T2> |
153 | bool operator!= (const handle<T2>& theHandle) const |
154 | { |
e8862cf4 |
155 | return get() != theHandle.get(); |
e7195ab4 |
156 | } |
157 | |
158 | //! Check for inequality |
e8862cf4 |
159 | template <class T2> |
160 | bool operator!= (const T2 *thePtr) const |
e7195ab4 |
161 | { |
e8862cf4 |
162 | return get() != thePtr; |
e7195ab4 |
163 | } |
164 | |
165 | //! Check for inequality |
e8862cf4 |
166 | template <class T2> |
167 | friend bool operator!= (const T2 *left, const handle& right) |
e7195ab4 |
168 | { |
e8862cf4 |
169 | return left != right.get(); |
e7195ab4 |
170 | } |
171 | |
d1a67b9d |
172 | //! Compare operator for possible use in std::map<> etc. |
173 | template <class T2> |
174 | bool operator< (const handle<T2>& theHandle) const |
175 | { |
176 | return get() < theHandle.get(); |
177 | } |
178 | |
a9dde4a3 |
179 | //! Down casting operator from handle to base type |
e7195ab4 |
180 | template <class T2> |
a9dde4a3 |
181 | static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type |
182 | DownCast (const handle<T2>& theObject) |
e7195ab4 |
183 | { |
184 | return handle (dynamic_cast<T*>(const_cast<T2*>(theObject.get()))); |
185 | } |
186 | |
a9dde4a3 |
187 | //! Down casting operator from pointer to base type |
e7195ab4 |
188 | template <class T2> |
a9dde4a3 |
189 | static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type |
190 | DownCast (const T2* thePtr) |
191 | { |
192 | return handle (dynamic_cast<T*>(const_cast<T2*>(thePtr))); |
193 | } |
194 | |
195 | //! For compatibility, define down casting operator from non-base type, as deprecated |
196 | template <class T2> |
197 | Standard_DEPRECATED("down-casting from object of the same or unrelated type is meaningless") |
198 | static handle DownCast (const handle<T2>& theObject, typename std::enable_if<!is_base_but_not_same<T2, T>::value, void*>::type = 0) |
199 | { |
200 | return handle (dynamic_cast<T*>(const_cast<T2*>(theObject.get()))); |
201 | } |
202 | |
203 | //! For compatibility, define down casting operator from non-base type, as deprecated |
204 | template <class T2> |
205 | Standard_DEPRECATED("down-casting from object of the same or unrelated type is meaningless") |
206 | static handle DownCast (const T2* thePtr, typename std::enable_if<!is_base_but_not_same<T2, T>::value, void*>::type = 0) |
e7195ab4 |
207 | { |
208 | return handle (dynamic_cast<T*>(const_cast<T2*>(thePtr))); |
209 | } |
210 | |
d9e90905 |
211 | #if (defined(__clang__)) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1300) || \ |
212 | (defined(_MSC_VER) && _MSC_VER >= 1800) || \ |
213 | (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) |
d1a67b9d |
214 | |
215 | //! Conversion to bool for use in conditional expressions |
216 | explicit operator bool () const |
217 | { |
218 | return entity != nullptr; |
219 | } |
220 | |
221 | #else /* fallback version for compilers not supporting explicit conversion operators (VC10, VC11, GCC below 4.5) */ |
222 | |
223 | //! Conversion to bool-compatible type for use in conditional expressions |
224 | operator Standard_Transient* handle::* () const |
225 | { |
226 | return entity ? &handle::entity : 0; |
227 | } |
228 | |
229 | #endif |
230 | |
4796758e |
231 | // Support of conversions to handle of base type: |
232 | // - copy and move constructors and assignment operators if OCCT_HANDLE_NOCAST is defined |
233 | // - operators of upcast to const reference to base type otherwise |
d9e90905 |
234 | #if (defined(__clang__)) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1206) || \ |
235 | (defined(_MSC_VER) && _MSC_VER >= 1800) || \ |
236 | (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) |
e7195ab4 |
237 | |
4796758e |
238 | #ifdef OCCT_HANDLE_NOCAST |
239 | |
240 | //! Generalized copy constructor. |
241 | //! Constructs handle holding entity of base type (T) from the one which holds entity of derived type (T2). |
242 | template <class T2, typename = typename std::enable_if <is_base_but_not_same <T, T2>::value>::type> |
243 | handle (const handle<T2>& theHandle) : |
244 | entity(theHandle.entity) |
245 | { |
246 | BeginScope(); |
247 | } |
248 | |
5d351a08 |
249 | //! Generalized move constructor |
250 | template <class T2, typename = typename std::enable_if <is_base_but_not_same <T, T2>::value>::type> |
251 | handle (handle<T2>&& theHandle) |
252 | : entity(theHandle.entity) |
253 | { |
254 | theHandle.entity = 0; |
255 | } |
256 | |
4796758e |
257 | //! Generalized assignment operator |
258 | template <class T2, typename = typename std::enable_if <is_base_but_not_same <T, T2>::value>::type> |
259 | handle operator = (const handle<T2>& theHandle) |
260 | { |
261 | Assign (theHandle.entity); |
262 | return *this; |
263 | } |
264 | |
5d351a08 |
265 | //! Generalized move operator |
266 | template <class T2, typename = typename std::enable_if <is_base_but_not_same <T, T2>::value>::type> |
267 | handle& operator= (handle<T2>&& theHandle) |
268 | { |
269 | std::swap (this->entity, theHandle.entity); |
270 | return *this; |
271 | } |
272 | |
4796758e |
273 | #else |
274 | |
e7195ab4 |
275 | //! Upcast to const reference to base type. |
4796758e |
276 | template <class T2, typename = typename std::enable_if<is_base_but_not_same<T2, T>::value>::type> |
e7195ab4 |
277 | operator const handle<T2>& () const |
278 | { |
279 | return reinterpret_cast<const handle<T2>&>(*this); |
280 | } |
281 | |
282 | //! Upcast to non-const reference to base type. |
d1a67b9d |
283 | //! NB: this cast can be dangerous, but required for legacy code; see #26377 |
4796758e |
284 | template <class T2, typename = typename std::enable_if<is_base_but_not_same<T2, T>::value>::type> |
e7195ab4 |
285 | operator handle<T2>& () |
286 | { |
287 | return reinterpret_cast<handle<T2>&>(*this); |
288 | } |
289 | |
4796758e |
290 | #endif /* OCCT_HANDLE_NOCAST */ |
291 | |
e7195ab4 |
292 | #else /* fallback version for compilers not supporting default arguments of function templates (VC10, VC11, GCC below 4.3) */ |
293 | |
4796758e |
294 | #ifdef OCCT_HANDLE_NOCAST |
295 | |
296 | //! Generalized copy constructor. |
297 | //! Constructs handle holding entity of base type (T) from the one which holds entity of derived type (T2). |
298 | template <class T2> |
299 | handle (const handle<T2>& theHandle, typename std::enable_if <is_base_but_not_same <T, T2>::value>::type* = nullptr) : |
300 | entity(theHandle.entity) |
301 | { |
302 | BeginScope(); |
303 | } |
304 | |
5d351a08 |
305 | //! Generalized move constructor |
306 | template <class T2> |
307 | handle (handle<T2>&& theHandle, typename std::enable_if <is_base_but_not_same <T, T2>::value>::type* = nullptr) |
308 | : entity(theHandle.entity) |
309 | { |
310 | theHandle.entity = 0; |
311 | } |
312 | |
4796758e |
313 | //! Generalized assignment operator. |
314 | template <class T2> |
315 | handle operator = (const handle<T2>& theHandle) |
316 | { |
317 | std::enable_if <is_base_but_not_same <T, T2>::value, void*>::type aTypeCheckHelperVar; |
318 | (void)aTypeCheckHelperVar; |
319 | Assign (theHandle.entity); |
320 | return *this; |
321 | } |
322 | |
5d351a08 |
323 | //! Generalized move operator |
324 | template <class T2> |
325 | handle& operator= (handle<T2>&& theHandle) |
326 | { |
327 | std::enable_if <is_base_but_not_same <T, T2>::value, void*>::type aTypeCheckHelperVar; |
328 | (void)aTypeCheckHelperVar; |
329 | std::swap (this->entity, theHandle.entity); |
330 | return *this; |
331 | } |
332 | |
4796758e |
333 | #else |
334 | |
e7195ab4 |
335 | //! Upcast to const reference to base type. |
336 | //! NB: this implementation will cause ambiguity errors on calls to overloaded |
337 | //! functions accepting handles to different types, since compatibility is |
338 | //! checked in the cast code rather than ensured by SFINAE (possible with C++11) |
339 | template <class T2> |
340 | operator const handle<T2>& () const |
341 | { |
342 | // error "type is not a member of enable_if" will be generated if T2 is not sub-type of T |
343 | // (handle is being cast to const& to handle of non-base type) |
4796758e |
344 | return reinterpret_cast<typename std::enable_if<is_base_but_not_same<T2, T>::value, const handle<T2>&>::type>(*this); |
e7195ab4 |
345 | } |
346 | |
347 | //! Upcast to non-const reference to base type. |
d1a67b9d |
348 | //! NB: this cast can be dangerous, but required for legacy code; see #26377 |
e7195ab4 |
349 | template <class T2> |
aa00364d |
350 | Standard_DEPRECATED("Passing non-const reference to handle of base type in function is unsafe; use variable of exact type") |
e7195ab4 |
351 | operator handle<T2>& () |
352 | { |
353 | // error "type is not a member of enable_if" will be generated if T2 is not sub-type of T |
354 | // (handle is being cast to const& to handle of non-base type) |
4796758e |
355 | return reinterpret_cast<typename std::enable_if<is_base_but_not_same<T2, T>::value, handle<T2>&>::type>(*this); |
e7195ab4 |
356 | } |
357 | |
4796758e |
358 | #endif /* OCCT_HANDLE_NOCAST */ |
359 | |
360 | #endif /* compiler switch */ |
e7195ab4 |
361 | |
362 | private: |
363 | |
364 | //! Assignment |
365 | void Assign (Standard_Transient *thePtr) |
366 | { |
367 | if (thePtr == entity) |
368 | return; |
369 | EndScope(); |
370 | entity = thePtr; |
371 | BeginScope(); |
372 | } |
373 | |
374 | //! Increment reference counter of referred object |
375 | void BeginScope() |
376 | { |
377 | if (entity != 0) |
378 | entity->IncrementRefCounter(); |
379 | } |
380 | |
381 | //! Decrement reference counter and if 0, destroy referred object |
382 | void EndScope() |
383 | { |
384 | if (entity != 0 && entity->DecrementRefCounter() == 0) |
385 | entity->Delete(); |
386 | entity = 0; |
387 | } |
388 | |
389 | template <class T2> friend class handle; |
390 | |
391 | private: |
392 | Standard_Transient* entity; |
393 | }; |
394 | |
395 | } // namespace opencascade |
396 | |
397 | //! Define Handle() macro |
398 | #define Handle(Class) opencascade::handle<Class> |
399 | |
400 | //! Global method HashCode(), for use in hash maps |
401 | template <class T> |
402 | inline Standard_Integer HashCode (const Handle(T)& theHandle, const Standard_Integer theUpper) |
403 | { |
404 | return ::HashCode (const_cast<Standard_Address>(static_cast<const void*>(theHandle.get())), theUpper); |
405 | } |
406 | |
a13f2dc4 |
407 | //! For compatibility with previous versions of OCCT, define Handle_Class alias for opencascade::handle<Class>. |
408 | #if (defined(_MSC_VER) && _MSC_VER >= 1800) |
409 | //! For Visual Studio 2013+, define Handle_Class as non-template class to allow exporting this type in C++/CLI. |
410 | #define DEFINE_STANDARD_HANDLECLASS(C1,C2,BC) class C1; class Handle_##C1 : public Handle(C1) \ |
411 | { \ |
412 | public: \ |
413 | Handle_##C1() {} \ |
414 | Handle_##C1(Handle(C1)&& theHandle) : Handle(C1)(theHandle) {} \ |
415 | template <class T2, typename = typename std::enable_if <std::is_base_of <C1,T2>::value>::type> \ |
416 | inline Handle_##C1(const opencascade::handle<T2>& theOther) : Handle(C1)(theOther) {} \ |
417 | template <class T2, typename = typename std::enable_if <std::is_base_of <C1,T2>::value>::type> \ |
418 | inline Handle_##C1(const T2* theOther) : Handle(C1)(theOther) {} \ |
419 | template<typename T> inline Handle_##C1& operator=(T theOther) { Handle(C1)::operator=(theOther); return *this; } \ |
420 | }; |
421 | #else |
422 | //! For other compilers, use simple typedef |
d1a67b9d |
423 | #define DEFINE_STANDARD_HANDLECLASS(C1,C2,BC) class C1; typedef Handle(C1) Handle_##C1; |
a13f2dc4 |
424 | #endif |
425 | |
e7195ab4 |
426 | #define DEFINE_STANDARD_HANDLE(C1,C2) DEFINE_STANDARD_HANDLECLASS(C1,C2,Standard_Transient) |
427 | #define DEFINE_STANDARD_PHANDLE(C1,C2) DEFINE_STANDARD_HANDLECLASS(C1,C2,Standard_Persistent) |
428 | |
429 | #endif |