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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With