Skip to content

Commit bdd14a4

Browse files
committed
Avoid std::is_polymorphic instantiation on incomplete types
Added is_polymorphic_safe to defer evaluation of std::is_polymorphic<T> until T is known to be a complete type. This prevents compilation errors when used with forward-declared types.
1 parent 38f9b42 commit bdd14a4

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

include/behaviortree_cpp/basic_types.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ class TypeInfo
358358

359359
if constexpr(!std::is_same_v<Base, void>)
360360
{
361-
static_assert(std::is_polymorphic_v<Base>, "TypeInfo Base trait specialization "
361+
static_assert(is_polymorphic_safe_v<Base>, "TypeInfo Base trait specialization "
362362
"must be "
363363
"polymorphic");
364364
return TypeInfo{ typeid(std::shared_ptr<Base>),

include/behaviortree_cpp/utils/safe_any.hpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,33 @@ struct is_shared_ptr<std::shared_ptr<U>> : std::true_type
4747
{
4848
};
4949

50+
// Trait to detect if a type is complete
51+
template <typename T, typename = void>
52+
struct is_complete : std::false_type
53+
{
54+
};
55+
56+
template <typename T>
57+
struct is_complete<T, decltype(void(sizeof(T)))> : std::true_type
58+
{
59+
};
60+
61+
// Trait to detect if a trait is complete and polymorphic
62+
template <typename T, typename = void>
63+
struct is_polymorphic_safe : std::false_type
64+
{
65+
};
66+
67+
// Specialization only enabled if T is complete
68+
template <typename T>
69+
struct is_polymorphic_safe<T, std::enable_if_t<is_complete<T>::value>>
70+
: std::integral_constant<bool, std::is_polymorphic<T>::value>
71+
{
72+
};
73+
74+
template <typename T>
75+
inline constexpr bool is_polymorphic_safe_v = is_polymorphic_safe<T>::value;
76+
5077
// Rational: since type erased numbers will always use at least 8 bytes
5178
// it is faster to cast everything to either double, uint64_t or int64_t.
5279
class Any
@@ -83,12 +110,12 @@ class Any
83110
};
84111

85112
template <typename T>
86-
struct IsPolymorphicSharedPtr<T, std::void_t<typename T::element_type>>
87-
: std::integral_constant<
88-
bool,
89-
is_shared_ptr<T>::value && std::is_polymorphic_v<typename T::element_type> &&
90-
!std::is_same_v<typename any_cast_base<typename T::element_type>::type,
91-
void>>
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
92119
{
93120
};
94121

@@ -159,7 +186,7 @@ class Any
159186
// store as base class if specialized
160187
if constexpr(!std::is_same_v<Base, void>)
161188
{
162-
static_assert(std::is_polymorphic_v<Base>, "Any Base trait specialization must be "
189+
static_assert(is_polymorphic_safe_v<Base>, "Any Base trait specialization must be "
163190
"polymorphic");
164191
_any = std::static_pointer_cast<Base>(value);
165192
}
@@ -633,7 +660,7 @@ inline nonstd::expected<T, std::string> Any::tryCast() const
633660
using Derived = typename T::element_type;
634661
using Base = typename any_cast_base<Derived>::type;
635662

636-
if constexpr(std::is_polymorphic_v<Derived> && !std::is_same_v<Base, void>)
663+
if constexpr(is_polymorphic_safe_v<Derived> && !std::is_same_v<Base, void>)
637664
{
638665
// Attempt to retrieve the stored shared_ptr<Base> from the Any container
639666
auto base_ptr = linb::any_cast<std::shared_ptr<Base>>(_any);

0 commit comments

Comments
 (0)