1

X is defined as the following:

struct X
{
    Y y;
    // more fields...
    int a;
};

I have a variable of type X. However, I would like to cast it to the type of its first member, in order to pass that into a function. I know that the C Standard permits it (and I suppose the C++ one does so as well).

In C I would do it like so:

X x;
Y* y = (Y*) x;
doStuff(y);

What type of cast is the right one in C++ for this? static_cast or reinterpret_cast?

Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131
  • Why would you want to do such a thing? – Fred Larson Dec 07 '20 at 21:05
  • `static_cast` doesn't compile for this – Collin Dec 07 '20 at 21:06
  • 1
    *as the standard permits* The standard does not permit this. – NathanOliver Dec 07 '20 at 21:08
  • @FredLarson In this case, I have a `PROCESS_MEMORY_COUNTERS_EX` variable. However the WinAPI function `GetProcessMemoryInfo` takes a `PROCESS_MEMORY_COUNTERS*`. The former type starts with the exact same fields as the latter, and adds a few at the end. The intended usage is to pass into the function a pointer to the latter type, even if we hold a pointer to the former (larger) type. – Aviv Cohn Dec 07 '20 at 21:09
  • @NathanOliver You're right, I meant to say something a bit different. Please see the updated question. – Aviv Cohn Dec 07 '20 at 21:11
  • @FredLarson: The linux kernel uses something like this (in C, obviously) to handle a lot of the driver structures: https://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-the-linux-kernel. – Collin Dec 07 '20 at 21:12
  • like [this](https://stackoverflow.com/questions/32698298/getprocessmemoryinfo-process-memory-counters-ex-privateusage-always-0)? – Thomas Dec 07 '20 at 21:13
  • @NathanOliver how about pointer interconvertibility between a pointer to the structure and a pointer to its first element? – Fureeish Dec 07 '20 at 21:17
  • @Fureeish Yep that's what I meant. Updated the question accordingly. – Aviv Cohn Dec 07 '20 at 21:18
  • 1
    @Fureeish They might be okay with that, but that caveat only applies to standard layout classes. Not sure if `// more fields...` leaves `Y` as standard layout or not. – NathanOliver Dec 07 '20 at 21:19
  • @AvivCohn I think your question doesn't quite express what you want to do. All you would need to do to do the "equivalent" of your C code is `doStuff(&x.y)`, which I'm pretty sure you would have done if that was a possibility for you. –  Dec 07 '20 at 21:21
  • Unrelated Typo: `Y* y = (Y*) x;` -> `Y* y = (Y*) &x;` – user4581301 Dec 07 '20 at 21:21
  • `doStuff(&x.y);` looks to be more suitable than anything else you could do here. Better example required? – user4581301 Dec 07 '20 at 21:23
  • 1
    Why not use the address of the first element, if that's what you're trying to do. This is very strange & dangerous. – Joseph Larson Dec 07 '20 at 21:25
  • Used to do this all the time C. For example, if you put the linked list node structure as the first member of another struct, you have a sort-of poor-man's polymporphism. The casting is usually done on the way out, though. Can blow up in your face very badly in C++. – user4581301 Dec 07 '20 at 21:40

3 Answers3

4

None.

You can't mess around with objects using pointers like that. C++ is not C, and these are not "just bytes" (contrary to popular belief).

And you don't need to!

Pass &x.y instead; it's already the Y* you want.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
  • 1
    Apart from the obvious typo, `reinterpret_cast` is most likely the correct cast. It's not none. – Passer By Dec 08 '20 at 00:03
  • Hi, thank you for your answer. Since this original question was not properly phrased, I opened a new one more accurately explains the question. Essentially, the question should have been written to say simply that `Y` starts with the exact same layout as `X`. But it does not contain an `X` directly. Please see updated question: https://stackoverflow.com/questions/65196623/what-is-the-correct-way-to-cast-a-struct-to-a-type-that-matches-its-first-set-of – Aviv Cohn Dec 08 '20 at 09:52
2

I'd always recommend using static_cast instead of reinterpret_cast in any situation where the static_cast isn't rejected by the compiler. If possible try to avoid doing any casting at all - in this case you probably want: Y* y = &x.y.

To answer the comment:

In this case, I have a PROCESS_MEMORY_COUNTERS_EX variable. However the WinAPI function GetProcessMemoryInfo takes a PROCESS_MEMORY_COUNTERS*. The former type starts with the exact same fields as the latter, and adds a few at the end. The intended usage is to pass into the function a pointer to the latter type, even if we hold a pointer to the former (larger) type.

The documentation for GetProcessMemoryInfo() states that the second parameter is:

A pointer to the PROCESS_MEMORY_COUNTERS or PROCESS_MEMORY_COUNTERS_EX structure that receives information about the memory usage of the process.

The Win32 API is a C API, and not a C++ one, so you can just use a C style cast here, or preferably a reinterpret_cast to make your intention clearer. I'd expect static_cast to be rejected by the compiler in this case. Note that the third cb parameter is there to tell the function which type of structure you actually provided - it should be set to either sizeof(PROCESS_MEMORY_COUNTERS) or sizeof(PROCESS_MEMORY_COUNTERS_EX).

Adam
  • 882
  • 5
  • 10
0

@FredLarson In this case, I have a PROCESS_MEMORY_COUNTERS_EX variable. However the WinAPI function GetProcessMemoryInfo takes a PROCESS_MEMORY_COUNTERS*. The former type starts with the exact same fields as the latter, and adds a few at the end. The intended usage is to pass into the function a pointer to the latter type, even if we hold a pointer to the former (larger) type.

The cleanest way to accomplish that would be probably (ab)using inheritance. This way, you can have your _EX type share members with the base type while actually being an instance of it.

struct X {
  int a;
  int b;
};

struct X_EX : public X {
  int other_member;
};

void doStuff(X*);

void foo(X_EX* ptr) {
 doStuff(ptr);
}

However, do note that "Inherit-to-extend" is seen as a code smell, and something to avoid if possible nowadays. I'd make sure to put a comment explaining why it's necessary here.