Created
June 23, 2019 16:55
-
-
Save langthom/4c9b27534e06c2f03161e6ebb8a374e9 to your computer and use it in GitHub Desktop.
Implementation of a unique_ptr which can store some additional bits with low overhead. Only for normal pointers, not array pointers.
This file contains hidden or 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 <iostream> | |
#include <cstring> | |
#include <bitset> | |
#include <limits> | |
#include <memory> | |
#include <type_traits> | |
#define GENERATE_PRINT_FUNCTION | |
namespace enhancedPointer | |
{ | |
namespace | |
{ | |
template< std::size_t Bits, std::size_t Shift > | |
struct SelectAllMaskHelper : std::integral_constant< std::size_t, ((0xF) << Shift) | SelectAllMaskHelper< Bits-4, Shift+1 >::value > {}; | |
template< std::size_t Shift > | |
struct SelectAllMaskHelper< 4, Shift > : std::integral_constant< std::size_t, ((0xF) << Shift) > {}; | |
// Constructs a bit-mask fully set for the amount of Bits which must be a multiple of 4. | |
template< std::size_t Bits > | |
struct SelectAllMask : SelectAllMaskHelper< Bits, 0 > { | |
static_assert((Bits & 3) == 0, "Number of free bits must be multiple of 4."); | |
}; | |
// Constructs the inverse bit-mask for the number of bits which must be a multiple of 4. | |
template< std::size_t Bits > | |
struct DeselectAllMask : std::integral_constant< std::size_t, ~SelectAllMask< Bits >::value > { | |
static_assert((Bits & 3) == 0, "Number of free bits must be multiple of 4."); | |
}; | |
} | |
template< | |
class ValueTypeT, | |
std::size_t FreeBitsT = 4 | |
> | |
class EnhancedPointer | |
{ | |
static_assert(((1ul << FreeBitsT) & 15) == 0, "1 << FreeBitsT must be multiple of 16."); | |
public: | |
typedef ValueTypeT element_type; | |
typedef element_type* pointer; | |
private: | |
typedef std::uint16_t offset_t; | |
enum | |
{ | |
FreeBits = FreeBitsT, | |
Alignment = (1ul << FreeBits), | |
ExtraSpace = sizeof(offset_t) + Alignment - 1ul, | |
TotalSize = sizeof(element_type) + ExtraSpace, | |
}; | |
// The raw address to a buffer of the needed size including space for the offset and alignment issues. | |
unsigned char* raw; | |
public: | |
constexpr EnhancedPointer() noexcept | |
: raw(nullptr) | |
{ | |
} | |
constexpr EnhancedPointer(std::nullptr_t /* unused */) noexcept | |
: EnhancedPointer() | |
{ | |
} | |
// takes ownership | |
explicit EnhancedPointer(pointer _p) | |
: EnhancedPointer() | |
{ | |
if(_p) | |
{ | |
// align pointer. | |
unsigned char* _raw = new unsigned char[TotalSize]; | |
if(_raw) | |
{ | |
raw = alignPointer(_raw); | |
std::memset(raw, 0, sizeof(element_type) + sizeof(offset_t)); | |
std::memcpy(reinterpret_cast< pointer >(raw), _p, sizeof(element_type)); | |
storeOffset(raw - _raw); | |
} | |
delete _p; | |
} | |
} | |
EnhancedPointer(EnhancedPointer&& other) noexcept | |
: raw(other.raw) | |
{ | |
other.raw = nullptr; | |
} | |
EnhancedPointer(const EnhancedPointer& other) = delete; | |
EnhancedPointer& operator=(const EnhancedPointer& other) = delete; | |
EnhancedPointer& operator=(std::nullptr_t /* unused */) noexcept | |
{ | |
reset(); | |
return *this; | |
} | |
EnhancedPointer& operator=(EnhancedPointer&& other) noexcept | |
{ | |
if(this != &other) | |
{ | |
destroy(); | |
swap(other); | |
} | |
return *this; | |
} | |
~EnhancedPointer() | |
{ | |
destroy(); | |
} | |
inline constexpr operator bool(void) const noexcept | |
{ | |
return static_cast< bool >(raw); | |
} | |
inline constexpr pointer get(void) const noexcept | |
{ | |
return reinterpret_cast< pointer >( reinterpret_cast< std::uintptr_t >(raw) & DeselectAllMask< FreeBits >::value ); | |
} | |
inline constexpr pointer operator->(void) const noexcept | |
{ | |
return get(); | |
} | |
inline constexpr typename std::add_lvalue_reference< element_type >::type operator*(void) const | |
{ | |
return *get(); | |
} | |
inline constexpr void swap(EnhancedPointer& other) noexcept | |
{ | |
std::swap(raw, other.raw); | |
} | |
inline constexpr void reset(pointer other = pointer()) noexcept | |
{ | |
// Destroy currently managed object. | |
destroy(); | |
// Acquire ownership of passed pointer. | |
EnhancedPointer newPtr(other); | |
swap(newPtr); | |
} | |
/* | |
* A note about the function 'release' in the std::unique_ptr functionality. | |
* There, the raw pointer is returned but the object is not destroyed. Some other mechanism then is | |
* responsible for deleting the pointer/object. | |
* | |
* However, in our case the user would have to know how we implemented the alignment process to properly | |
* delete the object (by reverting the offset). | |
* | |
* We do not want to expose this to the user, thus we DO NOT provide this functionality. | |
*/ | |
#ifdef GENERATE_PRINT_FUNCTION | |
void print(std::ostream& out = std::cout) | |
{ | |
pointer _ptr = get(); | |
out << "Address (hex): " << std::hex << _ptr; | |
out << "\nAddress (bin): " << std::bitset< (sizeof(pointer) << 3) >(reinterpret_cast< std::size_t >(_ptr)); | |
const std::size_t bitsVal = reinterpret_cast< std::size_t >(raw) & SelectAllMask< FreeBits >::value; | |
out << "\nAdditional information bits: " << std::bitset< FreeBits >(bitsVal); | |
if(_ptr) | |
{ | |
out << "\nStored value: " << *get(); | |
} | |
out << '\n'; | |
} | |
#endif // GENERATE_PRINT_FUNCTION | |
// | |
template< std::size_t BitIndex, bool Value > | |
void store(void) | |
{ | |
static_assert(BitIndex < FreeBits, "EnhancedPointer::store: BitIndex must be less than FreeBits"); | |
if(Value) | |
{ | |
reinterpret_cast< std::uintptr_t& >(raw) |= reinterpret_cast< std::uintptr_t >(1ul << BitIndex); | |
} | |
else | |
{ | |
reinterpret_cast< std::uintptr_t& >(raw) &= ~reinterpret_cast< std::uintptr_t >(1ul << BitIndex); | |
} | |
} | |
template< std::size_t BitIndex > | |
bool isSet(void) const | |
{ | |
return (reinterpret_cast< std::uintptr_t >(raw) & reinterpret_cast< std::uintptr_t >(1ul << BitIndex)) != 0; | |
} | |
private: | |
template< class U > | |
inline constexpr U* alignPointer(U* ptr) const | |
{ | |
return reinterpret_cast< U* >((reinterpret_cast< std::uintptr_t >(ptr) + Alignment - 1ul) & ~reinterpret_cast< std::uintptr_t >(Alignment - 1ul)); | |
} | |
inline constexpr void storeOffset(const offset_t offset) | |
{ | |
*reinterpret_cast< offset_t* >(raw + sizeof(element_type)) = offset; | |
} | |
inline constexpr offset_t loadOffset(void) const | |
{ | |
return *reinterpret_cast< offset_t* >(raw + sizeof(element_type)); | |
} | |
inline constexpr void resetAllFlagsAndRepairOffset(void) noexcept | |
{ | |
reinterpret_cast< std::size_t& >(raw) &= DeselectAllMask< FreeBits >::value; | |
const offset_t offset = loadOffset(); | |
*reinterpret_cast< offset_t* >(raw + sizeof(element_type)) = 0; // clear offset. | |
raw -= offset; | |
} | |
inline constexpr void destroy(void) noexcept | |
{ | |
if(raw) | |
{ | |
get()->~element_type(); | |
resetAllFlagsAndRepairOffset(); | |
delete[] raw; | |
raw = nullptr; | |
} | |
} | |
}; | |
} // namespace enhancedPointer | |
// ----------------------------------------------------------------------------- | |
template< class CoordinateT, std::size_t Dim > | |
class Point | |
{ | |
CoordinateT coords[Dim]; | |
public: | |
template< class... T > | |
Point(T&&... args) | |
: coords{std::forward< T >(args)...} | |
{ | |
} | |
template< class U, std::size_t D > | |
friend std::ostream& operator<<(std::ostream& out, const Point< U, D >& point) | |
{ | |
out << '('; | |
for(std::size_t i = 0; i < D; ++i) | |
{ | |
if(i > 0) out << ", "; | |
out << point.coords[i]; | |
} | |
out << ')'; | |
return out; | |
} | |
}; | |
int main(void) | |
{ | |
using namespace enhancedPointer; | |
//typedef EnhancedPointer< int, 4 > EnhancedPtrType; | |
typedef EnhancedPointer< int, 8 > EnhancedPtrType; | |
EnhancedPtrType enhancedPtrEmpty; | |
enhancedPtrEmpty.print(); | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
EnhancedPtrType enhancedPtr(new int(42)); | |
std::cout << "is bit 1 stored? " << std::boolalpha << enhancedPtr.isSet< 1 >() << '\n'; | |
enhancedPtr.print(); | |
std::cout << '\n' << std::string(50, '-') << '\n'; | |
enhancedPtr.store< 1, true >(); | |
std::cout << "is bit 1 stored? " << std::boolalpha << enhancedPtr.isSet< 1 >() << '\n'; | |
enhancedPtr.print(); | |
std::cout << '\n' << std::string(50, '-') << '\n'; | |
enhancedPtr.store< 1, false >(); | |
std::cout << "is bit 1 stored? " << std::boolalpha << enhancedPtr.isSet< 1 >() << '\n'; | |
enhancedPtr.print(); | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
#if 0 | |
typedef EnhancedPointer< int[], 8 > ArrayPointer; | |
ArrayPointer arrayPtr(new int[42]); | |
arrayPtr.print(); | |
arrayPtr.store< 0, true >(); | |
arrayPtr.print(); | |
arrayPtr.store< 0, false >(); | |
arrayPtr.print(); | |
#endif | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
typedef EnhancedPointer< Point< double, 3 >, 8 > PointPointer; | |
PointPointer pointPtr(new Point< double, 3 >(1.2, 3.4, 5.6)); | |
pointPtr.print(); | |
std::cout << '\n' << std::string(50, '-') << '\n'; | |
pointPtr.store< 0, true >(); // interpretation: point is selected e.g. in a GUI. | |
pointPtr.print(); | |
std::cout << '\n' << std::string(50, '-') << '\n'; | |
pointPtr.store< 0, false >(); // de-select operation | |
pointPtr.print(); | |
std::cout << '\n' << std::string(50, '-') << '\n'; | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
std::cout << "SWAP test:\n"; | |
EnhancedPtrType ptr1(new int(7)); | |
ptr1.store< 2, true >(); | |
EnhancedPtrType ptr2(new int(42)); | |
ptr2.store< 1, true >(); | |
std::cout << "Before swap:\n"; | |
ptr1.print(); | |
ptr2.print(); | |
ptr1.swap(ptr2); | |
std::cout << "After swap:\n"; | |
ptr1.print(); | |
ptr2.print(); | |
std::cout << '\n'; | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
std::cout << "RESET test:\n"; | |
EnhancedPtrType ptr3(new int(7)); | |
ptr3.store< 2, true >(); | |
std::cout << "Before reset:\n"; | |
ptr3.print(); | |
ptr3.reset(new int(8)); | |
std::cout << "After reset:\n"; | |
ptr3.print(); | |
std::cout << '\n'; | |
std::cout << '\n' << std::string(50, '+') << '\n'; | |
std::cout << "Move test:\n"; | |
EnhancedPtrType ptr4(new int(2)); | |
EnhancedPtrType ptr5(std::move(ptr4)); | |
ptr5.print(); | |
std::cout << std::string(50, '-') << '\n'; | |
EnhancedPtrType ptr6(EnhancedPtrType(new int(1))); | |
ptr6.print(); | |
std::cout << std::string(50, '-') << '\n'; | |
EnhancedPtrType ptr7 = std::move(ptr6); | |
ptr7.print(); | |
std::cout << '\n'; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment