Here's some code which tries to "recreate" floats by passing through char
buffer:
#include <memory.h>
#include <array>
#include <iostream>
#include <span>
#include <vector>
struct A {
std::span<const char> GetAsSpan() const {
return std::span{reinterpret_cast<const char*>(f_arr.data()),
f_arr.size() * sizeof(float)};
}
std::vector<char> GetAsVector() const {
std::vector<char> vec(f_arr.size() * sizeof(float));
for (int i = 0; i < f_arr.size(); ++i) {
char* buffer = vec.data() + i * sizeof(float);
memcpy(buffer, &f_arr[i], sizeof(float));
}
return vec;
}
std::array<float, 2> f_arr{0.1, 0.2};
};
void Print(const std::vector<float>& f_vec) {
for (const auto f : f_vec) {
std::cout << f << ' ';
}
std::cout << '\n';
}
int main() {
A a;
{
// BLOCK 1: CORRECT
const auto sp = a.GetAsSpan();
// The original backing array is actually of floats, so reinterpret_cast
// should work.
std::vector<float> f_vec1;
const size_t n_elems1 = sp.size() / sizeof(float);
for (auto i = 0u; i < n_elems1; ++i) {
const char* buffer = sp.data() + i * sizeof(float);
const float v = *reinterpret_cast<const float*>(buffer);
f_vec1.push_back(v);
}
Print(f_vec1);
}
{
// BLOCK 2: UNDEFINED BEHAVIOR
const auto vec = a.GetAsVector();
// The original backing array is "NOT" floats, so reinterpret_cast should
// be UB.
std::vector<float> f_vec2;
const size_t n_elems2 = vec.size() / sizeof(float);
for (auto i = 0u; i < n_elems2; ++i) {
const char* buffer = vec.data() + i * sizeof(float);
// UB:
const float v = *reinterpret_cast<const float*>(buffer);
f_vec2.push_back(v);
}
Print(f_vec2);
}
{
// BLOCK 3: CORRECT
const auto vec = a.GetAsVector();
std::vector<float> f_vec3;
const size_t n_elems2 = vec.size() / sizeof(float);
for (auto i = 0u; i < n_elems2; ++i) {
const char* buffer = vec.data() + i * sizeof(float);
float v = 0.f;
// memcpy should remove the UB.
memcpy(&v, buffer, sizeof(float));
f_vec3.push_back(v);
}
Print(f_vec3);
}
}
I wanted to confirm that:
- BLOCK 1: The
reinterpret_cast
tofloat
over here is correct, since the original backing array is of typefloat
. - BLOCK 2: The
reinterpret_cast
over here is "incorrect" (undefined behavior, if that value "v" is accessed later), since the original backing vector is of typechar
. - BLOCK 3: The
memcpy
being used removes the undefined behavior which was showing in BLOCK 2.