C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [[packed]] std::unaligned

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
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

Received on 2023-12-20 03:42:04