Last time, we tried to add non-modifying trackers to our tracking pointers implementation. I noted at the end that our attempt was wrong.
The problem is in the code we didn’t change:
void set_target(T* p) noexcept
{
for (tracking_node* n = m_trackers.next;
n != &m_trackers; n = n->next) {
static_cast<tracking_ptr<T>*>(n)->tracked = p;
}
}
The static_cast is a downcast from a tracking_node to its derived tracking_ptr<T>. But the derived class might not be tracking_ptr<T>! It could be a tracking_ptr<const T>.
To fix this, we need to use a consistent type for the derived class. We can do this by renaming our original tracking_ptr to tracking_ptr_base, which will serve as the consistent derived class, and then move the get() method to a tracking_ptr that is derived from tracking_ptr_base.
template<typename T> struct tracking_ptr_base : private tracking_node { // T* get() const { return tracked; }
tracking_ptr_base() noexcept :
tracking_node(as_solo{}),
tracked(nullptr) {}
tracking_ptr_base(tracking_ptr_base const& other) noexcept :
tracking_node(copy_node(other)),
tracked(other.tracked) { }
~tracking_ptr_base() = default;
tracking_ptr_base& operator=(tracking_ptr_base const& other) noexcept {
tracked = other.tracked;
if (tracked) {
join(trackers(tracked));
} else {
disconnect();
}
return *this;
}
tracking_ptr_base& operator=(tracking_ptr_base&& other) noexcept {
tracked = std::exchange(other.tracked, nullptr);
tracking_node::displace(other);
return *this;
}
private: friend struct trackable_object<T>;
static tracking_node& trackers(T* p) noexcept {
return p->trackable_object<T>::m_trackers;
}
tracking_node copy_node(tracking_ptr_base const& other) noexcept
{
if (other.tracked) {
return tracking_node(as_join{},
trackers(other.tracked));
} else {
return tracking_node(as_solo{});
}
}
tracking_ptr_base(T* p) noexcept :
tracking_node(as_join{}, trackers(p)),
tracked(p) { }
protected: T* tracked; };
template<typename T>
struct tracking_ptr : tracking_ptr_base<std::remove_cv_t<T>>
{
public:
T* get() const { return this->tracked; }
using tracking_ptr::tracking_ptr_base::
tracking_ptr_base;
};
template<typename T> struct trackable_object { ⟦ … ⟧
private: friend struct tracking_ptr_base<T>; // friend struct tracking_ptr<const T>;
⟦ ... ⟧
void set_target(T* p)
{
for (tracking_node* n = m_trackers.next;
n != &m_trackers; n = n->next) {
static_cast<tracking_ptr_base<T>*>(n)->
tracked = p;
}
}
};
Okay, now we can have an object give away a non-modifying tracking pointer to itself by using ctrack() instead of track().
But wait, this still requires that the original object be itself mutable. But if all you have is a const reference to a trackable object, surely you should be allowed to create a non-modifying tracking pointer to it, right?
We’ll do that next time.
The post Thoughts on creating a tracking pointer class, part 7: Non-modifying trackers, second try appeared first on The Old New Thing.
From The Old New Thing via this RSS feed