3

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)

TSG
  • 4,242
  • 9
  • 61
  • 121
  • 11
    Your understanding is wrong. structs are basically the same in both C and C++. A struct defined in C will compile and link in c++ –  Apr 08 '18 at 22:29
  • 9
    If the struct members would be valid in a C struct, they are equivalent. Adding virtual functions, for example, would make the struct incompatible. – Bo Persson Apr 08 '18 at 22:33
  • 1
    I can only guess what "actually an object" means to you. Whatever it is, it does not apply to C++. – Jive Dadson Apr 08 '18 at 22:43
  • A C++ class (as declared by a by either the `struct` keyword or the `class` keyword) can be a whole lot of things, but _typically_ it's something resembling a C struct. http://en.cppreference.com/w/cpp/concept/StandardLayoutType and http://en.cppreference.com/w/cpp/concept/TrivialType might help. – hegel5000 Apr 08 '18 at 23:05
  • 2
    This question is tricky to answer because there are a lot of issues to be considered. The short and not quite complete answer is that a C++ `struct` (or `class`) conforming to the requirements of a 'standard layout' class (which any purely C declaration does) is just fine. `extern "C"` is a complete red herring here and not at all relevant. – Omnifarious Apr 08 '18 at 23:08
  • In C++, *every* object is "just a block of memory divided up into individually addressable pieces" – Caleth Apr 24 '18 at 14:29

3 Answers3

5

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, i.e. 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);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • I see your point now. If I may, I suggest you expand it a little further and address C headers being included in a language linkage block. Since that's what the OP seems to be dealing with. – StoryTeller - Unslander Monica Apr 08 '18 at 22:40
  • Will wrapping the struct in extern "C" cause the struct to be packed to he byte level? And for the library, is there a benefit to wrapping it in a namespace so it doesn't pollute the global address space? – TSG Apr 10 '18 at 13:37
  • @TSG: No, it will not. And - structures in C are not auto-packed either. Packing is (AFAIK) done using compiler-specific extensions. – einpoklum Apr 10 '18 at 13:43
  • it's worth noting that you can actually inherit off the C structs too, insofar as you don't use any `virtual` functions that would alter the layout and create a v-table – Mgetz Apr 24 '18 at 14:19
  • This is misinformation. `extern "C"` has nothing to do with `struct` layout at all. – Omnifarious Apr 24 '18 at 16:16
  • @Omnifarious: I said so in the body of the answer, but let me clarify a bit further. – einpoklum Apr 24 '18 at 17:15
  • 2
    @Omnifarious *This is misinformation. `extern "C"` has nothing to do with `struct` layout at all.* Not quite true. There are ways. Some parts of a `struct` can be implementation-defined (bit fields) and using a C `struct` in C++ code is quite likely to be done with a different implementation, especially when it's something like the "older library" in this very question. FWIW, I've also encountered situations where different compiler optimization options between the library and the calling application led to different `struct` layouts. Imagine compiling the library with GCC and `-fpack-struct`. – Andrew Henle Apr 24 '18 at 19:26
  • @AndrewHenle - And the compiler used the declaration of the struct in an `extern "C"` block as a signal to use the C layout for bitfields? Hmm... I suppose it's possible, but I'm not sure I like that choice. – Omnifarious Apr 27 '18 at 13:37
  • @Omnifarious: Why not? It seems like the only reasonable one. – einpoklum Apr 27 '18 at 13:45
  • @einpoklum - `extern`? `extern` has always clearly been about symbols. In C, structure tags never show up as symbols in object files. And you're not even using it to control anything related to a symbol name anyway, you're using it to control the in-memory representation of a structure. It's a conceptual mismatch. I would rather a bletcherous extra keyword like `__packed` or something similarly stupid. As a side benefit, it's then also clear you are relying on implementation defined behavior. – Omnifarious Apr 27 '18 at 15:16
  • @Omnifarious: Seems like this was "reserved word frugality" - reusing extern for something else. – einpoklum Apr 27 '18 at 15:30
1

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

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 structs.

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.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • 4
    This answer is flat out wrong when it comes to Windows. The Microsoft compilers team has released a LOT of information about how windows works and open sourced components required for other compilers to work. To the point that Chrome now builds with clang on windows. The Microsoft specific calling conventions have been documented for ages and supported by mingw and icc for ages as well. – Mgetz Apr 24 '18 at 14:21
  • "*since Microsoft has actively prevented its emergence wishing to prevent competition from competing compilers on its platform*" I'm curious about the foundation for this statement. Windows does very much have a standard ABI; just not the *same* standard that Linux uses (Itanium). After all, you can't just change an ABI on a whim; that'd break pretty much every program that doesn't get recompiled. – Nicol Bolas Apr 24 '18 at 15:07
  • Follow up for clarity the windows calling conventions for [32bit x86](https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions) [x86-64](https://learn.microsoft.com/en-us/cpp/build/overview-of-x64-calling-conventions) [ARM](https://learn.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions) are all very well documented. – Mgetz Apr 24 '18 at 15:19
  • @Mgetz - I'm talking C++. The only standard I know for C++ is COM, and that isn't really an ABI at all in the normal sense of the word. Yes, I will agree, the C stuff is largely standardized. The C++ stuff is not at all. It's basically impossible to link code compiled by g++ or Clang to code compiled by Visual C++. At least, everybody working on SQL Server for Windows was of the opinion that this was the case. – Omnifarious Apr 24 '18 at 15:26
  • @NicolBolas - My recent experience working with the SQL Server for Windows team left me with the distinct impression that there was basically no standard C++ ABI for Windows. And I used to use Borland C++ and a few others on Microsoft platforms. At that time Microsoft could've created a standard of some sort that they all conformed to, and chose instead to destroy them by purposefully poaching all the developers and other shady means. – Omnifarious Apr 24 '18 at 15:27
  • 1
    @Omnifarious please do not confuse name mangling for ABI, C++ uses the same ABI practices as C. The difference is that there are things that are compiler version safe (COM, `extern 'C'`) and things that are not `std::string` etc. On linux there is only ever one compiler and stdlib running at once (usually) and thus having to worry about having ABI incompatible C++ library objects isn't a concern. On windows the guide has always been that they are safe within a compiler and library combo. – Mgetz Apr 24 '18 at 15:31
  • @Mgetz: "*On linux there is only ever one compiler and stdlib running at once (usually)*" That's not really the case anymore. Linux has its own standard library ABI issues, such as the transition from C++98 COW `std::string` to a C++11-compliant `std::string`. Programs that use the wrong one cannot talk to each other through such an object. But you're right that standard library versioning is ultimately a different issue from a platform's ABI. – Nicol Bolas Apr 24 '18 at 15:37
  • @Mgetz - Interesting. I will have to do a little more research. My experience with Windows is heavily colored by their extremely anti-competitive practices in the late 90s and early 2000s. I basically have not written code for the platform (nor even really used it) at all since about 1997. – Omnifarious Apr 24 '18 at 15:37
  • @NicolBolas - Also, libstdc++ has adopted an internal convention of putting things in compiler version specific namespaces and just importing names from there into `::std` so the canonical name for the symbol still includes the compiler version. But, there has been an effort as well to make the library more compatible between compiler versions. There's also Clang's libc++, which I believe has adopted similar conventions. – Omnifarious Apr 24 '18 at 15:39
  • @NicolBolas I'm aware but it wasn't immediately relevant to the point I was trying to make. It's why I included the (usually) when referring to linux. Because there are poor unfortunate souls still on an RHEL version with an ancient GCC that have to deal with that pain. – Mgetz Apr 24 '18 at 15:45
  • @Mgetz - I refined my answer a bit to hopefully have less misinformation about Microsoft. I did not realize that Microsoft had made any attempt to standardize the C++ ABI at all. But the link you pointed me at clearly talks about exception handling issues and other C++ specific ABI concerns, so it seems this is changing. – Omnifarious Apr 24 '18 at 16:15
  • @Omnifarious reading your changes my comment still stands. The C++ issues are caused by things that would break the ABI in C as well. E.g. allocators changing and structure layout changing. Name mangling is a non-issue. Exception handling is dealt with at the runtime library level (and is based on SEH so it's possible to have an exception that crosses various different modules even if they have different compilers). The fact of the matter is that there never was a C++ abi issue on Windows, but there was a runtime library ABI changing issue that has always been present on all platforms. – Mgetz Apr 24 '18 at 16:23
  • @Mgetz - Name mangling isn't a non-issue. IMHO, a standard C++ ABI means that you can compile a bunch of C++ code with one compiler, a bunch of other C++ code with another, link them together and have it work just the same is if you compiled all of that code with just one compiler. Name mangling is an issue that prevents this. Anything that prevents this from working is an ABI standardization issue. Name mangling, C++ class layout conventions (because of inline functions) and a number of other things all contribute here. – Omnifarious Apr 24 '18 at 16:30
  • @Mgetz - Fixed. – Omnifarious Apr 24 '18 at 19:01