Skip to content

Commit 4a2e42c

Browse files
committed
Support recursive root base resolution for polymorphic shared_ptr types
- Introduced root_base_t<T> to recursively resolve the ultimate base type using the any_cast_base trait. - Applied root_base_t in TypeInfo::Create, Any constructor, castPtr, and tryCast to normalize stored/casted types across polymorphic hierarchies. - Ensured static_assert enforces polymorphic base safety on resolved RootBase.
1 parent 5829a40 commit 4a2e42c

File tree

2 files changed

+64
-12
lines changed

2 files changed

+64
-12
lines changed

include/behaviortree_cpp/basic_types.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,11 +358,14 @@ class TypeInfo
358358

359359
if constexpr(!std::is_same_v<Base, void>)
360360
{
361-
static_assert(is_polymorphic_safe_v<Base>, "TypeInfo Base trait specialization "
362-
"must be "
363-
"polymorphic");
364-
return TypeInfo{ typeid(std::shared_ptr<Base>),
365-
GetAnyFromStringFunctor<std::shared_ptr<Base>>() };
361+
using RootBase = root_base_t<Elem>;
362+
363+
static_assert(is_polymorphic_safe_v<RootBase>, "TypeInfo Base trait "
364+
"specialization "
365+
"must be "
366+
"polymorphic");
367+
return TypeInfo{ typeid(std::shared_ptr<RootBase>),
368+
GetAnyFromStringFunctor<std::shared_ptr<RootBase>>() };
366369
}
367370
}
368371
return TypeInfo{ typeid(T), GetAnyFromStringFunctor<T>() };

include/behaviortree_cpp/utils/safe_any.hpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,48 @@ struct any_cast_base
3636
using type = void; // Default: no base known, fallback to default any storage
3737
};
3838

39+
// C++17 backport of std::type_identity
40+
template <typename T>
41+
struct type_identity
42+
{
43+
using type = T;
44+
};
45+
46+
// Trait to check if a type has a valid cast base
47+
template <typename T, typename = void>
48+
struct has_valid_cast_base : std::false_type
49+
{
50+
};
51+
52+
template <typename T>
53+
struct has_valid_cast_base<T, std::void_t<typename any_cast_base<T>::type>>
54+
{
55+
static constexpr bool value =
56+
!std::is_same<typename any_cast_base<T>::type, void>::value;
57+
};
58+
59+
// Recursive helper (non-self-recursive, SFINAE-safe)
60+
template <typename T>
61+
struct resolve_root_base_helper
62+
{
63+
using Base = typename any_cast_base<T>::type;
64+
65+
using type = typename std::conditional<std::is_same<T, Base>::value, type_identity<T>,
66+
resolve_root_base_helper<Base>>::type::type;
67+
};
68+
69+
// Public interface with guard
70+
template <typename T>
71+
struct root_base_resolver
72+
{
73+
using type = typename std::conditional<has_valid_cast_base<T>::value,
74+
resolve_root_base_helper<T>,
75+
type_identity<T>>::type::type;
76+
};
77+
78+
template <typename T>
79+
using root_base_t = typename root_base_resolver<T>::type;
80+
3981
// Trait to detect std::shared_ptr types.
4082
template <typename T>
4183
struct is_shared_ptr : std::false_type
@@ -160,9 +202,12 @@ class Any
160202
// store as base class if specialized
161203
if constexpr(!std::is_same_v<Base, void>)
162204
{
163-
static_assert(is_polymorphic_safe_v<Base>, "Any Base trait specialization must be "
164-
"polymorphic");
165-
_any = std::static_pointer_cast<Base>(value);
205+
using RootBase = root_base_t<T>;
206+
207+
static_assert(is_polymorphic_safe_v<RootBase>, "Any Base trait specialization must "
208+
"be "
209+
"polymorphic");
210+
_any = std::static_pointer_cast<RootBase>(value);
166211
}
167212
else
168213
{
@@ -264,15 +309,17 @@ class Any
264309

265310
if constexpr(is_polymorphic_safe_v<Derived> && !std::is_same_v<Base, void>)
266311
{
312+
using RootBase = root_base_t<Derived>;
313+
267314
try
268315
{
269316
// Attempt to retrieve the stored shared_ptr<Base> from the Any container
270-
auto base_ptr = linb::any_cast<std::shared_ptr<Base>>(&_any);
317+
auto base_ptr = linb::any_cast<std::shared_ptr<RootBase>>(&_any);
271318
if(!base_ptr)
272319
return nullptr;
273320

274321
// Case 1: If Base and Derived are the same, no casting is needed
275-
if constexpr(std::is_same_v<Base, Derived>)
322+
if constexpr(std::is_same_v<RootBase, Derived>)
276323
{
277324
return reinterpret_cast<T*>(base_ptr);
278325
}
@@ -636,15 +683,17 @@ inline nonstd::expected<T, std::string> Any::tryCast() const
636683

637684
if constexpr(is_polymorphic_safe_v<Derived> && !std::is_same_v<Base, void>)
638685
{
686+
using RootBase = root_base_t<Derived>;
687+
639688
// Attempt to retrieve the stored shared_ptr<Base> from the Any container
640-
auto base_ptr = linb::any_cast<std::shared_ptr<Base>>(_any);
689+
auto base_ptr = linb::any_cast<std::shared_ptr<RootBase>>(_any);
641690
if(!base_ptr)
642691
{
643692
throw std::runtime_error("Any::cast cannot cast to shared_ptr<Base> class");
644693
}
645694

646695
// Case 1: If Base and Derived are the same, no casting is needed
647-
if constexpr(std::is_same_v<T, std::shared_ptr<Base>>)
696+
if constexpr(std::is_same_v<T, std::shared_ptr<RootBase>>)
648697
{
649698
return base_ptr;
650699
}

0 commit comments

Comments
 (0)