=?ISO-8859-15?Q?Juli=E1n?= Albo posted:
Frederick Gotham wrote:
The C++ Standard, if it wished, could also impose such a limitation
as "An object should not store its own address within itself, or the
address of a member object, or base object.". It could then implement
a universal swap algorithm quite elementarily as follows:
Buy they don't do. Probably for a good reason, such as consider that a
requisite so strict will be unacceptable for many uses, and that is
undesirable for standard containers.
Not if we write an "OffsetPointer" class that behaves exactly like a
domestic pointer.
I'll start off with some sample code. Here's a class which stores its own
address within itself (or the address of a member, etc.):
#include <cstddef>
#include <cstdlib>
#include <iostream>
class StringStream {
private:
static std::size_t const buflen = 64;
char buffer[buflen];
char *p_pos;
public:
StringStream() : p_pos( &*buffer ), buffer() {}
void Append( char const c )
{
if( p_pos != buffer + (buflen - 2) ) *p_pos++ = c;
}
void Print() const
{
std::cout << buffer << '\n';
}
};
int main()
{
StringStream ss;
ss.Append('H');
ss.Append('e');
ss.Append('l');
ss.Append('l');
ss.Append('o');
ss.Append('!');
ss.Print();
std::system("PAUSE");
}
If we were to re-locate an object of the StringStream class in memory,
then
its "p_pos" member would become corrupted.
I propose to change the "p_pos" member from:
char *p_pos;
to:
OffsetPointer<char> p_pos;
but without changing anything else. Thus, the class's definition would
become:
#include <offp.hpp> /* Contains definition of OffsetPointer */
#include <cstddef>
#include <iostream>
class StringStream {
private:
static std::size_t const buflen = 64;
char buffer[buflen];
OffsetPointer<char> p_pos;
public:
StringStream() : p_pos( &*buffer ), buffer() {}
void Append( char const c )
{
if( p_pos != buffer + (buflen - 2) ) *p_pos++ = c;
}
void Print() const
{
std::cout << buffer << '\n';
}
};
And then I would implement the OffsetPointer class something like the
following:
#include <cstddef>
#include <cstdlib>
#include <iostream>
template<class T>
class OffsetPointer {
protected:
mutable T *p ;
mutable const OffsetPointer *old_this;
public:
OffsetPointer( T * const arg_p )
: old_this(this), p(arg_p) {}
OffsetPointer( const OffsetPointer &original )
: old_this(this), p(original.p) {}
/* old_this becomes refreshed when copy-constructed
or assigned. */
OffsetPointer &operator=( const OffsetPointer &rhs ) { p = rhs.p; }
protected:
void ValidateP() const
{
if ( this == old_this ) return;
if ( this > old_this )
{
std::size_t const moved_forward_bytes =
reinterpret_cast<const unsigned char*>(this)
- reinterpret_cast<const unsigned char*>(old_this);
p = reinterpret_cast<T*>(
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(p)
)
+ moved_forward_bytes );
}
else
{
std::size_t const moved_backward_bytes =
reinterpret_cast<const unsigned char*>(old_this)
- reinterpret_cast<const unsigned char*>(this);
p = reinterpret_cast<T*>(
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(p)
)
- moved_backward_bytes );
}
old_this = this;
}
public:
operator T* &() const
{
ValidateP();
return p;
}
};
class StringStream {
private:
static std::size_t const buflen = 64;
char buffer[buflen];
OffsetPointer<char> p_pos;
public:
StringStream() : p_pos( &*buffer ), buffer() {}
void Append( char const c )
{
if( p_pos != buffer + (buflen - 2) ) *p_pos++ = c;
}
void Print() const
{
std::cout << buffer << '\n';
}
};
template<class T>
void AdvanceObjectAndIncrementPointer( T * &p_obj )
{
std::memcpy( p_obj + 1, p_obj, sizeof(T) );
++p_obj;
}
int main()
{
unsigned char * const p_buf =
new unsigned char[ sizeof(StringStream) * 7 ];
StringStream *p_ss = new(p_buf) StringStream;
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('H');
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('e');
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('l');
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('l');
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('o');
AdvanceObjectAndIncrementPointer(p_ss);
p_ss->Append('!');
p_ss->Print();
std::system("PAUSE");
/* Time for clean up: */
p_ss->~StringStream();
delete [] p_buf;
}
As you can see, it wouldn't be too complicated for C++ to adopt a policy
of
"A class should be written in such a way that an object of it may be re-
located in memory without ill-effect".
pointer.