Skip to content

Instantly share code, notes, and snippets.

@langthom
Created June 23, 2019 16:55
Show Gist options
  • Save langthom/4c9b27534e06c2f03161e6ebb8a374e9 to your computer and use it in GitHub Desktop.
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.
#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