@@ -101,32 +101,6 @@ class Any
101
101
typename std::enable_if<!std::is_arithmetic<T>::value && !std::is_enum<T>::value &&
102
102
!std::is_same<T, std::string>::value>::type*;
103
103
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
-
130
104
template <typename T>
131
105
nonstd::expected<T, std::string> stringToNumber () const ;
132
106
@@ -247,7 +221,10 @@ class Any
247
221
// Method to access the value by pointer.
248
222
// It will return nullptr, if the user try to cast it to a
249
223
// 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>
251
228
[[nodiscard]] T* castPtr ()
252
229
{
253
230
static_assert (!std::is_same_v<T, float >, " The value has been casted internally to "
@@ -278,57 +255,53 @@ class Any
278
255
" tea"
279
256
" d" );
280
257
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)
304
261
{
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;
309
264
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 >)
312
266
{
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
+ }
315
299
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 ;
320
301
}
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 ;
329
302
}
330
303
331
- return nullptr ;
304
+ return _any. empty () ? nullptr : linb::any_cast<T>(&_any) ;
332
305
}
333
306
334
307
// This is the original type
@@ -351,6 +324,7 @@ class Any
351
324
private:
352
325
linb::any _any;
353
326
std::type_index _original_type;
327
+ mutable std::shared_ptr<void > _cached_derived_ptr = nullptr ;
354
328
355
329
// ----------------------------
356
330
0 commit comments