Skip to content

Commit d11512a

Browse files
committed
Refactor castPtr() to unify shared_ptr casting logic
Introduced _cached_derived_ptr to temporarily store downcasted shared_ptr results which are polymorphic and base-registered.
1 parent bdd14a4 commit d11512a

File tree

1 file changed

+45
-71
lines changed

1 file changed

+45
-71
lines changed

include/behaviortree_cpp/utils/safe_any.hpp

Lines changed: 45 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -101,32 +101,6 @@ class Any
101101
typename std::enable_if<!std::is_arithmetic<T>::value && !std::is_enum<T>::value &&
102102
!std::is_same<T, std::string>::value>::type*;
103103

104-
// Helper: IsPolymorphicSharedPtr<T> is true if T is a shared pointer,
105-
// its element_type exists, is polymorphic, and any_cast_base for that element
106-
// is specialized (i.e. not void).
107-
template <typename T, typename = void>
108-
struct IsPolymorphicSharedPtr : std::false_type
109-
{
110-
};
111-
112-
template <typename T>
113-
struct IsPolymorphicSharedPtr<
114-
T,
115-
std::enable_if_t<
116-
is_shared_ptr<T>::value && is_polymorphic_safe_v<typename T::element_type> &&
117-
!std::is_same_v<typename any_cast_base<typename T::element_type>::type, void>>>
118-
: std::true_type
119-
{
120-
};
121-
122-
template <typename T>
123-
using EnablePolymorphicSharedPtr =
124-
std::enable_if_t<IsPolymorphicSharedPtr<T>::value, int*>;
125-
126-
template <typename T>
127-
using EnableNonPolymorphicSharedPtr =
128-
std::enable_if_t<!IsPolymorphicSharedPtr<T>::value, int*>;
129-
130104
template <typename T>
131105
nonstd::expected<T, std::string> stringToNumber() const;
132106

@@ -247,7 +221,10 @@ class Any
247221
// Method to access the value by pointer.
248222
// It will return nullptr, if the user try to cast it to a
249223
// wrong type or if Any was empty.
250-
template <typename T, typename = EnableNonPolymorphicSharedPtr<T>>
224+
//
225+
// WARNING: The returned pointer may alias internal cache and be invalidated by subsequent castPtr() calls.
226+
// Do not store it long-term. Applies only to shared_ptr<Derived> where Derived is polymorphic and base-registered.
227+
template <typename T>
251228
[[nodiscard]] T* castPtr()
252229
{
253230
static_assert(!std::is_same_v<T, float>, "The value has been casted internally to "
@@ -278,57 +255,53 @@ class Any
278255
"tea"
279256
"d");
280257

281-
return _any.empty() ? nullptr : linb::any_cast<T>(&_any);
282-
}
283-
284-
// Specialized version of castPtr() for shared_ptr<T> where T is a polymorphic type
285-
// with a registered base class via any_cast_base.
286-
//
287-
// Returns a raw pointer to T::element_type (i.e., Derived*), or nullptr on failure.
288-
//
289-
// Note: This function intentionally does not return a std::shared_ptr<T>* because doing so
290-
// would expose the internal ownership mechanism, which:
291-
// - Breaks encapsulation and may lead to accidental misuse (e.g., double-deletion, ref count tampering)
292-
// - Offers no real benefit, as the purpose of this function is to provide access
293-
// to the managed object, not the smart pointer itself.
294-
//
295-
// By returning a raw pointer to the object, we preserve ownership semantics and safely
296-
// allow read-only access without affecting the reference count.
297-
template <typename T, typename = EnablePolymorphicSharedPtr<T>>
298-
[[nodiscard]] typename T::element_type* castPtr()
299-
{
300-
using Derived = typename T::element_type;
301-
using Base = typename any_cast_base<Derived>::type;
302-
303-
try
258+
// Special case: applies only when requesting shared_ptr<Derived> and Derived is polymorphic
259+
// with a registered base via any_cast_base.
260+
if constexpr(is_shared_ptr<T>::value)
304261
{
305-
// Attempt to retrieve the stored shared_ptr<Base> from the Any container
306-
auto base_ptr = linb::any_cast<std::shared_ptr<Base>>(&_any);
307-
if(!base_ptr)
308-
return nullptr;
262+
using Derived = typename T::element_type;
263+
using Base = typename any_cast_base<Derived>::type;
309264

310-
// Case 1: If Base and Derived are the same, no casting is needed
311-
if constexpr(std::is_same_v<Base, Derived>)
265+
if constexpr(is_polymorphic_safe_v<Derived> && !std::is_same_v<Base, void>)
312266
{
313-
return base_ptr ? base_ptr->get() : nullptr;
314-
}
267+
try
268+
{
269+
// Attempt to retrieve the stored shared_ptr<Base> from the Any container
270+
auto base_ptr = linb::any_cast<std::shared_ptr<Base>>(&_any);
271+
if(!base_ptr)
272+
return nullptr;
273+
274+
// Case 1: If Base and Derived are the same, no casting is needed
275+
if constexpr(std::is_same_v<Base, Derived>)
276+
{
277+
return reinterpret_cast<T*>(base_ptr);
278+
}
279+
280+
// Case 2: Originally stored as shared_ptr<Derived>
281+
if(_original_type == typeid(std::shared_ptr<Derived>))
282+
{
283+
_cached_derived_ptr = std::static_pointer_cast<Derived>(*base_ptr);
284+
return reinterpret_cast<T*>(&_cached_derived_ptr);
285+
}
286+
287+
// Case 3: Fallback to dynamic cast
288+
auto derived_ptr = std::dynamic_pointer_cast<Derived>(*base_ptr);
289+
if(derived_ptr)
290+
{
291+
_cached_derived_ptr = derived_ptr;
292+
return reinterpret_cast<T*>(&_cached_derived_ptr);
293+
}
294+
}
295+
catch(...)
296+
{
297+
return nullptr;
298+
}
315299

316-
// Case 2: If the original stored type was shared_ptr<Derived>, we can safely static_cast
317-
if(_original_type == typeid(std::shared_ptr<Derived>))
318-
{
319-
return std::static_pointer_cast<Derived>(*base_ptr).get();
300+
return nullptr;
320301
}
321-
322-
// Case 3: Otherwise, attempt a dynamic cast from Base to Derived
323-
auto derived_ptr = std::dynamic_pointer_cast<Derived>(*base_ptr);
324-
return derived_ptr ? derived_ptr.get() : nullptr;
325-
}
326-
catch(...)
327-
{
328-
return nullptr;
329302
}
330303

331-
return nullptr;
304+
return _any.empty() ? nullptr : linb::any_cast<T>(&_any);
332305
}
333306

334307
// This is the original type
@@ -351,6 +324,7 @@ class Any
351324
private:
352325
linb::any _any;
353326
std::type_index _original_type;
327+
mutable std::shared_ptr<void> _cached_derived_ptr = nullptr;
354328

355329
//----------------------------
356330

0 commit comments

Comments
 (0)