Specifically, I am wrapping a C API in a friendly C++ wrapper. The C API has this fairly standard shape:
struct foo {...};
void get_foos(size_t* count, foo* dst);
And what I'd like to do, is save myself an extra copy by passing a typed-punned wrapper array directly to the C api with a bunch of sanity checking static_assert()
.
class fooWrapper {
foo raw_;
public:
[...]
};
std::vector<fooWrapper> get_foo_vector() {
size_t count = 0;
get_foos(&count, nullptr);
std::vector<fooWrapper> result(count);
// Is this OK?
static_assert(sizeof(foo) == sizeof(fooWrapper), "");
static_assert(std::is_standard_layout<fooWrapper>::value, "");
get_foos(&count, reinterpret_cast<foo*>(result.data()));
return result;
}
My understanding is that it is valid code, since all accessed memory locations individually qualify under the rule, but I'd like confirmation on that.
Edit: Obviously, as long as reinterpret_cast<char*>(result.data() + n) == reinterpret_cast<char*>(result.data()) + n*sizeof(foo)
is true, it'll work under all major compilers today. But I'm wondering if the standard agrees.
First, this is not type punning. The reinterpret_cast
you're doing is just an over-written way of doing &result.data().foo_
. Type punning is accessing an object of one type through a pointer/reference to another type. You're accessing a subobject of the other type.
Second, this doesn't work. Pointer arithmetic is based on having an array (a single object acts as an array of 1 element for the purposes of pointer arithmetic). And vector<T>
is defined by fiat to produce an array of T
s. But an array of T
is not equivalent to an array of some subobject of T
, even if that subobject is the same size as T
and T
is standard layout.
Therefore, if get_foos
performs pointer arithmetic on its given array of foo
s, that's UB. Oh sure, it will almost certainly work. But the language's answer is UB.