# A nonstandard “shared pointer” of sorts

In a few of my projects, I had sort of a common situation, where I wanted to share a pointer to some object, and I want the recipient to be able to check if it is still valid, but I don't want them to "share ownership" of the object that is pointed to.

To make it concrete, suppose I have some service class that does something useful. And, I want to make a gui interface to it. That means, I'm going to use some existing gui framework, and pass delegates to it that call various members of this service object. I didn't write the gui framework, it has it's own management system and I don't necessarily know exactly when it's going to delete those delegates. I'm not thrilled about a bunch of dead pointers floating around inside of it.

Now you might say, oh, just use a std::shared_ptr or std::weak_ptr, and bind your delegates to that. However, my service class is not owned by a std::shared_ptr and I don't necessarily want to commit to that. I don't really want the GUI to be able to take ownership of the service, that seems wrong. I want to know exactly what the lifetime of the service is, and all of its resources.

The quick and dirty thing that I usually did was, if the service is of type T, then the service owns a std::shared_ptr<T*> which it initializes with this. Then std::weak_ptr are produced from that, and the delegates use that. Because expressions like if (auto l = ptr_.lock()) { something(**l); ... } are ugly, I put this behind some common interface, where .lock() returns a T* which is nullptr if we can no longer access the object.

Because I had a few classes like this in my projects, I decided to extract and make a common implementation. I also decided that, it should not be based on std::shared_ptr, since it has overhead for thread-safety and having multiple owners. If .lock() returns a T* then it can never be thread-safe, but these apps I worked on weren't passing these things between threads anyways.

I tried to make a familiar interface and decently optimize the result. I guess my plan now is to put the new version into my other projects, and also open source it and put it on my github. But I would appreciate some code review first :)

//  (C) Copyright 2015 - 2016 Christopher Beck

#ifndef NONSTD_WEAK_REF_HPP_INCLUDED
#define NONSTD_WEAK_REF_HPP_INCLUDED

/***
* Synopsis:
* unique_ref and weak_ref are a pair of "smart pointer-like" objects.
*
* - unique_ref is constructed from a reference to an object (or, default
constructed.)
* - weak_ref can be constructed from unique_ref.
* - They do not manage the lifetime of the thing they are referring to,
instead, the unique_ref merely manages the validity of the weak_ref's,
making it easier to manage the possibility of dangling pointers. (But not
100% eliminating it.)
* - On the plus side, it has significantly less overhead, and can be used
with any object, even a stack-allocated one.
* - "weak_ref::lock" returns a raw pointer rather than a smart pointer, since
it is not locking in the sense of taking ownership. It's merely returning
a validity-checked pointer -- if it's not nullptr, then it is safe to
dereference, unless the "unique_ref" itself is dangling (easier to manage.)
* - They are not thread safe, they should not be passed across threads. If you
need that then you should use std::shared_ptr instead.
* - The interface mimics std::weak_ptr.
* - There is no possibility of leaks due to "cyclic references". The only object
whose lifetime is managed by reference counting here, is a shared control
structure, whose destructor is trivial.
*/

namespace nonstd {

namespace detail {

template <typename T>
struct weak_ref_control_structure {
mutable long ref_count_;

explicit weak_ref_control_structure(T * t) : payload_(t), ref_count_(0) {}
};

} // end namespace detail

// Forward declare weak_ref
template <typename T>
class weak_ref;

// unique_ref: Owner of control structure
template <typename T>
class unique_ref {
using ctrl_t = detail::weak_ref_control_structure<T>;

ctrl_t * ptr_;

void init(T & t) { ptr_ = new ctrl_t(&t); }

void move(unique_ref & o) {
ptr_ = o.ptr_;
o.ptr_ = nullptr;
}

// Invariant: If ptr_ is not null, it points to a ctrl_t that no other
// unique_rf points to, and ptr_->payload_ is also not null.

friend class weak_ref<T>;

public:
// Special member functions
unique_ref() : ptr_(nullptr) {}
unique_ref(T & t) { this->init(t); }

~unique_ref() { this->reset(); }

unique_ref(unique_ref && other) { this->move(other); }

unique_ref & operator = (unique_ref && other) {
this->move(other);
return *this;
}

// Copy ctor: Make a new ctrl structure pointing to the same payload
unique_ref(const unique_ref & other) {
if (other.ptr_) {
} else {
ptr_ = nullptr;
}
}

// Copy assignment: Copy and swap
unique_ref & operator = (const unique_ref & other) {
unique_ref temp{other};
this->swap(temp);
return *this;
}

// Reset (release managed object)
void reset() {
if (ptr_) {
if (!ptr_->ref_count_) {
delete ptr_;
}
ptr_ = nullptr;
}
}

// Swap
void swap(unique_ref & other) {
ctrl_t * temp = ptr_;
ptr_ = other.ptr_;
other.ptr_ = temp;
}

// Observers
// Get the managed pointer
T * get() const {
if (ptr_) { return ptr_->payload_; }
return nullptr;
}

// Operator *: Blindly dereference the getted pointer, without a null check.
T & operator *() const {
return *this->get();
}

// Operator bool: check if there is a managed object
explicit operator bool() const { return ptr_; }

// use_count: mimic std::shared_ptr interface
long use_count() const {
return ptr_ ? 1 : 0;
}

// unique: mimic std::shared_ptr interface
bool unique() const {
return this->use_count() == 1;
}

// weak_ref_count: do something more useful :)
long weak_ref_count() const {
if (ptr_) { return ptr_->ref_count_; }
return 0;
}
};

template <typename T>
class weak_ref {
using ctrl_t = detail::weak_ref_control_structure<T>;

// Rationale: When we lock the weak_ref, if the ref has expired, we want to
// release this immediately, and set to nullptr, so that future lookups are
// faster. Since the caller is going to test the pointer we return anyways,
// this should be a cheap operation in an optimized build.
mutable const ctrl_t * ptr_;

void init(const ctrl_t * c) {
if (c) {
++(c->ref_count_);
}
ptr_ = c;
}

void move(weak_ref & o) {
ptr_ = o.ptr_;
o.ptr_ = nullptr;
}

void release() const {
if (ptr_) {
if (!--(ptr_->ref_count_)) {
delete ptr_;
}
ptr_ = nullptr;
}
}

public:
// Special member functions
weak_ref() : ptr_(nullptr) {}
weak_ref(const unique_ref<T> & u) {
this->init(u.ptr_);
}
weak_ref(const weak_ref & o) {
this->init(o.ptr_);
}
weak_ref(weak_ref && o) { this->move(o); }
~weak_ref() { this->release(); }

weak_ref & operator = (const weak_ref & o) {
this->release();
this->init(o.ptr_);
return *this;
}

weak_ref & operator = (weak_ref && o) {
this->release();
this->move(o);
return *this;
}

// Swap
void swap(weak_ref & o) {
const ctrl_t * temp = ptr_;
ptr_ = o.ptr_;
o.ptr_ = temp;
}

// Reset is not const qualified, from user perspective this makes the most sense.
void reset() {
this->release();
}

// Lock: Obtain the payload if possible, otherwise return nullptr
T * lock() const {
if (ptr_) {
if (!result) { this->release(); }
return result;
}
return nullptr;
}

// Expired: cast this->lock() to bool
bool expired() const {
return static_cast<bool>(this->lock());
}

// use_count: mimic std::shared_ptr interface
long use_count() const {
return this->expired() ? 1 : 0;
}

// weak_ref_count: do something more useful :)
long weak_ref_count() const {
if (ptr_) {
return ptr_->ref_count_;
}
this->release();
}
return 0;
}
};

} // end namespace nonstd

#endif // NONSTD_WEAK_REF_HPP_INCLUDED

• Note: I put this on github here with some updates and improvements. Thanks to Jan Korous for comments :) github.com/cbeck88/weak_ref – Chris Beck Jun 23 '16 at 21:54

## explicit constructors

I would definitely make all single parameter constructors explicit.

## typo

// unique_rf points to, and ptr_->payload_ is also not null.


Should be probably

// unique_ref points to, and ptr_->payload_ is also not null.


## interface

I am not sure I understand your design. Do I get it correct that whenever you call unique_ref(T & t) constructor and object t is referring to actually goes out of scope you find yourself dangerously close to undefined behavior with dangling reference in your hand?

• Thanks. Yes, you got the design I think. The main unique_ref is similar to a T &, if it is left dangling then you get UB. But as long as that ref is properly managed, all the weak_ref are okay. The idea is that there should be only one, or, few, unique_ref. – Chris Beck Jun 22 '16 at 2:21
• @ChrisBeck Wouldn't it possibly make sense to rethink the design so that unique_ref is actually able to manage the referred resource? I could imagine in-place construction with arguments forwarded, moving the resource or something like that. – Jan Korous Jun 22 '16 at 7:39
• I mean then, you probably just want std::shared_ptr I guess. I agree that that strategy has a lot going for it. But it has some drawbacks -- once you have something in a std::shared_ptr you don't know exactly when it will be deleted (maybe the order relative to something else is important for some reason?). And you don't get as compact of a memory layout since you have to make a dynamic allocation. unique_ref is really only for the edge cases where those two things are very unattractive. – Chris Beck Jun 22 '16 at 20:18
• I guess I am going to change the interface, so that unique_ref is constructed from a pointer and not a reference. That way, when you use it, you have to explicitly take a pointer and pass it away, to make it more explicit that badness could be occurring if you aren't careful. – Chris Beck Jun 22 '16 at 22:28
• @ChrisBeck Not necessarily, I think there are other ways than shared_ptr. But I am not convinced about compact memory layout as it is dereferrencing all the way to referred object which is living who know where (possibly heap). – Jan Korous Jun 22 '16 at 22:29