I'm a bit confused: I have a C API which is supposed to be called from C code and uses __cdecl
in the function declarations.
There's a vtable with function pointers like this:
void (__cdecl *funptr) (const MyStruct& obj);
references are a C construct, are they not? How can there be a __cdecl
with references?
And finally: is __cdecl
equivalent to wrapping everything in an extern "C"
statement? What about references in that case?
I'm a bit confused..
CodePudding user response:
These are apples and oranges.
__cdecl
is a non-standard keyword used to describe one of the more common x86 ABI calling conventions (together with __stdcall
) which specifies how variables are passed/stacked between caller and callee. It has nothing to do with C specifically - some historic Microsoft C compiler just used this calling convention, hence the name. Many programming languages can use this calling convention and similarly, C code doesn't have to use it.
extern "C"
just means that the code should be compiled "like C" by the C compiler, disabling various name mangling etc used internally by the C compiler. It's not necessarily related to compliant C, but could as well be used when sharing code between two different C compilers that may use different name mangling.
Neither has anything to do with how references work. In C they will not compile, in C they will compile. The code you posted is C .
CodePudding user response:
__cdecl
is a calling convention only. It's not related to C language specifically. It specifies who is responsible for stack cleanup with function calls.
extern "C"
prevents name decoration, and uses the c language calling convention.
CodePudding user response:
This code does indeed look rather sloppy.
The __cdecl
keyword defines the calling convention: it sets where arguments are supposed to be passed (registers or memory), in what order, which side of the function call is responsible for disposing of them once the function call finishes, and what register state is supposed to be preserved across the call. The extern "C"
declaration controls name mangling as well, i.e. under what name the function is visible to code outside the current translation unit. Neither declaration influences the memory representations of the arguments themselves. Collectively, all those concerns are known as the Application Binary Interface (ABI).
This means that a function signature that declares a C calling convention while simultaneously mentioning types foreign to C is not necessarily useless or meaningless. It’s of course not required that only transitively pure types (those shared between C and C ) should be passed across the C/C boundary: it’s expected that sometimes C code may receive opaque (to C) pointers to classes, to structures containing references inside, and so on. However, the presence of types foreign to C directly in the function signature (references, non-POD classes passed by value, non-opaque pointers to such) should be somewhat worrying. It signals whoever wrote that code probably did not think too deeply about ABI stability, or at least did not value it as much as programmer convenience.
Now, a reference is typically represented internally as a pointer, which means that on the C side, one should expect the declaration to behave the same as:
void (*funptr)(const MyStruct *obj);
This is what I think the programmer who wrote that signature had assumed. However, this is not guaranteed. While the C ABI is pretty much set in stone on each platform, the ABI of C has historically been much less stable. There is no telling if compilers may some day, for example, start passing const
references to ‘small’ types without mutable
fields as if they were by-value arguments, which might break declarations like mentioned in the question. At the moment this seems unlikely, it is but not entirely outside the realms of possibility.