you can mark an exported C++ function with extern "C"
so it can be called by another compiler, however you usually cannot pass std
types because their layout depends on the compiler, another method is virtual functions whose ABI is stable and don't need to be marked extern "C"
Is it safe to pass or return std::span<T>
across compiler boundaries ? the cppref page says
A typical implementation holds a pointer to T, if the extent is dynamic, the implementation also holds a size.
- is there any guarantee that its layout will be stable between different compilers ?
- is its layout at least practically safe to be used between the commonly available implementations (MSVC, libstdc++, libc++, ?????) ?
or should i just create a dummy non-templated struct that holds T*
and size_t
to be able to pass or return it safely from extern "C"
functions.
you can mark an exported C++ function with extern "C"
so it can be called by another compiler, however you usually cannot pass std
types because their layout depends on the compiler, another method is virtual functions whose ABI is stable and don't need to be marked extern "C"
Is it safe to pass or return std::span<T>
across compiler boundaries ? the cppref page says
A typical implementation holds a pointer to T, if the extent is dynamic, the implementation also holds a size.
- is there any guarantee that its layout will be stable between different compilers ?
- is its layout at least practically safe to be used between the commonly available implementations (MSVC, libstdc++, libc++, ?????) ?
or should i just create a dummy non-templated struct that holds T*
and size_t
to be able to pass or return it safely from extern "C"
functions.
2 Answers
Reset to default 12is there any guarantee that its layout will be stable between different compilers ?
No. There is no such guarantee between anything at all whatsoever in C++.
(The Common C++ ABI makes a few guarantees, but not for the layout of any particular std types.)
is its layout at least practically safe to be used between the commonly available implementations
No. There is nothing that is safe to be used between MSVC and anything else in general, and basically nothing between libc++ and libstdc++. The only thing that is somewhat safe is to use libstdc++ and mix Clang- and GCC-compiled object files. But I would avoid even this.
from conducting tests using MSVC, gcc with libstdc++, and clang with libc++ on windows.
std::span<T>
has the same layout in MSVC, libc++ and libstdc++, and on windows you can pass it as a parameter to an extern "C"
functions without crashing between the 3 compilers.
But std::span<T>
is treated as a C++ object even though it is trivial, so it has different return ABI between MSVC and gcc and clang. gcc and clang have the same return ABI that is different from MSVC. so you cannot return it from an extern "C"
function, in fact MSVC will prevent it from compiling, and if you force the compilation you get a malformed return value, and will likely crash.
the following function compiles and works well between the 3 compilers on windows with different standard libraries (MSVC, libstdc++, libc++)
extern "C" __declspec(dllexport)
int print_span(std::span<int> in, std::span<int>& out)
{
for (const auto& val:in)
{
std::cout << val << '\n';
}
out = in;
return 0;
}
even debug mode doesn't change its layout, so no strange surprises so far.