@@ -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
351324private:
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