Date: Thu, 7 Nov 2024 09:29:38 -0600
Hi,
This is an interesting idea. It a tough problem but a problem that would be
nice to solve.
In general this is of course something that should be diagnosed at link
time (and it usually is in the case of a libc++ vs libstdc++ mismatch
albeit not super clearly). The plugin case is special. As others have
mentioned, simply seeing the list of field sizes isn’t enough though.
My suggestion: Rrquire plugins to publish a C string with some identifier
indicating the standard library being used (e.g. "libstdc++" or "libcxx"
depending on preprocessor macros set by the libraries). Then check that on
load.
Jeremy
On Thu, Nov 7, 2024 at 04:13 Frederick Virchanza Gotham via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> 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 }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
This is an interesting idea. It a tough problem but a problem that would be
nice to solve.
In general this is of course something that should be diagnosed at link
time (and it usually is in the case of a libc++ vs libstdc++ mismatch
albeit not super clearly). The plugin case is special. As others have
mentioned, simply seeing the list of field sizes isn’t enough though.
My suggestion: Rrquire plugins to publish a C string with some identifier
indicating the standard library being used (e.g. "libstdc++" or "libcxx"
depending on preprocessor macros set by the libraries). Then check that on
load.
Jeremy
On Thu, Nov 7, 2024 at 04:13 Frederick Virchanza Gotham via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> 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 }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2024-11-07 15:29:54