对齐 unique_ptr 的负载数据
Payload data with aligned unique_ptr
我有 unique_ptr<T>
,它始终与 4096 对齐 - 意味着指针值的最低 12 位始终等于 0。我的意图是在指针中存储一个字节的有效负载数据/元数据本身”。我的意思是创建一个 class 它将有一个移动转换构造函数,如:
template<typename T>
struct PayloadPtr {
/* ... */
PayloadPTr( unique_ptr<T> ptr ) noexcept { ... }
/* ... */
};
这将 'sink' unique_ptr<T>
并让它存储为成员,但重复使用最低的 12 位来存储字节变量...总体布局很重要,正如硬件给出的那样。
{
uint32_t payload : 12;
uint32_t pointer : 20;
}
整个 32 位值可以作为屏蔽有效负载的指针读取,可以屏蔽最高 20 位读出有效负载...重要的是 T 的 constructor/destructor 在存储期间不会被调用PayloadPtr
,就像常规的unique_ptr
成员变量存储一样。
如何实现这种行为?要使用具有 unique_ptr 作为一个成员同时将有效负载作为另一个成员的联合?或者将其存储为纯数据(例如 uintptr_t
)并重新解释 getters/setters?
中的数据
推荐的实施方式是什么?感谢任何愿意提供帮助的人!
您可以创建类似于 unique_ptr
的内容,但将指针存储为 std::uintptr_t
并在需要时投射和过滤掉有效载荷。
下面是它的大概样子:
#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
我有 unique_ptr<T>
,它始终与 4096 对齐 - 意味着指针值的最低 12 位始终等于 0。我的意图是在指针中存储一个字节的有效负载数据/元数据本身”。我的意思是创建一个 class 它将有一个移动转换构造函数,如:
template<typename T>
struct PayloadPtr {
/* ... */
PayloadPTr( unique_ptr<T> ptr ) noexcept { ... }
/* ... */
};
这将 'sink' unique_ptr<T>
并让它存储为成员,但重复使用最低的 12 位来存储字节变量...总体布局很重要,正如硬件给出的那样。
{
uint32_t payload : 12;
uint32_t pointer : 20;
}
整个 32 位值可以作为屏蔽有效负载的指针读取,可以屏蔽最高 20 位读出有效负载...重要的是 T 的 constructor/destructor 在存储期间不会被调用PayloadPtr
,就像常规的unique_ptr
成员变量存储一样。
如何实现这种行为?要使用具有 unique_ptr 作为一个成员同时将有效负载作为另一个成员的联合?或者将其存储为纯数据(例如 uintptr_t
)并重新解释 getters/setters?
推荐的实施方式是什么?感谢任何愿意提供帮助的人!
您可以创建类似于 unique_ptr
的内容,但将指针存储为 std::uintptr_t
并在需要时投射和过滤掉有效载荷。
下面是它的大概样子:
#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