Calling Conventions for Libraries on Mac and UNIX

Started by
10 comments, last by Markie 13 years, 4 months ago
Hi all,

Here's another question that burns me while I'm trying to write a cross-platform game engine:

What calling convention(s) are usually used on POSIX (MaxOSX and UNIX) for functions to be exported from libraries (DLLs)?

Is it tradition to use C Declaration, or Standard (Windows) Call?

I know Windows traditionally prefers stdcall (Standard Call), such as "CALLBACK" etc. for all DLL functions.
So if I write cross-platform libraries, should I create a cross-platform macro that maps to "_stdcall" on Windows and "_cdecl" on POSIX and stick that macro before every function to export?

Mark
Advertisement
I personally don't use any on my application of shared objects (dll's on unix)
POSIX using the C calling convention, I believe, but I don't know if that's actually called out in the POSIX standards or just at the lever of the POSIX C API. Apple has a document on the various calling conventions appropriate for their OS on various platforms (some of which, like the 68k and PPC chips, aren't relevant any more): click here.
Hi, thanks for the answers!

Sometimes I think I should just FORGET about cross-platform compatibility entirely. Linux and games... Not really, Apple? Oh well... But then I keep hearing how "easy" it supposedly is if you just keep a few important things in mind... And that there might be even other advantages of separating OS-specific code from non-OS-specific code.

Hmmm...

I guess I'll create a cross platform macro "LIBRARYFUNCTIONCALLINGCONVENTION" that maps to whatever convention (or noting at all) that I will use before every DLL function prototype / body.

Regards,
Mark
Quote:Original post by Markie
Hi, thanks for the answers!

Sometimes I think I should just FORGET about cross-platform compatibility entirely. Linux and games... Not really, Apple? Oh well... But then I keep hearing how "easy" it supposedly is if you just keep a few important things in mind... And that there might be even other advantages of separating OS-specific code from non-OS-specific code.

Hmmm...

I guess I'll create a cross platform macro "LIBRARYFUNCTIONCALLINGCONVENTION" that maps to whatever convention (or noting at all) that I will use before every DLL function prototype / body.

Regards,
Mark


The "trick" to making crossplatform development easy is to use existing cross platform middleware, when writing a game engine the calling conventions really aren't a big issue, both gcc and msvc use __cdecl by default.

The drawback of __cdecl is that it results in slightly larger code for non inlined functions, its advantage is that it allows for functions with a variable number of arguments and won't cause your program to crash catastrophically if you call a function using the wrong number of arguments. (If you use c or c++ the compiler will handle that anyway but for asm applications it can cause some problems)


There is absolutely nothing wrong with using __stdcall on Linux/MacOS or __cdecl on Windows, in fact, most Windows software use __cdecl for the majority of their functions, choose calling convention based on how the function in question is used, not what OS its running on.

The one thing you should avoid however is __fastcall since its implementation varies between compilers (Thus if you use it to compile your engine any user of your engine will have to compile their code using the same compiler (or one that happens to be compatible with it (Ofcourse, if there is a significant performance advantage by using this it might be worth the restriction it brings))

[Edited by - SimonForsman on December 4, 2010 11:02:31 PM]
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Quote:The one thing you should avoid however is __fastcall since its implementation varies between compilers

With 64 bit this might be necessary non the less. On 64 bit, gcc (on linux, didn't test mingw though) uses registers for the first 6 parameters (the rest goes on the stack as in 32 bit c call) where as the MS compiler only uses 4 registers.
It might be better to stick with one compiler and calling convention for engine and application but use whatever is default on the platform instead of trying to use the same calling convention on all platforms.
Quote:Original post by MarkieI guess I'll create a cross platform macro "LIBRARYFUNCTIONCALLINGCONVENTION" that maps to whatever convention (or noting at all) that I will use before every DLL function prototype / body.
That's actually not as crazy as you make it sound. I personally have all my commonly used functionality arranged as modules in a hierarchy with the one at the very bottom being dedicated to platform isolation.

For example, you could define a macro like this in a shared library of yours
#ifdef __WINDOWS__#   ifdef __cplusplus#       define MYEXPORT extern "C" __declspec(dllexport)#   else#       define MYEXPORT __declspec(dllexport)#   endif#elif defined(__APPLE__)#   define MYEXPORT ...#elif defined(__LINUX__)#   define MYEXPORT ......
The major benefit of keeping macros like this in a separate module/file is that you only need to write them once; afterward you can conveniently import them into each of your projects
#include <mymacros.h>MYEXPORT int MyExportedFunction ( void );
And the party doesn't stop here, because re-using the module like this all the projects using it are affected immediately everytime you update it; all you need to do is re-compile them!
Hi SimonForsman!

Awesome post! Thanks!!! :-)

Quote:Original post by SimonForsman
The "trick" to making crossplatform development easy is to use existing cross platform middleware, when writing a game engine the calling conventions really aren't a big issue, both gcc and msvc use __cdecl by default.
I think Petzold writes that in Windows programming, it is a tradition (that is expected by other Windows programmers as well), that functions exported from DLLs use the _stdcall (STanDard) Windows calling convention.
The argumentation is that a DLL can be seen as a part of the operating system (Windows) rather than the application, because multiple apps can use one and the same dll, hence it's part of the OS, seen from a certain perspective.
And... Windows internally, meaning Windows System functions use _stdcall, so, so should functions in DLLs.
That's the whole point of Petzold (and other Windows programmers?) using _stdcall (or CALLBACK, etc.) for DLL functions.
Obviously this tradition is not in place on POSIX.

Quote:Original post by SimonForsman
The drawback of __cdecl is that it results in slightly larger code for non inlined functions, its advantage is that it allows for functions with a variable number of arguments and won't cause your program to crash catastrophically if you call a function using the wrong number of arguments. (If you use c or c++ the compiler will handle that anyway but for asm applications it can cause some problems)

There is absolutely nothing wrong with using __stdcall on Linux/MacOS or __cdecl on Windows, in fact, most Windows software use __cdecl for the majority of their functions, choose calling convention based on how the function in question is used, not what OS its running on.
Yes, thanks, I was aware of the drawbacks and advantages of _std and _cdecl. Thanks. Most Windows software indeed uses __cdelc for the majority of their functions, but that goes only for function not in DLLs, right?
At least Petzold seems to be saying so.

You're absolutely right I think that one should choose the correct calling convention based on what one needs, (multiple arguments OR smaller code). So this means I really shouldn't mind about either Windows or POSIX traditions?
If so, I guess I'd specifically use _stdcall for all library functions, both on Windows and POSIX, except for those functions with variable arguments, where I'd be using _cdecl on both platforms.
Is that really smart / correct? Should I thus really abandon the Windows tradition as Petzold shows and encourages?


Quote:Original post by Dragonion
The major benefit of keeping macros like this in a separate module/file is that you only need to write them once; afterward you can conveniently import them into each of your projects

#include <mymacros.h>
MYEXPORT int MyExportedFunction ( void );

And the party doesn't stop here, because re-using the module like this all the projects using it are affected immediately everytime you update it; all you need to do is re-compile them!
Hey, thanks to you too for your help!
Yes, I got a similar example from "Programming Applications for MS Windows" by Jeffrey Richter. I even took it to another level.
I'm actually creating a library-specific "IMPORT" / "EXPORT" macro (the one you call "MYEXPORT". It's defined in the header of each library as "IMPORT" (if it was not defined yet - #ifndef) because the header is included in the library-using source code, so the functions are correctly imported by the app. Then in the library source code it is defined as "EXPORT" BEFORE the library header is included (which would define it as "IMPORT"). Thus in the compiled library header (which would define it as "IMPORT" only if it's not defined yet - #ifndef) and source code it is correctly "EXPORT".
- Actually that's an old trick I assume.
I also created a header to define different import and export specifiers, depending on the type of library, you know, static, run-time dll or load-time dll, as they have different import / export specifiers.
The "IMPORT" / "EXPORT" specifier of each library actually maps / expands to the correct one of those, so I can use one common cross-platform import / export macro for all functions from libraries.

Anyway, all this is totally independent of the calling convention which my original question actually was about, as the calling convention must go between the function's return type and the function name, not BEFORE the function's return type as the "IMPORT" / "EXPORT" specifiers. But thanks for helping! :-)

Mark
Quote:Original post by Markie
Thanks. Most Windows software indeed uses __cdelc for the majority of their functions, but that goes only for function not in DLLs, right?
At least Petzold seems to be saying so.


In general you want __stdcall for dynamic libraries yes since those functions won't be inlined anyway allthough the only real benefit is marginally smaller code and compatibility with languages that doesn't support __cdecl (such as VB6), i was wrong in my earlier post though, gcc and msvc doesn't handle __stdcall in the same way. for gcc you want:
__attribute__((stdcall)) rather than __stdcall

so you should probably add:

#ifdef __GNUC__
#define __stdcall __attribute__((stdcall))
#endif

or better yet i guess:

#ifndef WIN32
#define __stdcall
#endif

then simply ignore all non Microsoft compilers for the Windows platform and let other platforms use their default convention.

IIRC this applies to gcc on all platforms, even Windows so its necessary if you want to support more than one compiler.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Quote:Original post by SimonForsman
In general you want __stdcall for dynamic libraries yes since those functions won't be inlined anyway allthough the only real benefit is marginally smaller code and compatibility with languages that doesn't support __cdecl (such as VB6), i was wrong in my earlier post though, gcc and msvc doesn't handle __stdcall in the same way. for gcc you want:
__attribute__((stdcall)) rather than __stdcall

so you should probably add:

#ifdef __GNUC__
#define __stdcall __attribute__((stdcall))
#endif

or better yet i guess:

#ifndef WIN32
#define __stdcall
#endif

then simply ignore all non Microsoft compilers for the Windows platform and let other platforms use their default convention.

IIRC this applies to gcc on all platforms, even Windows so its necessary if you want to support more than one compiler.


Hey yea, thanks!
That was exactly my next question, I was gonna add it by edit, but you've already addressed it: :-)
The implementations of invoking the std and cdecl calling conventions are not the same in Windows and in POSIX, are they?
If they are not, as your posts suggests (on Linux, std calling convention is invoked by "__attribute__((stdcall))" instead of "__stdcall", it might be wise to create cross-platform (cross-compiler as well?) macros such as "cp_CALLCONV_STD" and "cp_CALLCONV_CDECL" which map to std and cdecl with the correct implementation on every platform / compiler.
For instance:
#ifdef __GNUC__#define cp_CALLCONV_STD      __attribute__((stdcall))#define cp_CALLCONV_CDECL  ??__attribute__((cdeclcall))??#endif#ifdef __MACOSX__#define cp_CALLCONV_STD    ??whatever??#define cp_CALLCONV_CDECL  ??whatever??#endif#ifdef WIN32#define cp_CALLCONV_STD     __stdcall#define cp_CALLCONV_CDECL   __cdecl#endif


I prefer a "complicated" setup like this instead of a "nifty" #ifndef on one platform only. This way I can explicitly SET any calling convention I want for any function on each platform with one single macro. And if I omit the macro, I simply get the native convention.
(That would probably be in line with your probably good suggestion of using the calling convention which best suits you personally for every function call, regardless of platform or any possible traditions, etc. :-)

For you Linux gurus out there, I'd be curious what the "attribute" stands for in __attribute__((stdcall))?
Does it have to be replaced with anything (if so, what?) or is that literally how std calling convention is invoked? If so what's the meaning / background of "attribute"??
:-)

Mark

This topic is closed to new replies.

Advertisement