简体   繁体   中英

Passing C++ struct to library expecting C struct

I am writing a C++ program in Linux and need to use an older library written in C. The library uses C structs for passing information in and out of functions, and these structs are byte aligned (no padding).

My understanding is that a struct in C++ is actually an object, while a struct in C is just a block of memory divided up into individually addressable pieces.

How can I create a C style struct in C++ to pass to the library? (I can't pass an object)

You're asking two questions here, really...

How can I create a C style struct in C++?

Instead of

struct foo { /* ... */ };

use

extern "C" {
    struct foo { /* ... */ };
}

This probably won't result in anything different, ie a "C++ style struct" and a "C style struct" are usually the same thing, as long as you don't add methods, protected members, and bit fields. Since "extern C" is needed for functions, however, it's reasonable to just surround all code intended for use in C within these braces.

For more details, read: What is the effect of extern "C" in C++? and @AndrewHenle's comment.


I ... need to use a library written in C

I'm paraphrasing an official C++ FAQ item here, telling you to (surprise, surprise) just include the library header within an extern C block, then use whatever's in it like you would if you were writing C:

extern "C" {
  // Get declaration for `struct foo` and for `void f(struct foo)`
  #include "my_c_lib.h"
}

int main() {
    struct foo { /* initialization */ } my_foo;
    f(my_foo);
}

My understanding is that a struct in C++ is actually an object, while a struct in C is just a block of memory divided up into individually addressable pieces.

You have just described almost the exact same thing, in different words.

Because of the syntax overlap of both languages, you can usually take a simple class defined with the struct keyword and compile that same definition with a C compiler.

Furthermore, you can usually pass a pointer to an object of a user-defined type from a C++ program, into a C program. However you will usually want to wrap the definition in extern "C" in order to tell the computer you want this compatibility.

Finally, remember that in C++ the keyword class and the keyword struct both introduce a class definition so C++ doesn't really have "structs", but in C they are most definitely a thing (whereas classes are not). As such just be a little careful with the terminology.

So, this question has some complex issues associated with it. But, to a first approximation, the answer is simple.

Any C struct declaration that will be accepted by both a C compiler and a C++ compiler (which is most of them that are accepted by a C compiler) will be a ' Standard Layout Type '. This is a concept defined in the C++ standard, and C++ compilers are supposed to treat them a certain way.

In particular, a C compiler that conforms to a subset of the ABI (aka Application Binary Interface) that a C++ compiler conforms to should have the exact same memory representation for such a type.

For both Linux and Windows, the ABI is very carefully defined for both C and C++ and has been for 5-10 years now. All compilers on either platform are supposed to conform to it. So this is true for any of the common C and C++ compiler combinations. This includes Clang, g++, Visual Studio, and basically practically any compiler that isn't highly specialized that works on that platform. This doesn't necessarily mean that different versions of the standard C++ libraries have compatible implementations because (for example) the way ::std::string has been implemented over the years (and therefor what pieces of data are actually in the string structure) has changed drastically even though the public interface hasn't changed much.

The C++ ABI is complicated by a few different factors. Among them being exception handling and function name mangling (encoding the types of function arguments in the linker symbol name for the function) conventions. Also, because of the way types are defaulted to int in so many situations and other issues, C largely required the caller to pop arguments off the stack after the function finished because the called function couldn't really be sure of the sizes of the arguments that had been pushed on. C++ has stricter type checking, and for awhile this resulted in a 'called function pops the parameters' calling convention. I don't think this is the case anymore because I think it turned out that it was less efficient. I might be wrong though.

Now, this all goes out the window if you start using compiler-specific keywords like 'packed' or 'aligned'. Then, you should carefully consult the documentation to figure out what happens.

People have mentioned an extern "C" declarations. These are important for interfacing between C and C++ code, but they have nothing to do with struct layout and are not needed for this at all. What extern "C" declarations are good for is declaring that function names and calling conventions conform to the C ABI and not the C++ ABI. This is about whether the caller pops function call parameters off the stack or the called function does, how the function name is turned into a symbol for the linker to work with, the order parameters are pushed on the stack, and so forth. Again, nothing about how structures are layed out in memory has anything to do with extern "C" . That construct is all about functions, not struct s.

And, to re-iterate, there's a lot of complexity here. But in the vast majority of cases, you don't need to worry about it at all. It will just work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM