2

Say I have some data allocated somewhere in my program, like:

some_type a;

and I want to wrap this data in a class for access. Is it valid to say,

class Foo {
private:
    some_type _val;
public:
    inline void doSomething() { c_doSomething(&_val); }
}

Foo *x = reinterpret_cast<Foo *>(&a);
x->double();

The class has no virtual functions, and only includes a single data item of the type I'm trying to wrap. Does the C++ standard specify that this reinterpret_cast is safe and valid? sizeof(Foo) == sizeof(some_type), no address alignment issues, or anything? (In my case, I'd be ensuring that some_type is either a primitive type like int, or a POD structure, but I'm curious what happens if we don't enforce that restriction, too - for example, a derived class of a UIWidget like a UIMenuItem, or something.)

Thanks!

Aidan Cully
  • 5,457
  • 24
  • 27

5 Answers5

4

Is it valid to say...

No, this is not valid. There are only a small number of types that a can be treated as; the complete list can be found in an answer I gave to another question.

Does the C++ standard specify that this reinterpret_cast is safe and valid?

The C++ Standards says very little about reinterpret_cast. Its behavior is almost entirely implementation-defined, so use of it is usually non-portable.

The correct way to do this would be to either

  • have a Foo constructor that takes a some_type argument and makes a copy of it or stores a reference or pointer to it, or

  • implement your "wrapper" interface as a set of non-member functions that take a some_type object by reference as an argument.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • I'm not so great at standard-ese, but reading through the list you gave in that answer, I see "an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)", which seems like it might apply here - the only storage that should be accessed should be at the same address as `a`, and this is of the same dynamic type as `a`, right? – Aidan Cully Feb 08 '11 at 21:58
  • 1
    Technically the class is not an aggregate because it has access specifiers. That let's the compiler off the C compatibility clauses and allows it to select any layout for the class. – Bo Persson Feb 08 '11 at 22:31
  • @Aidan: That's not what the dynamic type is. The dynamic type of `a` is `some_type`. Since `a` is declared as an object of type `some_type`, its dynamic type and static type will both always be `some_type`. The dynamic type is the actual type of the object. The static type is the type named in the source code. The only time they can be different is when you have pointers or references (e.g., in `Base* p = new Derived;`, the dynamic type of `*p` is `Derived` but the static type is `Base`). – James McNellis Feb 08 '11 at 22:32
  • If the class `Foo` was an aggregate (i.e., if you make all the data members public and have no constructors, base classes, or virtual functions), then yes, you would be okay. If you did that, I would use a pair of static casts, though (`static_cast(static_cast(&a))`) to avoid the `reinterpret_cast`. That said, either of the recommendations in my answer would make for much cleaner code. – James McNellis Feb 08 '11 at 22:35
  • @Aidan: Also, good catch on the aggregate/union clause. I had never needed to use that "feature" and thus I wasn't really thinking about that. – James McNellis Feb 08 '11 at 22:38
  • @Bo, thanks for that - this probably also explains why I had `g++` complaining about using `offsetof` against such classes. – Aidan Cully Feb 09 '11 at 02:44
  • @James, thank you - I'm still learning C++. I appreciate the ideas, but I think this is the easiest way to implement my C++ wrappers - the `some_type` is embedded within a larger structure, generated from a different source language (a Ruby internal DSL), and I want to access the fields without having to allocate any data for it, or construct any new objects. – Aidan Cully Feb 09 '11 at 02:49
2

14882/1998/9.2.17:

"A pointer to a PODstruct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bitfield, then to the unit in which it resides) and vice versa. [Note: There might therefore be unnamed padding within a PODstruct object, but not at its beginning, as necessary to achieve appropriate alignment. ]"

So, it would be valid if your wrapper was strictly a POD in itself. However, access specifiers mean that it is not a strictly a POD. That said, I would be interested in knowing whether any current implementation changes object layout due to access specifiers. I think that for all practical purposes, you are good to go.

And for the case when the element is not a POD, it follows that the container is not a POD, and hence all bets are off.

Keith
  • 6,756
  • 19
  • 23
1

Since your Foo object is already only valid as long as the existing a is valid:

struct Foo {
  some_type &base;

  Foo(some_type &base) : base (base) {}

  void doSomething() { c_doSomething(&base); }
}

//...
Foo x = a;
x.doSomething();
Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
1

You want to look up the rules governing POD (plain old data) types. If the C++ class is a POD type, then yes, you can cast it.

The details of what actually happens and how aliasing is handled are implementation defined, but are usually reasonable and should match what would happen with a similar C type or struct.

I happen to use this a lot in a project of mine that implements B+ trees in shared memory maps. It has worked in GCC across multiple types of Linux and BSDs including Mac OS X. It also works fine in Windows with MSVC.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
0

Yes, this is valid as long as the wrapper type you are creating (Foo in your example) is a POD-type.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226