3

When using C-libraries it might be appropopriate to derive a class from a C-structure and add some methods to operate on it without any data-members. F.e. you could add a constructor to initialize the members more conveniently. So this objects might be implicitly upcasted and passed to the C-APIs.

There might be cases where the API expects an array of the C-structures. But is there any guarantee of the C++-language that the derived objectds have the same size as the base-struct so that the distances between the objects are properly offsetted?

BTW: None of the suggestions of similar questions matches my question.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
Bonita Montero
  • 2,817
  • 9
  • 22
  • 2
    It's a good question. However, I question the intended use. You can create a non-member function to construct a fully initialized C-struct without having to derive from it and using the derived class's constructor to initialize the members of the C-struct. – R Sahu Sep 04 '19 at 17:10
  • 2
    [Here's](https://stackoverflow.com/a/32759314/485343) an authoritative answer regarding C/C++ layout compatibility. Then you just need to take care to maintain *standard layout*, e.g. not use virtual members, virtual inheritance etc. – rustyx Sep 04 '19 at 17:18
  • Have you actually tried doing this? And, if so, have you thought of maybe putting a checking directive in your code, like comparing `sizeof(base)` to `sizeof(derived)` (both should be compile-time constants)? – Adrian Mole Sep 04 '19 at 17:32
  • Adrian, what rustyx links to does't tell anything about the issue of a possible size-increase of a derived class without any additional data-members. – Bonita Montero Sep 04 '19 at 17:51

2 Answers2

3

In general, there is no such guarantee. And in particular, if you introduce virtual member functions for example, then there would typically be additional memory used for the virtual table pointer.

If we add an additional assumption that the derived class is standard layout, and no non-standard features such as "packing" is used, then the size would be the same in practice.

However, even if the size is the same, you technically cannot pretend that an array of derived type is an array of base type. In particular, iterating the "pretended" array with a pointer to base would have undefined behaviour. At least that's how it is within C++. Those operations are presumably performed in C across the API. I really don't know what guarantees there are in that case.


I would recommend that if you need to deal with arrays of the C struct (i.e. the pointer would be incremented or subscripted by the API), then instead of wrapping the individual struct, create a C++ wrapper for the entire array.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

But is there any guarantee of the C++-language that the derived objectds have the same size as the base-stuct

In general I would expect, that the class will not add any additional to the class memory layout, as long you did not introduce new data members or virtual functions. Use of virtual functions results in adding the v-table pointer.

The implementation is also free to add a v-table pointer if you use virtual inheritance. This will also change the layout for most compilers ( clang and c++ use a vtable in that case! )

But this all is implementation specific and I did not know of a guarantee in the C++ standards which defines that the class layout will guarantee that you can use a derived class without a cast operation as the base class.

You also have to think of padding of data structures which may be different for the derived class.

Generate something ( the derived class ) and use it as something different ( the base struct ) is in general undefined behavior. We are not talking of cast operations! If you cast the derived class to the base class, everything is fine. But packing many instances into a derived class array and simply use it as a base class array is undefined behavior.

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Why should the derived class add any padding? The base structure will be padded according to the language-settings already set. – Bonita Montero Sep 04 '19 at 17:22
  • @BonitaMontero: The implementation *may* have different padding rules for C and C++. The OP asks especially for using c++ derived class in combination with C library functions. As said: I would not expect that, but it is also possible. BTW: It is undefined behavior and this point is only a possible reason! – Klaus Sep 04 '19 at 17:23
  • 1. I'm the OP. 2. As you can't mark C-structs included in C++-sourcefiles as C-specific there can't be different padding rules. – Bonita Montero Sep 04 '19 at 17:26
  • @BonitaMontero: We discuss how compilers *may* be constructed. It is, whatever reason we find from the c++ standard undefined behavior. You use an instance of one type as an instance of another one. UB! Technically I believe it will work perfectly as long you use no additional data members nor virtual inheritance and functions. – Klaus Sep 04 '19 at 17:40
  • @BonitaMontero: What should I write? You got two answers with more or less the same content: What you want to do is UB. We can continue to discuss what the potential reasons can be behind the implementation details of the compiler. But it will not help. It is still UB but will work in most cases... – Klaus Sep 04 '19 at 18:17
  • Klaus, a structure defined in a C-header included in a C++ translation-unit isn't handled differently as any other structure. And even C-structures included into a C-module have have a compatible layout with C++ to enable interoperability. – Bonita Montero Sep 04 '19 at 18:36