uniptr_t – C++ Pointer Utility Class
uniptr_t stands for univerasl pointer. It’s a small C++ class wrapping any pointer type. It removes the need to cast any pointer to any other pointer type, or to cast a numerical to a pointer type and vice versa.
The purpose of this library is to make working with pointers a lot a lot easier, but you still must understand how memory and pointers work. Be careful:
uniptr_t is not always the best to use when working with pointers or memory addresses.
- Automatic pointer casting
- Utility functions
- All operators are overloaded
- Platform independent
- Works on C++14 and newer versions (I haven’t tested it on C++11 or older versions)
- 1 file include
- Doesn’t affect performance of your code at all
You can assign all
uniptr_t objects to any pointer type or to a numerical.
// assign to a void pointer void* a_void_pointer = nullptr; uniptr_t p1 = a_void_pointer; // assign to any other pointer int* any_pointer_type = nullptr; uniptr_t p2 = any_pointer_type; // assign to nullptr uniptr_t p3 = nullptr; // assign to 0 without any casting uniptr_t p4 = 0; // assign to any other numerical without casting uniptr_t p5 = 0x18401;
Of course you can also do this vice versa – automatically cast
uniptr_t to any pointer or numerical:
uniptr_t p1 = 0xDEADBEEF; // assign to a void pointer void* a_void_pointer = p1; // assign to any other pointer int* any_pointer_type = p1; // assign to any numerical int numerical1 = p1; uintptr_t numerical2 = p1; // and even to floats or doubles! float floating1 = p1; double floating2 = p1;
All operators are overloaded and work with any pointer type or numerical:
int numerical1 = 10; uintptr_t numerical2 = 20; float floating1 = 10.f; double floating2 = 49.0; void* void_ptr = (void*)0x100; int* any_ptr = (int*)0x81; uniptr_t my_ptr = 0; my_ptr += numerical1; my_ptr = my_ptr - floating1; my_ptr /= any_ptr; my_ptr++; --my_ptr; // etc.
uniptr_t has some utility functions:
uniptr_t my_ptr = nullptr; my_ptr.set<int>(10); // same as *(int*)my_ptr = 10; int my_num = my_ptr.get<int>(); // same as int my_num = *(int*)my_ptr; // if you want to make sure the pointer is casted properly, you can use .as() or .as_ptr() uintptr_t numerical = my_ptr.as<uintptr_t>(); int* void_ptr = my_ptr.as_ptr<int>(); // here you only need to specify the type of the pointer without * // get the size of void* (same as sizeof(void*)) size_t pointer_size = my_ptr.size(); // you can even do uniptr_t::size() since it's a static function // .relative_addr() and .to_relative_addr() are hard to explain but // as the name suggests, I use them to caluclate relative addresses // that an assembly instruction might be holding // and finally we have some windows specific functions // .is_valid() checks if the memory address uniptr_t is pointing to is valid (checks if it's commited memory) // .is_readable() checks if the memory address uniptr_t is pointing to is readable // .is_writeable() checks if the memory address uniptr_t is pointing to is writeable // .region_size() if the function succeeds, the return value is non-zero and holds the number of bytes in memory of the // region uniptr_t is pointing to (in other words how big the region is)