7

Is it possible to write a C++ class or struct that is fully compatible with C struct. From compatibility I mean size of the object and memory locations of the variables. I know that its evil to use *(point*)&pnt or even (float*)&pnt (on a different case where variables are floats) but consider that its really required for the performance sake. Its not logical to use regular type casting operator million times per second.

Take this example

Class Point {
    long x,y;
    Point(long x, long y) {
        this->x=x;
        this->y=y;
    }

    float Distance(Point &point) {
        return ....;
    }
};

C version is a POD struct

struct point {
    long x,y;
};
Cem Kalyoncu
  • 14,120
  • 4
  • 40
  • 62
  • For what purpose do you want this compatibility? Do you want to pass binary data from a c++ program to a c program, or do you want to pass a c++ object to a c function? – Dima Sep 15 '09 at 20:39
  • 4
    *"but consider that its really required for the performance sake. Its not logical to use regular type casting operator million times per second."* This makes no sense to me. – GManNickG Sep 15 '09 at 20:45
  • @Dima: to C function @GMan: non-evil method should define type casting operator operator point() { point p; p.x=this->x; p.y=this.y; return p; } However, I will be using these points in two cases int typed to pass windows function to find cursor pos. Float typed is sent to OpenGL as vertices (while it is converted to float array), first one is executed one per each frame (350 times/sec), other is for 4 x objects drawn x 350. They will be used within a game engine. – Cem Kalyoncu Sep 15 '09 at 22:33

7 Answers7

21

The cleanest was to do this is to inherit from the C struct:

struct point
{
    long x, y;
};

class Point : public struct point
{
  public:
    Point(long x, long y)
        {    this->x=x; this->y=y; }

    float Distance(Point &point)
        {                return ....;        }
}

The C++ compiler guarantees the C style struct point has the same layout as with the C compiler. The C++ class Point inherits this layout for its base class portion (and since it adds no data or virtual members, it will have the same layout). A pointer to class Point will be converted to a pointer to struct point without a cast, since conversion to a base class pointer is always supported. So, you can use class Point objects and freely pass pointers to them to C functions expecting a pointer to struct point.

Of course, if there is already a C header file defining struct point, then you can just include this instead of repeating the definition.

Stephen C. Steel
  • 4,380
  • 1
  • 20
  • 21
  • This is what I have been found. Thanks! – eonil Mar 17 '11 at 05:19
  • This can lead to horrible messes, as the struct members are public. It basically utterly breaks any encapsulation. – Fake Name Jun 15 '15 at 23:40
  • 1
    @FakeName That's inherent when C is involved, see http://stackoverflow.com/questions/2522424/is-there-any-workaround-for-making-a-structure-member-somehow-private-in-c And, you can obtain encapsulation at least the C++ Point if you do your inheritance `protected` in place of `public`. See also https://isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c – Antonio Oct 06 '16 at 12:45
10

Yes.

  • Use the same types in the same order in both languages
  • Make sure the class doesn't have anything virtual in it (so you don't get a vtable pointer stuck on the front)
  • Depending on the compilers used you may need to adjust the structure packing (usually with pragmas) to ensure compatibility.

(edit)

  • Also, you must take care to check the sizeof() the types with your compilers. For example, I've encountered a compiler that stored shorts as 32 bit values (when most will use 16). A more common case is that an int will usually be 32 bits on a 32-bit architecture and 64 bits on a 64-bit architecture.
Jason Williams
  • 56,972
  • 11
  • 108
  • 137
  • 1
    In my experience pack pragmas are a bad idea. If you order the member variables such that they have natural alignment, then pack pragmas aren't necessary. And if you *don't* order them to have natural alignment and attempt to use pragmas to fix it, you can run into two problems: 1) The pragmas still won't work 2) You will force the compiler to misalign something the processor doesn't want misaligned, like floating point numbers. On PowerPC at least, a misaligned float will generate an interrupt that gets handled by software, that can *significantly* impact performance. – KeyserSoze Sep 15 '09 at 22:34
  • Defining a C++ object with the same layout as the C struct works, but it doesn't experess the intent as clearly as having the class interet from the struct (see my answer below). – Stephen C. Steel Sep 15 '09 at 23:18
  • @keysersoze: Yes, natural alignment is better. However, some compilers will still pack data differently, which is where it's useful to mention packing. Indeed, I forgot to mention taking care with member sizes too. I'll edit my post to add that point. – Jason Williams Sep 16 '09 at 07:10
  • The word you're looking for is POD types. They're clearly defined in the standard, and basically their entire purpose is to be compatible with C. – jalf Sep 16 '09 at 10:00
  • @jalf: Yes. I saw no point in repeating keysersoze's good post about POD. I simply meant that (in a general sense) all you need to verify is that the specific compilers you are using produce identical binary structures. I'd test my code to verify this regardless of what types I used. – Jason Williams Sep 16 '09 at 11:39
  • Just noticed your comment re: sizeof()... Are you aware of stdint.h? http://en.wikipedia.org/wiki/Stdint.h "Both C and C++ developers should know that it is important to update their coding standards even if the compiler is not C99 compliant because a version of stdint.h (for C), and a version of stdint.h and cstdint (for C++) can be downloaded or quickly created." – KeyserSoze Sep 16 '09 at 20:22
5

POD applies to C++. You can have member functions. "A POD type in C++ is an aggregate class that contains only POD types as members, has no user-defined destructor, no user-defined copy assignment operator, and no nonstatic members of pointer-to-member type"

You should design your POD data structures so they have natural alignment, and then they can be passed between programs created by different compilers on different architectures. Natural alignment is where the memory offset of any member is divisible by the size of that member. IE: a float is located at an address that is divisible by 4, a double is on an address divisible by 8. If you declare a char followed by a float, most architectures will pad 3 bytes, but some could conceivably pad 1 byte. If you declare a float followed by a char, all compilers (I ought to add a source for this claim, sorry) will not pad at all.

KeyserSoze
  • 2,501
  • 1
  • 16
  • 17
  • There's no guarantee that the compiler won't insert extra padding, so you can't be *sure* it'll work unless you use a pack pragma or similar. – jalf Sep 16 '09 at 10:01
  • I've never seen a compiler that inserted padding for structures that have natural alignment. VC++ 6.0, 2003, 2005, 2008, Metrowerks for PPC, Metrowerks for HC12, gcc for ARM, gcc 2.98-3.x for PowerPC, gcc 3.x-4.x for x86, SDCC for 8051, Crossworks for MSP430. I would greatly appreciate hearing a counter-example, or a good justification for why this is so. – KeyserSoze Sep 16 '09 at 20:27
  • If you use a pack pragma on structures that don't already have natural alignment, there are often times harmful side effects. If you have improperly aligned floats on PowerPC for example, a hardware exception will occur. The hardware handler can choose to reset the processor (common in embedded apps), or handle the problem in software (which is SLOOOOW). In either case, using a pack pragma can only hurt you, you definitely want to define the structure to have natural alignment and not use pack so it works the same across platforms. – KeyserSoze Sep 16 '09 at 20:28
1

C and C++ are different languages but it has always been the C++'s intention that you can have an implementation that supports both languages in a binary compatible fashion. Because they are different languages it is always a compiler implementation detail whether this is actually supported. Typically vendors who supply both a C and C++ compiler (or a single compiler with two modes) do support full compatibility for passing POD-structs (and pointers to POD-structs) between C++ code and C code.

Often, merely having a user-defined constructor breaks the guarantee although sometimes you can pass a pointer to such an object to a C function expecting a pointer to a struct with and identical data structure and it will work.

In short: check your compiler documentation.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
1

Use the same "struct" in both C and C++. If you want to add methods in the C++ implementation, you can inherit the struct and the size should be the same as long as you don't add data members or virtual functions.

Be aware that if you have an empty struct or data members that are empty structs, they are different sizes in C and C++. In C, sizeof(empty-struct) == 0 although in C99, empty-structs are not supposed to be allowed (but may be supported anyway as a "compiler extension"). In C++, sizeof(empty-struct) != 0 (typical value is 1).

Adisak
  • 6,708
  • 38
  • 46
0

In addition to other answers, I would be sure not to put any access specifiers (public:, private: etc) into your C++ class / struct. IIRC the compiler is allowed to reorder blocks of member variables according to visibility, so that private: int a; pubic: int b; might get a and b swapped round. See eg this link: http://www.embedded.com/design/218600150?printable=true
I admit to being baffled as to why the definition of POD does not include a prohibition to this effect.

tragomaskhalos
  • 2,733
  • 2
  • 17
  • 10
-2

As long as your class doesn't exhibit some advanced traits of its kind, like growing something virtual, it should be pretty much the same struct.

Besides, you can change Class (which is invalid due to capitalization, anyway) to struct without doing any harm. Except for the members will turn public (they are private now).

But now that I think of your talking about type conversion… There's no way you can turn float into long representing the same value or vice versa by casting pointer type. I hope you only want it these pointers for the sake of moving stuff around.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173