Skip to content

Commit fe5491b

Browse files
authored
Merge pull request #5 from cschreib/pointer_casts
Add pointer casts
2 parents a1c175e + 6efeca0 commit fe5491b

File tree

4 files changed

+948
-10
lines changed

4 files changed

+948
-10
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
build/
2+
build_clang/
3+
doc/html/

include/oup/observable_unique_ptr.hpp

+205-10
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,11 @@ class observable_unique_ptr_base {
258258
*/
259259
template<typename U, typename D>
260260
observable_unique_ptr_base(observable_unique_ptr_base<U,D>&& manager, T* value) noexcept :
261-
observable_unique_ptr_base(manager.block, value) {
261+
observable_unique_ptr_base(value != nullptr ? manager.block : nullptr, value) {
262+
if (manager.ptr_deleter.data != nullptr && value == nullptr) {
263+
manager.delete_and_pop_ref_();
264+
}
265+
262266
manager.block = nullptr;
263267
manager.ptr_deleter.data = nullptr;
264268
}
@@ -272,7 +276,11 @@ class observable_unique_ptr_base {
272276
*/
273277
template<typename U, typename D>
274278
observable_unique_ptr_base(observable_unique_ptr_base<U,D>&& manager, T* value, Deleter del) noexcept :
275-
observable_unique_ptr_base(manager.block, value, std::move(del)) {
279+
observable_unique_ptr_base(value != nullptr ? manager.block : nullptr, value, std::move(del)) {
280+
if (manager.ptr_deleter.data != nullptr && value == nullptr) {
281+
manager.delete_and_pop_ref_();
282+
}
283+
276284
manager.block = nullptr;
277285
manager.ptr_deleter.data = nullptr;
278286
}
@@ -520,7 +528,10 @@ class observable_unique_ptr :
520528
* \param value The casted pointer value to take ownership of
521529
* \note After this observable_unique_ptr is created, the source
522530
* pointer is set to null and looses ownership. The deleter
523-
* is default constructed.
531+
* is default constructed. The raw pointer `value`
532+
* must be obtained by casting the raw pointer managed by `manager`
533+
* (const cast, dynamic cast, etc), such that deleting `value` has
534+
* the same effect as deleting the pointer owned by `manager`.
524535
*/
525536
template<typename U, typename D, typename V, typename enable =
526537
std::enable_if_t<std::is_convertible_v<V*,T*>>>
@@ -534,7 +545,10 @@ class observable_unique_ptr :
534545
* \param value The casted pointer value to take ownership of
535546
* \param del The deleter to use in the new pointer
536547
* \note After this observable_unique_ptr is created, the source
537-
* pointer is set to null and looses ownership.
548+
* pointer is set to null and looses ownership. The raw pointer `value`
549+
* must be obtained by casting the raw pointer managed by `manager`
550+
* (const cast, dynamic cast, etc), such that deleting `value` has
551+
* the same effect as deleting the pointer owned by `manager`.
538552
*/
539553
template<typename U, typename D, typename V, typename enable =
540554
std::enable_if_t<std::is_convertible_v<V*,T*>>>
@@ -728,10 +742,14 @@ class observable_sealed_ptr :
728742
/** \param manager The smart pointer to take ownership from
729743
* \param value The casted pointer value to take ownership of
730744
* \note After this `observable_sealed_ptr` is created, the source
731-
* pointer is set to null and looses ownership.
745+
* pointer is set to null and looses ownership. The raw pointer `value`
746+
* must be obtained by casting the raw pointer managed by `manager`
747+
* (const cast, dynamic cast, etc), such that deleting `value` has
748+
* the same effect as deleting the pointer owned by `manager`.
732749
*/
733-
template<typename U>
734-
observable_sealed_ptr(observable_sealed_ptr<U>&& manager, T* value) noexcept :
750+
template<typename U, typename V, typename enable =
751+
std::enable_if_t<std::is_convertible_v<V*,T*>>>
752+
observable_sealed_ptr(observable_sealed_ptr<U>&& manager, V* value) noexcept :
735753
base(std::move(manager), value) {}
736754

737755
/// Transfer ownership by implicit casting
@@ -996,6 +1014,24 @@ class observer_ptr {
9961014
}
9971015
}
9981016

1017+
/// Copy an existing `observer_ptr` instance with explicit casting
1018+
/** \param manager The observer pointer to copy the observed data from
1019+
* \param value The casted pointer value to observe
1020+
* \note After this smart pointer is created, the source
1021+
* pointer is set to null and looses ownership. The deleter
1022+
* is default constructed. The raw pointer `value` may or may
1023+
* not be related to the raw pointer observed by `manager`.
1024+
* This could be a pointer to any other object which is known to
1025+
* have the same lifetime.
1026+
*/
1027+
template<typename U>
1028+
observer_ptr(const observer_ptr<U>& manager, T* value) noexcept :
1029+
block(value != nullptr ? manager.block : nullptr), data(value) {
1030+
if (block) {
1031+
++block->refcount;
1032+
}
1033+
}
1034+
9991035
/// Move from an existing `observer_ptr` instance
10001036
/** \param value The existing observer pointer to move from
10011037
* \note After this `observer_ptr` is created, the source
@@ -1018,6 +1054,27 @@ class observer_ptr {
10181054
value.data = nullptr;
10191055
}
10201056

1057+
/// Move from an existing `observer_ptr` instance with explicit casting
1058+
/** \param manager The observer pointer to copy the observed data from
1059+
* \param value The casted pointer value to observe
1060+
* \note After this smart pointer is created, the source
1061+
* pointer is set to null and looses ownership. The deleter
1062+
* is default constructed. The raw pointer `value` may or may
1063+
* not be related to the raw pointer observed by `manager`.
1064+
* This could be a pointer to any other object which is known to
1065+
* have the same lifetime.
1066+
*/
1067+
template<typename U>
1068+
observer_ptr(observer_ptr<U>&& manager, T* value) noexcept :
1069+
block(value != nullptr ? manager.block : nullptr), data(value) {
1070+
if (manager.data != nullptr && value == nullptr) {
1071+
manager.pop_ref_();
1072+
}
1073+
1074+
manager.block = nullptr;
1075+
manager.data = nullptr;
1076+
}
1077+
10211078
/// Point to another owning pointer.
10221079
/** \param owner The new owner pointer to observe
10231080
* \note This operator only takes part in overload resolution if
@@ -1320,9 +1377,6 @@ class enable_observer_from_this : public virtual details::enable_observer_from_t
13201377
};
13211378

13221379
public:
1323-
1324-
using observer_element_type = T;
1325-
13261380
/// Return an observer pointer to 'this'.
13271381
/** \return A new observer pointer pointing to 'this'.
13281382
* \note If 'this' is not owned by a unique or sealed pointer, i.e., if
@@ -1352,6 +1406,147 @@ class enable_observer_from_this : public virtual details::enable_observer_from_t
13521406
}
13531407
};
13541408

1409+
/// Perform a `static_cast` for an `observable_unique_ptr`.
1410+
/** \param ptr The pointer to cast
1411+
* \note Ownership will be transfered to the returned pointer.
1412+
If the input pointer is null, the output pointer will also be null.
1413+
*/
1414+
template<typename U, typename T>
1415+
observable_unique_ptr<U> static_pointer_cast(observable_unique_ptr<T>&& ptr) {
1416+
return observable_unique_ptr<U>(std::move(ptr), static_cast<U*>(ptr.get()));
1417+
}
1418+
1419+
/// Perform a `static_cast` for an `observable_unique_ptr`.
1420+
/** \param ptr The pointer to cast
1421+
* \note Ownership will be transfered to the returned pointer.
1422+
If the input pointer is null, the output pointer will also be null.
1423+
*/
1424+
template<typename U, typename T>
1425+
observable_sealed_ptr<U> static_pointer_cast(observable_sealed_ptr<T>&& ptr) {
1426+
return observable_sealed_ptr<U>(std::move(ptr), static_cast<U*>(ptr.get()));
1427+
}
1428+
1429+
/// Perform a `static_cast` for an `observer_ptr`.
1430+
/** \param ptr The pointer to cast
1431+
* \note A new observer is returned, the input observer is not modified.
1432+
If the input pointer is null, the output pointer will also be null.
1433+
*/
1434+
template<typename U, typename T>
1435+
observer_ptr<U> static_pointer_cast(const observer_ptr<T>& ptr) {
1436+
// NB: can use raw_get() as static cast of an expired pointer is fine
1437+
return observer_ptr<U>(ptr, static_cast<U*>(ptr.raw_get()));
1438+
}
1439+
1440+
/// Perform a `static_cast` for an `observer_ptr`.
1441+
/** \param ptr The pointer to cast
1442+
* \note A new observer is returned, the input observer is set to null.
1443+
If the input pointer is null, the output pointer will also be null.
1444+
*/
1445+
template<typename U, typename T>
1446+
observer_ptr<U> static_pointer_cast(observer_ptr<T>&& ptr) {
1447+
// NB: can use raw_get() as static cast of an expired pointer is fine
1448+
return observer_ptr<U>(std::move(ptr), static_cast<U*>(ptr.raw_get()));
1449+
}
1450+
1451+
/// Perform a `const_cast` for an `observable_unique_ptr`.
1452+
/** \param ptr The pointer to cast
1453+
* \note Ownership will be transfered to the returned pointer.
1454+
If the input pointer is null, the output pointer will also be null.
1455+
*/
1456+
template<typename U, typename T>
1457+
observable_unique_ptr<U> const_pointer_cast(observable_unique_ptr<T>&& ptr) {
1458+
return observable_unique_ptr<U>(std::move(ptr), const_cast<U*>(ptr.get()));
1459+
}
1460+
1461+
/// Perform a `const_cast` for an `observable_unique_ptr`.
1462+
/** \param ptr The pointer to cast
1463+
* \note Ownership will be transfered to the returned pointer.
1464+
If the input pointer is null, the output pointer will also be null.
1465+
*/
1466+
template<typename U, typename T>
1467+
observable_sealed_ptr<U> const_pointer_cast(observable_sealed_ptr<T>&& ptr) {
1468+
return observable_sealed_ptr<U>(std::move(ptr), const_cast<U*>(ptr.get()));
1469+
}
1470+
1471+
/// Perform a `const_cast` for an `observer_ptr`.
1472+
/** \param ptr The pointer to cast
1473+
* \note A new observer is returned, the input observer is not modified.
1474+
If the input pointer is null, the output pointer will also be null.
1475+
*/
1476+
template<typename U, typename T>
1477+
observer_ptr<U> const_pointer_cast(const observer_ptr<T>& ptr) {
1478+
// NB: can use raw_get() as const cast of an expired pointer is fine
1479+
return observer_ptr<U>(ptr, const_cast<U*>(ptr.raw_get()));
1480+
}
1481+
1482+
/// Perform a `const_cast` for an `observer_ptr`.
1483+
/** \param ptr The pointer to cast
1484+
* \note A new observer is returned, the input observer is set to null.
1485+
If the input pointer is null, the output pointer will also be null.
1486+
*/
1487+
template<typename U, typename T>
1488+
observer_ptr<U> const_pointer_cast(observer_ptr<T>&& ptr) {
1489+
// NB: can use raw_get() as const cast of an expired pointer is fine
1490+
return observer_ptr<U>(std::move(ptr), const_cast<U*>(ptr.raw_get()));
1491+
}
1492+
1493+
/// Perform a `dynamic_cast` for an `observable_unique_ptr`.
1494+
/** \param ptr The pointer to cast
1495+
* \note Ownership will be transfered to the returned pointer unless the cast
1496+
* fails, in which case ownership remains in the original pointer, std::bad_cast
1497+
* is thrown, and no memory is leaked. If the input pointer is null,
1498+
* the output pointer will also be null.
1499+
*/
1500+
template<typename U, typename T>
1501+
observable_unique_ptr<U> dynamic_pointer_cast(observable_unique_ptr<T>&& ptr) {
1502+
if (ptr == nullptr) {
1503+
return observable_unique_ptr<U>{};
1504+
}
1505+
1506+
U& casted_object = dynamic_cast<U&>(*ptr.get());
1507+
return observable_unique_ptr<U>(std::move(ptr), &casted_object);
1508+
}
1509+
1510+
/// Perform a `dynamic_cast` for an `observable_unique_ptr`.
1511+
/** \param ptr The pointer to cast
1512+
* \note Ownership will be transfered to the returned pointer unless the cast
1513+
* fails, in which case ownership remains in the original pointer, and
1514+
* no memory is leaked.
1515+
*/
1516+
template<typename U, typename T>
1517+
observable_sealed_ptr<U> dynamic_pointer_cast(observable_sealed_ptr<T>&& ptr) {
1518+
if (ptr == nullptr) {
1519+
return observable_sealed_ptr<U>{};
1520+
}
1521+
1522+
U& casted_object = dynamic_cast<U&>(*ptr.get());
1523+
return observable_sealed_ptr<U>(std::move(ptr), &casted_object);
1524+
}
1525+
1526+
/// Perform a `dynamic_cast` for an `observer_ptr`.
1527+
/** \param ptr The pointer to cast
1528+
* \note A new observer is returned, the input observer is not modified.
1529+
If the input pointer is null, or if the cast fails, the output pointer
1530+
will be null.
1531+
*/
1532+
template<typename U, typename T>
1533+
observer_ptr<U> dynamic_pointer_cast(const observer_ptr<T>& ptr) {
1534+
// NB: must use get() as dynamic cast of an expired pointer is UB
1535+
return observer_ptr<U>(ptr, dynamic_cast<U*>(ptr.get()));
1536+
}
1537+
1538+
/// Perform a `dynamic_cast` for an `observer_ptr`.
1539+
/** \param ptr The pointer to cast
1540+
* \note A new observer is returned, the input observer is set to null.
1541+
If the input pointer is null, or if the cast fails, the output pointer
1542+
will be null.
1543+
*/
1544+
template<typename U, typename T>
1545+
observer_ptr<U> dynamic_pointer_cast(observer_ptr<T>&& ptr) {
1546+
// NB: must use get() as dynamic cast of an expired pointer is UB
1547+
return observer_ptr<U>(std::move(ptr), dynamic_cast<U*>(ptr.get()));
1548+
}
1549+
13551550
}
13561551

13571552
#endif

0 commit comments

Comments
 (0)