C++ Logo

std-proposals

Advanced search

[std-proposals] std::sizeof_minus_trailing_padding

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 30 Nov 2023 10:17:50 +0000
Following on from the discussion about "std::optional<T>::abandon",
I'd like to discuss whether it would be helpful to be able to know at
compile time how much trailing padding there is inside a type? So we
could have two new standard library functions as follows:

    namespace std {
        template<typename T>
        consteval size_t trailing_padding_size(void) noexcept
        {
            return compiler_magic;
        }

        template<typename T>
        consteval size_t sizeof_minus_trailing_padding(void) noexcept
        {
            return sizeof(T) - trailing_padding_size<T>();
        }
    }

This would allow us to write our own 'std::optional' that places the
boolean flag inside T's trailing padding. Like for example in the
following code:

    https://godbolt.org/z/exfbxqqe3

And here it is copy-pasted:

#include <cstddef> // size_t
#include <new> // ::new(void*)
#include <type_traits> // is_trivially_copyable
#include <utility> // forward

class A {
    unsigned i;
    char unsigned ch;
public:
    A(void) : i(0), ch(0) {}
    A(A const &rhs) { this->i = rhs.i + 1u; this->ch = rhs.ch + 1u; }
};

static_assert(false == std::is_trivially_copyable_v<A>); // Because
we implemented copy-constructor
static_assert(sizeof(A) == sizeof(unsigned)*2u);

namespace std {
    template<typename T>
    consteval size_t trailing_padding_size(void) noexcept
    {
        return -1;
    }

    template<>
    consteval size_t trailing_padding_size<A>(void) noexcept
    {
        return sizeof(unsigned) - 1u;
    }

    template<typename T>
    consteval size_t sizeof_minus_trailing_padding(void) noexcept
    {
        return sizeof(T) - trailing_padding_size<T>();
    }
}

template<typename T>
class MyOptional {
    static_assert(false == std::is_trivially_copyable_v<T>);
    [[no_unique_address]] alignas(T) char unsigned
buf[std::sizeof_minus_trailing_padding<T>()];
    bool is_alive = false;
public:
    template<typename... Params>
    constexpr T &emplace(Params&& ...args) // g++ won't accept
'Params... &&args'
    {
        if ( this->is_alive ) static_cast<T*>(static_cast<void*>(buf))->~T();
        this->is_alive = false;
        T &retval = *::new(buf) T( std::forward<Params>(args)... );
        this->is_alive = true;
        return retval;
    }
    ~MyOptional(void)
    {
        if ( false == this->is_alive ) return;
        static_cast<T*>(static_cast<void*>(buf))->~T();
    }
    constexpr T& value(void) noexcept { return
*static_cast<T*>(static_cast<void*>(buf)); }
    constexpr void abandon(void) noexcept { this->is_alive = false; }
    constexpr void reset() noexcept
    {
        if ( this->is_alive ) static_cast<T*>(static_cast<void*>(buf))->~T();
        this->is_alive = false;
    }
};

int main(void)
{
    MyOptional<A> monkey;

    monkey.emplace();

    static_assert( sizeof(A) == sizeof(MyOptional<A>) );
}

Received on 2023-11-30 10:18:02