Date: Wed, 20 Dec 2023 03:42:12 +0000
On Tue, Dec 19, 2023 at 5:07 PM Frederick Virchanza Gotham wrote:
>
> I mean when you do:
>
> char c;
> char *p = &c;
> long double *p2 = static_cast<long double*>(static_cast<void*>(p));
>
> and then convert it back:
>
> char *p3 = static_cast<char*>(static_cast<void*>(p2));
>
> then you might not get back the original memory address.
Let's say we have an object of:
std::basic_string<long double>
So the pointer that resides inside the object will be a 'long double*'
instead of a 'char*'. I'm trying to figure out how to relocate such an
object to unaligned space without having a 'long double*' with an
invalid value (i.e. pointing to an unaligned address). So to use a
more simple example than 'basic_string', let's use the following
class:
struct Monkey {
long double *p;
long double buf[8u];
Monkey(void) : p(buf) {}
};
When we relocate an object of this class, we must check if 'p' points
to within the object, and if it does then we must adjust it. First I
wrote a function to get the modulus of the alignment:
template<typename T>
inline std::ptrdiff_t alignment_modulus(void const *const arg)
{
assert( nullptr != arg );
#ifdef UINTPTR_MAX
return reinterpret_cast<std::uintptr_t>(arg) % alignof(T);
#else
// This implementation does not need std::uintptr_t (which
might not exist)
using std::byte;
std::ptrdiff_t retval = alignof(T);
byte *p = static_cast<byte*>(const_cast<void*>(arg));
std::size_t buffer_size = sizeof(T);
for ( ; ; ++p, --retval )
{
void *pv = p;
if ( nullptr != std::align(alignof(T), sizeof(T), pv, buffer_size) )
{
return retval % alignof(T);
}
}
#endif
}
Next, I think the '_Relocate' function would be something like:
void _Relocate(void *const dst, void *const src)
{
using T = Monkey;
using std::byte;
std::memcpy(dst,src,__datasizeof(T));
long double *pld;
std::memcpy(&pld,src,sizeof pld); // 'pld' will always be
valid (because of Line #71)
byte *p = static_cast<byte*>(static_cast<void*>(pld));
p += alignment_modulus<T>(src);
if ( p < static_cast<byte*>(src) ) return;
if ( p >= (static_cast<byte*>(src) + __datasizeof(T))) return;
// If control reaches here, we must adjust the pointer
// but we must be careful because 'long double*' might
// have less precision than 'byte*'. We will do this
// in three steps.
// Step 1: Add the offset to 'p'
p += static_cast<byte*>(dst) - static_cast<byte*>(src);
// Step 2: Subtract the alignment modulus
p -= alignment_modulus<T>(p);
// Step 3: Convert to 'long double*' and store in (possibly
unaligned) destination
assert( 0u == alignment_modulus<long double>(p) );
pld = static_cast<long double*>(static_cast<void*>(p));
std::memcpy(dst,&pld,sizeof pld);
}
Putting it all together would be something like:
https://godbolt.org/z/59e6x7W8r
>
> I mean when you do:
>
> char c;
> char *p = &c;
> long double *p2 = static_cast<long double*>(static_cast<void*>(p));
>
> and then convert it back:
>
> char *p3 = static_cast<char*>(static_cast<void*>(p2));
>
> then you might not get back the original memory address.
Let's say we have an object of:
std::basic_string<long double>
So the pointer that resides inside the object will be a 'long double*'
instead of a 'char*'. I'm trying to figure out how to relocate such an
object to unaligned space without having a 'long double*' with an
invalid value (i.e. pointing to an unaligned address). So to use a
more simple example than 'basic_string', let's use the following
class:
struct Monkey {
long double *p;
long double buf[8u];
Monkey(void) : p(buf) {}
};
When we relocate an object of this class, we must check if 'p' points
to within the object, and if it does then we must adjust it. First I
wrote a function to get the modulus of the alignment:
template<typename T>
inline std::ptrdiff_t alignment_modulus(void const *const arg)
{
assert( nullptr != arg );
#ifdef UINTPTR_MAX
return reinterpret_cast<std::uintptr_t>(arg) % alignof(T);
#else
// This implementation does not need std::uintptr_t (which
might not exist)
using std::byte;
std::ptrdiff_t retval = alignof(T);
byte *p = static_cast<byte*>(const_cast<void*>(arg));
std::size_t buffer_size = sizeof(T);
for ( ; ; ++p, --retval )
{
void *pv = p;
if ( nullptr != std::align(alignof(T), sizeof(T), pv, buffer_size) )
{
return retval % alignof(T);
}
}
#endif
}
Next, I think the '_Relocate' function would be something like:
void _Relocate(void *const dst, void *const src)
{
using T = Monkey;
using std::byte;
std::memcpy(dst,src,__datasizeof(T));
long double *pld;
std::memcpy(&pld,src,sizeof pld); // 'pld' will always be
valid (because of Line #71)
byte *p = static_cast<byte*>(static_cast<void*>(pld));
p += alignment_modulus<T>(src);
if ( p < static_cast<byte*>(src) ) return;
if ( p >= (static_cast<byte*>(src) + __datasizeof(T))) return;
// If control reaches here, we must adjust the pointer
// but we must be careful because 'long double*' might
// have less precision than 'byte*'. We will do this
// in three steps.
// Step 1: Add the offset to 'p'
p += static_cast<byte*>(dst) - static_cast<byte*>(src);
// Step 2: Subtract the alignment modulus
p -= alignment_modulus<T>(p);
// Step 3: Convert to 'long double*' and store in (possibly
unaligned) destination
assert( 0u == alignment_modulus<long double>(p) );
pld = static_cast<long double*>(static_cast<void*>(p));
std::memcpy(dst,&pld,sizeof pld);
}
Putting it all together would be something like:
https://godbolt.org/z/59e6x7W8r
Received on 2023-12-20 03:42:04