C++ Logo

std-proposals

Advanced search

[std-proposals] Ensure ABI compatibility at runtime -- std::layout_of

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 7 Nov 2024 10:13:01 +0000
First a quick note on motivation:
Yesterday I was debugging a main program that loads a plugin at
runtime. The main program passed a vector by reference to the plugin,
the plugin populated the vector, and then the main program worked with
the vector contents. It wasn't working properly and I eventually
figured out that the plugin was compiled with LLVM clang++ while the
main program was compiled with GNU g++. The container type
"std::vector" isn't ABI-compatible between the two (or at least not
between the two compiler versions in question).

I thought it might be nice to be able to check this at runtime, something like:

    namespace std {
        template<typename T>
        consteval size_t const *layout_of(void);
    }

This function returns a pointer to a null-terminated sequence of
size_t's as follows:

    { alignment, size, alignment, size, alignment, size, alignment,
size, . . . , null terminator }

I'm thinking that the 'size' parameter would be '__datasizeof' rather
than 'sizeof'.

Using the following struct as an example:

    struct Monkey {
        int a;
        char b;
        char *p;
        alignas(32) char c;
    };

So then std::layout_of<Monkey> would come back with:

    { 4, 4, 1, 1, 8, 8, 32, 1 }

By the way if it were 'sizeof' instead of '__datasizeof', then it would be:

    { 4, 4, 1, 1, 8, 8, 32, 32 }

So then the main program, when it loads a plugin, could compare its
own vector to the vector in the plugin, something like:

    #include <cstddef> // size_t
    #include <cstdlib> // EXIT_FAILURE
    #include <vector> // vector
    using std::size_t;
    extern size_t const *GetVectorLayoutFromPlugin(void); // defined
in the plugin
    int main(void)
    {
        constexpr size_t const *domestic = std::layout_of<
std::vector<void*> >();
        size_t const *const exotic = GetVectorLayoutFromPlugin();

        if ( 0 != std::char_traits<size_t>::compare( domestic, exotic
) ) return EXIT_FAILURE;
    }

Of course there are a few more things to consider such as:
(1) Should the sequence be prefixed with 1 byte to indicate the size
of "size_t" -- just in case it's 4 on one compiler and 8 on another?
(2) It's possible for two classes to have identical layout but to
still have differing and incompatible implementations.
(3) Should the pointer to the VTable have a special marking? I was
thinking that the 'alignment' parameter could be negative for the
VTable pointer, so if you had:

    struct Donkey {
        virtual ~Donkey(void) = default;
        int a;
    };

then it would be:

    { -8, 8, 4, 4 }

This would be useful in particular when dealing with the Microsoft
compiler, as the VTable pointer might not be the first item if you
have a polymorphic class inheriting from a non-polymorphic class, such
as the following:

    struct Base {
        int a;
    };

    struct Derived : Base {
        virtual ~Derived(void) = default;
    };

So then on the Microsoft compiler, std::layout_of<Derived>() would
come back with:

    { 4, 4, -8, 8 }

where as on every other C++ compiler in existence, it would be:

    { -8, 8, 4, 4 }

Received on 2024-11-07 10:13:09