Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Last active June 16, 2022 15:04
Show Gist options
  • Save ecatmur/975385c1b242e9ac7ae0195be9ce9b78 to your computer and use it in GitHub Desktop.
Save ecatmur/975385c1b242e9ac7ae0195be9ce9b78 to your computer and use it in GitHub Desktop.
Motivating examples for relocation
#include <concepts>
#include <functional>
#include <utility>
template<class T>
struct unique_ptr {
T* ptr_;
unique_ptr(unique_ptr&& rhs) noexcept : ptr_(std::exchange(rhs.ptr_, nullptr)) {}
#if GLIBCXX_ABI < 202602L
[[gnu::abi::cxx23]]
#endif
unique_ptr(unique_ptr) = default;
unique_ptr& operator=(unique_ptr&& rhs) { delete std::exchange(ptr_, std::exchange(rhs.ptr_, nullptr)); }
unique_ptr& operator=(unique_ptr rhs) { std::swap(ptr_, (reloc rhs).ptr_); }
~unique_ptr() { delete ptr_; }
T* release() && { return std::exchange(ptr_, nullptr); }
T* release(this unique_ptr self)
{
union { unique_ptr x; } = { reloc self; };
return x.ptr_;
}
};
// https://stackoverflow.com/a/57299732/567292
template<class T>
struct list {
struct node_base {
node_base* prev;
node_base* next;
};
struct node : node_base {
T t;
};
node_base* head = new node_base; // MSVC style
void clear();
list(list) = default;
~list() { clear(); delete head; }
};
template<class P>
struct non_null {
P p;
non_null(non_null&&) = delete;
non_null(non_null const& r)
requires std::copy_constructible<P>
: p(r.p) {}
non_null(non_null) = default;
};
template<class T>
struct optional {
union {
T x;
char disengaged;
};
bool engaged = true;
~optional() {
if (engaged)
std::destroy_at(&x);
}
T pop() [[expects(engaged)]] [[ensures(not engaged)]] {
engaged = false;
return std::relocate(&x);
}
};
struct X {
non_null<unique_ptr<int>> p;
std::function<int()> f = [this] { return *p.p; };
explicit X(non_null<unique_ptr<int>> p) : p(reloc p) {}
X(X) : f([this] { return *p.p; }) {}
};
template<class T, class Alloc = std::allocator<T>>
struct vector {
[[no_unique_address]] Alloc alloc;
T* arr = nullptr;
int size = 0;
int capacity = 0;
void grow(int cap) {
if constexpr (std::is_trivially_relocatable_v<T>) {
arr = alloc.reallocate(arr, capacity = cap);
}
else if constexpr (std::is_nothrow_relocatable_v<T>) {
auto old = std::exchange(arr, alloc.allocate(capacity = cap));
std::uninitialized_relocate_n(old, size, arr);
alloc.deallocate(old);
}
else {
auto oldcap = std::exhange(capacity, cap);
auto old = std::exchange(arr, alloc.allocate(capacity));
try {
std::uninitialized_copy_n(old, size, arr);
std::destroy_n(old, size);
alloc.deallocate(old);
}
catch (...) {
alloc.deallocate(std::exchange(arr, old));
cap = oldcap;
throw;
}
}
}
};
template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple t) {
union { Tuple u } = { reloc t };
return [&]<size_t... I>(index_sequence<I...>) {
return f(std::relocate(&get<I>(u))...);
}(make_index_sequence<tuple_size_v<Tuple>>());
}
template<class T, class Tuple>
T make_from_tuple(Tuple t) {
return apply([](auto... x) { return T(reloc x...); }, reloc t);
}
auto x = make_from_tuple<X>(tuple(non_null(make_unique<int>(99))));
template<class T, class U = T>
T exchange(T &obj, U new_value) {
T old_value = std::relocate(&obj);
std::construct_at(&obj, reloc new_value);
return old_value;
}
template<class T>
void swap(T& lhs, T& rhs) {
rhs = exchange(lhs, rhs);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment