Skip to content

Instantly share code, notes, and snippets.

@jemand2001
Last active September 14, 2023 16:03
Show Gist options
  • Select an option

  • Save jemand2001/5d96083086dc8ca81b51a57f6a6f532b to your computer and use it in GitHub Desktop.

Select an option

Save jemand2001/5d96083086dc8ca81b51a57f6a6f532b to your computer and use it in GitHub Desktop.
rudimentary shared_ptr implementation
#include <atomic>
#include <concepts>
#include <iostream>
#include <stdexcept>
#include <vector>
template <typename T>
class Badge {
friend T;
Badge() = default;
};
struct bad_control_block_delete : std::logic_error {
using std::logic_error::logic_error;
};
template <typename, typename>
struct SharedPtr;
template <typename T>
struct default_delete {
void operator()(T *p) { delete p; }
};
template <typename T, typename Deleter>
requires std::invocable<Deleter, T *> && (!std::is_array_v<T>)
struct ControlBlock {
T *thing;
std::atomic<size_t> shared_count{1};
std::atomic<size_t> weak_count{0};
Deleter del;
~ControlBlock() noexcept(false) {
if (shared_count || weak_count) {
throw bad_control_block_delete{
"destroying a control block when things are still referring to "
"it"};
}
destroy_object();
}
ControlBlock(const ControlBlock &) = delete;
ControlBlock(ControlBlock &&) = delete;
ControlBlock(T *p, Deleter d = {}) : thing{p}, del{d} {}
size_t add_shared() { return shared_count++; }
size_t add_weak() { return weak_count++; }
size_t del_shared() { return shared_count--; }
size_t del_weak() { return weak_count--; }
bool deletable() { return !(shared_count || weak_count); }
void destroy_object() {
// if (thing)
del(thing);
thing = nullptr;
}
// returns a new SharedPtr using this control block
SharedPtr<T, Deleter> manifest();
};
template <typename T, typename Deleter = default_delete<T>>
struct SharedPtr {
using Control = ControlBlock<T, Deleter>;
Control *control{nullptr};
SharedPtr() = default;
SharedPtr(Badge<Control>, Control *block) : control{block} {
if (control)
control->add_shared();
}
SharedPtr(T *p) : control{new Control{p}} {}
SharedPtr(T *p, Deleter &&d)
: control{new Control{p, std::forward<Deleter>(d)}} {}
SharedPtr(SharedPtr &&other) : control{other.control} {
other.control = nullptr;
}
SharedPtr(const SharedPtr &other) : control{other.control} {
if (control)
control->add_shared();
}
~SharedPtr() {
if (!control)
return;
auto count = control->del_shared();
if (control->deletable())
delete control;
if (!count)
control->destroy_object();
}
size_t ref_count() const { return control->shared_count; }
operator bool() const { return control->thing; }
T &operator*() { return *control->thing; }
T *operator->() { return control->thing; }
};
template <typename T, typename Deleter>
requires std::invocable<Deleter, T *> && (!std::is_array_v<T>)
SharedPtr<T, Deleter> ControlBlock<T, Deleter>::manifest() {
return SharedPtr({}, this);
}
template <typename T, typename Deleter>
struct WeakPtr {
using Control = ControlBlock<T, Deleter>;
using Shared = SharedPtr<T, Deleter>;
Control *control{nullptr};
WeakPtr() = default;
WeakPtr(const Shared &shared) : control{shared.control} {
if (control)
control->add_weak();
}
WeakPtr(const WeakPtr &other) : control{other.control} {
if (control)
control->add_weak();
}
WeakPtr(WeakPtr &&other) : control{other.control} {
other.control = nullptr;
}
Shared lock() {
if (present())
return control->manifest();
return {nullptr};
}
~WeakPtr() {
if (!control)
return;
control->del_weak();
if (control->deletable())
delete control;
}
bool present() const { return control->thing; }
};
template <typename T, typename D = default_delete<T>>
SharedPtr<T, D> makeShared(auto &&...args) {
return SharedPtr<T, D>(new T{std::forward<decltype(args)>(args)...});
}
int main() {
std::vector<SharedPtr<int>> vec;
vec.push_back(makeShared<int>(1));
vec.push_back(makeShared<int>(2));
vec.push_back(makeShared<int>(3));
vec.push_back(makeShared<int>(4));
auto a = vec[0];
WeakPtr weak = vec[1];
std::cout << *a << '\n';
std::cout << a.ref_count() << '\n';
std::cout << std::boolalpha << weak.present() << '\n';
auto b = weak.lock();
std::cout << b.ref_count() << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment