Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I cast an array of POD which has floats to float*?

Consider the following:

#include <vector>
using namespace std;

struct Vec2
{
  float m_x;
  float m_y;
};

vector<Vec2> myArray;

int main()
{
  myArray.resize(100);

  for (int i = 0; i < 100; ++i)
  {
    myArray[i].m_x = (float)(i);
    myArray[i].m_y = (float)(i);
  }

  float* raw;
  raw = reinterpret_cast<float*>(&(myArray[0]));
}

Is raw guaranteed to have 200 contiguous floats with the correct values? That is, does the standard guarantee this?

EDIT: If the above is guaranteed, and if Vec2 has some functions (non-virtual) and a constructor, is the guarantee still there?

NOTE: I realize this is dangerous, in my particular case I have no choice as I am working with a 3rd party library.

like image 301
Samaursa Avatar asked Nov 04 '12 22:11

Samaursa


2 Answers

I realize this is dangerous, in my particular case I have no choice as I am working with a 3rd party library.

You may add compile time check of structure size:

live demo

struct Vec2
{
    float a;
    float b;
};

int main()
{
        int assert_s[ sizeof(Vec2) == 2*sizeof(float) ? 1 : -1 ];
}

It would increase your confidence of your approach (which is still unsafe due to reinterpret_cast, as mentioned).


raw = reinterpret_cast(&(myArray[0]));

ISO C++98 9.2/17:

A pointer to a POD struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. —end note ]


And finally, runtime check of corresponding addresses would make such solution rather safe. It can be done during unit-tests or even at every start of program (on small test array).

Putting it all together:

live demo

#include <vector>
#include <cassert>
using namespace std;
struct Vec2
{
    float a;
    float b;
};

int main()
{
    int assert_s[ sizeof(Vec2) == 2*sizeof(float) ? 1 : -1 ];
    typedef vector<Vec2> Vector;
    Vector v(32);
    float *first=static_cast<float*>(static_cast<void*>(&v[0]));
    for(Vector::size_type i,size=v.size();i!=size;++i)
    {
        assert((first+i*2) == (&(v[i].a)));
        assert((first+i*2+1) == (&(v[i].b)));
    }
    assert(false != false);
}
like image 85
Evgeny Panasyuk Avatar answered Nov 10 '22 13:11

Evgeny Panasyuk


No, this is not safe, because the compiler is free to insert padding between or after the two floats in the structure, and so the floats of the structure may not be contiguous.

If you still want to try it, you can add compile time checks to add more surety that it will work:

static_assert(sizeof(Vec2) == sizeof(float) * 2, "Vec2 struct is too big!");
static_assert(offsetof(Vec2, b) == sizeof(float), "Vec2::b at the wrong offset!");
like image 23
Seth Carnegie Avatar answered Nov 10 '22 13:11

Seth Carnegie