Safety of casting between pointers of two identical classes?
Asked Answered
B

5

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).

Bib answered 14/10, 2011 at 4:9 Comment(0)
G
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.

Gorge answered 14/10, 2011 at 4:15 Comment(4)
Another well-defined way to do these conversions is to use a union with a common initial sequence (as allowed by §9.2/19).Noachian
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.Bib
@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.Gorge
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.Tintometer
T
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.

Tintometer answered 14/10, 2011 at 4:14 Comment(1)
Right you are, my mistake! Corrected the questionBib
O
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.

Osugi answered 14/10, 2011 at 4:17 Comment(1)
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.Bib
C
1

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.

Carryingon answered 14/10, 2011 at 4:23 Comment(0)
C
0

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

So far as I can tell, every implementation can be configured in such a manner as to process such code reliably. The Standard allows conforming implementations to also support configurations that would not process such code reliably, and some configurations of both clang and gcc behave in that manner. Note the following from the C++ Standard:

Although this document states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs.

If a configuration will be used for tasks that wouldn't benefit from an ability to treat layout-compatible types interchangeably, the Standard would allow it to perform optimizations that would break code that uses such types interchangeably. While the Standard might not officially recognize a category of configurations that treat such types interchangeably, compiler writers have always offered such configurations anyhow, and the Standard's failure to acknowledge them is likely an effort to avoid expending ink on an optional feature that was already universally supported on every implementation where it might be useful.

Coatee answered 11/9, 2023 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.