Skip to content

Commit 8564a07

Browse files
committed
Fix slicing from "Array" to "ModelNode"
1 parent 6976c70 commit 8564a07

1 file changed

Lines changed: 42 additions & 4 deletions

File tree

include/simfil/model/nodes.h

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,16 @@ struct model_ptr
110110
model_ptr& operator=(model_ptr&&) = default;
111111

112112
template<typename OtherT>
113-
model_ptr(model_ptr<OtherT> const& other) : data_(other.data_) {} // NOLINT
113+
model_ptr(model_ptr<OtherT> const& other) : data_(makeCompatibleCopy(other)) {} // NOLINT
114114

115115
template<typename OtherT>
116-
model_ptr(model_ptr<OtherT>&& other) : data_(std::move(other.data_)) {} // NOLINT
116+
model_ptr(model_ptr<OtherT>&& other) : data_(makeCompatibleMove(std::move(other))) {} // NOLINT
117117

118118
template<typename OtherT>
119-
model_ptr& operator= (model_ptr<OtherT> const& other) {data_ = other.data_; return *this;}
119+
model_ptr& operator= (model_ptr<OtherT> const& other) {data_ = makeCompatibleCopy(other); return *this;}
120120

121121
template<typename OtherT>
122-
model_ptr& operator= (model_ptr<OtherT>&& other) {data_ = std::move(other.data_); return *this;}
122+
model_ptr& operator= (model_ptr<OtherT>&& other) {data_ = makeCompatibleMove(std::move(other)); return *this;}
123123

124124
template<typename... Args>
125125
explicit model_ptr(std::in_place_t, Args&&... args)
@@ -156,6 +156,44 @@ struct model_ptr
156156
inline operator bool () const {return data_.addr_;} // NOLINT (allow implicit bool cast)
157157

158158
private:
159+
template<typename>
160+
static constexpr bool AlwaysFalse = false;
161+
162+
template<typename OtherT>
163+
[[nodiscard]] static T makeCompatibleCopy(model_ptr<OtherT> const& other)
164+
{
165+
if constexpr (std::is_base_of_v<T, OtherT> && !std::is_same_v<T, OtherT>) {
166+
// Avoid slicing on derived->base conversions by rebuilding from node identity.
167+
if constexpr (requires { T(other.data_.model_, other.data_.addr_, other.data_.data_, detail::mp_key{0}); }) {
168+
return T(other.data_.model_, other.data_.addr_, other.data_.data_, detail::mp_key{0});
169+
} else if constexpr (requires { T(other.data_.model_, other.data_.addr_, detail::mp_key{0}); }) {
170+
return T(other.data_.model_, other.data_.addr_, detail::mp_key{0});
171+
} else {
172+
static_assert(
173+
AlwaysFalse<OtherT>,
174+
"Derived-to-base model_ptr conversion requires a constructor from model/address.");
175+
}
176+
}
177+
return T(other.data_);
178+
}
179+
180+
template<typename OtherT>
181+
[[nodiscard]] static T makeCompatibleMove(model_ptr<OtherT>&& other)
182+
{
183+
if constexpr (std::is_base_of_v<T, OtherT> && !std::is_same_v<T, OtherT>) {
184+
if constexpr (requires { T(std::move(other.data_.model_), other.data_.addr_, std::move(other.data_.data_), detail::mp_key{0}); }) {
185+
return T(std::move(other.data_.model_), other.data_.addr_, std::move(other.data_.data_), detail::mp_key{0});
186+
} else if constexpr (requires { T(std::move(other.data_.model_), other.data_.addr_, detail::mp_key{0}); }) {
187+
return T(std::move(other.data_.model_), other.data_.addr_, detail::mp_key{0});
188+
} else {
189+
static_assert(
190+
AlwaysFalse<OtherT>,
191+
"Derived-to-base model_ptr conversion requires a constructor from model/address.");
192+
}
193+
}
194+
return T(std::move(other.data_));
195+
}
196+
159197
T data_;
160198
};
161199

0 commit comments

Comments
 (0)