10

Let's say I have two different classes, both represent 2D coordinate data in the same internal way like the following:

class LibA_Vertex{
    public:
    // ... constructors and various methods, operator overloads
    float x, y
};

class LibB_Vertex{
    public:
    // ... same usage and internal data as LibA, but with different methods
    float x, y
};


void foobar(){
    LibA_Vertex * verticesA = new LibA_Vertex[1000];
    verticesA[50].y = 9;
    LibB_Vertex * verticesB = reinterpret_cast<LibB_Vertex*>( vertexA );
    print(verticesB[50].y); // should output a "9"
};

Given the two classes being identical and the function above, can I reliably count on this pointer conversion working as expected in every case?

(The background, is that I need an easy way of trading vertex arrays between two separate libraries that have identical Vertex classes, and I want to avoid needlessly copying arrays).

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101

4 Answers4

13

C++11 added a concept called layout-compatible which applies here.

Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).

where

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class.

A standard-layout union is a standard-layout class defined with the class-key union.

Finally

Pointers to cv-qualified and cv-unqualified versions (3.9.3) of layout-compatible types shall have the same value representation and alignment requirements (3.11).

Which guarantees that reinterpret_cast can turn a pointer to one type into a pointer to any layout-compatible type.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 3
    Another well-defined way to do these conversions is to use a union with a common initial sequence (as allowed by `§9.2/19`). – Mankarse Oct 14 '11 at 04:24
  • Oh! Well, C++11 to the rescue. I just hope this was among the things VS2010 decided to add when it went cherry picking through the standard. – Anne Quinn Oct 14 '11 at 04:34
  • 1
    @Clairvoire: This is one of the things that always worked in practice, even though being forbidden formally. I don't expect that any compiler writers had to "add" support. – Ben Voigt Oct 14 '11 at 04:35
  • 2
    It wasn't ever "forbidden", it was just "undefined behaviour". It was just that the actual behaviour in every compiler I know of was to work as you'd expect. C++11 just basically defined the behaviour that every compiler was using anyway. – Ayjay Oct 14 '11 at 04:55
1

I would wrap that conversion up in a class (so that if you need to change platform or something, it's at least localised in one spot) but yes it should be possible.

You'll want to use reinterpret_cast, not static_cast as well.

Ayjay
  • 3,413
  • 15
  • 20
1

Theoretically this is an undefined behavior. However, it may work in certain systems/platforms.

I would suggest that you should try to merge 2 classes into 1. i.e.

class Lib_Vertex{
// data (which is exactly same for both classes)
public:
// methods for LibA_Vertex
// methods for LibB_Vertex
};

Adding methods into a class will not affect its size. You may have to change your design a bit but it's worth it.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Ordinarily, I would use something like this too. I'd still have to make copies of the arrays though. One library (2d physics lib) returns entire arrays of vertices using it's internal Vertex class, which I then need to feed into the other library (2d rendering lib) which accepts arrays of it's internal Vertex class. – Anne Quinn Oct 14 '11 at 04:31
0

Technically this is undefined behavior. In reality, if the same compiler was used to compile both classes, they'll have the same layout in memory if the fields are declared in the same order, have the same types and the same access level.

Oscar Korz
  • 2,457
  • 1
  • 18
  • 18