I have unique_ptr<T> which is always aligned to 4096 - means the lowest 12bits of the pointer value are always equal to 0. My intension is to store a byte of payload data / metadata "in within the pointer itself". I mean to create a class which will have a move converting constructor like:
template<typename T>
struct PayloadPtr {
/* ... */
PayloadPTr( unique_ptr<T> ptr ) noexcept { ... }
/* ... */
};
which will 'sink' the unique_ptr<T> and let it store as member but reusing the lowest 12bits to store the byte variable... The overall layout is important as given by the HW.
{
uint32_t payload : 12;
uint32_t pointer : 20;
}
The whole 32bit value can be read as pointer masking out the payload, the payload can be read out masking the highest 20bits... Important stuff is the T's constructor/destructor is not called during the storage within the PayloadPtr, just like regular unique_ptr member variable storage.
How to achieve such behavior? To use union having unique_ptr as one member while having the payload as other? Or to store is as plain data (e.g. uintptr_t) and reinterpret the data in getters/setters?
What would be the recommended way to implement that? Thanks to anyone willing to help!
You could create something similar to unique_ptr but storing the pointer as a std::uintptr_t and cast and filter out the payload when needed.
Here's an outline what it could look like:
#ifndef __cpp_aligned_new
#error "Requires Dynamic memory allocation for over-aligned data (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html)"
#else
#include <cstdint>
#include <utility>
template<typename T>
class PayloadPtr {
public:
static_assert(alignof(T) % 0x1000 == 0, "must be aligned to a multiple of 0x1000");
// construct from a T*
PayloadPtr(T* t) : data(reinterpret_cast<std::uintptr_t>(t)) {}
// take over ownershipt from a unique_ptr
PayloadPtr(std::unique_ptr<T>&& o) : PayloadPtr(o.release()) {}
// rule of 5
PayloadPtr(const PayloadPtr&) = delete;
PayloadPtr(PayloadPtr&& o) noexcept : data(std::exchange(o.data, 0)) {}
PayloadPtr& operator=(const PayloadPtr&) = delete;
PayloadPtr& operator=(PayloadPtr&& o) noexcept {
std::swap(data, o.data);
return *this;
}
~PayloadPtr() { delete get(); }
T* get() const { return reinterpret_cast<T*>(data & ~0xFFFULL); }
T* release() { T* rv = get(); data = 0; return rv; }
// dereferencing
T& operator*() const { return *get(); }
T* operator->() const { return get(); }
explicit operator bool() const noexcept { return get() != nullptr; }
void reset(T* o = nullptr) noexcept {
delete get();
data = reinterpret_cast<std::uintptr_t>(o);
}
void set_payload(std::uint16_t p) { data = (data & ~0xFFFULL) | (p & 0xFFF); }
std::uint16_t get_payload() const { return data & 0xFFF; }
private:
std::uintptr_t data;
};
#endif
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With