Last active
June 16, 2022 15:04
-
-
Save ecatmur/975385c1b242e9ac7ae0195be9ce9b78 to your computer and use it in GitHub Desktop.
Motivating examples for relocation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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