In addition to my comments I also provide a very basic offset_ptr implementation. I agree that making a simple project dependent on something like boost may be an overkill but even until reaching a certain project size where you decide to switch to a more serious set of libraries it is worth wrapping critical things like offset pointers. Wrapping helps you to centralize your offset handler code and also to guard yourself with asserts. A simple offset_ptr template that doesn't handle all cases can still be much better than hand-coded offset+pointer manipulator code that is copy pasted everywhere and its basic implementation without attempting to be comprehensive is:
template <typename T, typename OffsetInt=ptrdiff_t>
class offset_ptr
{
template <typename U, typename OI> friend class offset_ptr;
public:
offset_ptr() : m_Offset(0) {}
offset_ptr(T* p)
{
set_ptr(p);
}
offset_ptr(offset_ptr& other)
{
set_ptr(other.get_ptr());
}
template <typename U, typename OI>
offset_ptr(offset_ptr<U,OI>& other)
{
set_ptr(static_cast<T*>(other.get_ptr()));
}
offset_ptr& operator=(T* p)
{
set_ptr(p);
return *this;
}
offset_ptr& operator=(offset_ptr& other)
{
set_ptr(other.get_ptr());
return *this;
}
template <typename U, typename OI>
offset_ptr& operator=(offset_ptr<U,OI>& other)
{
set_ptr(static_cast<T*>(other.get_ptr()));
return *this;
}
T* operator->()
{
assert(m_Offset);
return get_ptr();
}
const T* operator->() const
{
assert(m_Offset);
return get_ptr();
}
T& operator*()
{
assert(m_Offset);
return *get_ptr();
}
const T& operator*() const
{
assert(m_Offset);
return *get_ptr();
}
operator T* ()
{
return get_ptr();
}
operator const T* () const
{
return get_ptr();
}
private:
void set_ptr(const T* p)
{
m_Offset = p ? OffsetInt((char*)p - (char*)this) : OffsetInt(0);
}
T* get_ptr() const
{
return m_Offset ? (T*)((char*)this + m_Offset) : (T*)nullptr;
}
private:
OffsetInt m_Offset;
};
offset_ptr<int> p;
int x = 5;
struct TestStruct
{
int member;
void func()
{
printf("%s(%d)\n", __FUNCTION__, member);
}
};
TestStruct ts;
offset_ptr<TestStruct> pts;
int main()
{
p = &x;
*p = 6;
printf("%d\n", x);
ts.member = 11;
if (!pts)
printf("pts is null\n");
pts = &ts;
if (pts)
pts->func();
pts = nullptr;
if (!pts)
printf("pts is null again\n");
// this will cause an assert because pts is null
pts->func();
return 0;
}
It may contain some operator functions that are pain in the ass to write if you are not used to implement pointer stuff but this is really simple compared to a full fledged pointer implementation of a complex lib like boost, isn't it? And it makes the pointer (offset) manipulator code much nicer! There is no excuse for not using at least a wrapper like this!
Wrapping the locks and other stuff is also fruitful even if you do it for yourself without external libs because after writing/using the locks with bare native api for 2-3-4 times it truns out that your code will look much nicer if you use a simple wrapper with a ctor/destructor/lock()/unlock() not to mention the centralized lock/unlock code that can be guared with asserts and occasionally you can put in debug info to the lock class (like the id of the last locker thread to debug deadlocks easier...).
So don't omit wrappers even if you are rolling your code without std or boost.