Calling Convention Conundrum


#1

Hello, RMS Forum.

I’m evaluating JUCE for use with my employer’s existing software, and am experiencing some problems static linking to the JUCE lib as a project dependency.

The JUCE vcproj is built with the default __cdecl calling convention, and a few functions within the lib are defined as JUCE_CALLTYPE, which is __stdcall for JUCE_MSVC.

So, the JUCE lib is a mixture of mostly __cdecl with some __stdcall mixed in. The __cdecl JUCE functions aren’t declared as __cdecl, they are compiled this way due to a project setting. So, when linking to the JUCE lib, my project doesn’t get __cdecl info from the JUCE headers. It assumes the unspecified function calling conventions are whatever my project’s default calling convention is. This is causing me problems, as my project is build __stdcall for debug and __fastcall for release. (I can’t change this aspect of my project. It’s what I need to deal with, and is out of my hands.) I could change the JUCE project default to match my project’s, but the JUCE lib doesn’t successfully build if the project’s calling convention isn’t set to __cdecl.

It’s my understanding that the common practice for libraries is to specify the calling convention for all function prototypes so that they can be called from other libs without making calling convention assumptions.

Am I missing something with JUCE? Is there a calling convention switch I should be using? Multiple lib flavors?

I expect,as usual, I’m missing something rather simple.

Here are my link errors.
My project is building jucelib_static_Win32_debug.lib as a dependency and static linking to it.

Any help understanding this would be much appreciated.

3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static unsigned int __stdcall juce::Time::getMillisecondCounter(void)" (?getMillisecondCounter@Time@juce@@SGIXZ) referenced in function "public: virtual void __thiscall TestTimer::timerCallback(void)" (?timerCallback@TestTimer@@UAEXXZ)
3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static class juce::MidiMessage const __stdcall juce::MidiMessage::noteOff(int,int)" (?noteOff@MidiMessage@juce@@SG?BV12@HH@Z) referenced in function "public: virtual void __thiscall TestTimer::timerCallback(void)" (?timerCallback@TestTimer@@UAEXXZ)
3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static class juce::MidiMessage const __stdcall juce::MidiMessage::noteOn(int,int,unsigned char)" (?noteOn@MidiMessage@juce@@SG?BV12@HHE@Z) referenced in function "public: virtual void __thiscall TestTimer::timerCallback(void)" (?timerCallback@TestTimer@@UAEXXZ)
3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static void __stdcall juce::Process::setPriority(enum juce::Process::ProcessPriority)" (?setPriority@Process@juce@@SGXW4ProcessPriority@12@@Z) referenced in function "private: __thiscall JuceAudioEngine::JuceAudioEngine(void)" (??0JuceAudioEngine@@AAE@XZ)
3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static void __stdcall juce::JUCEApplication::quit(void)" (?quit@JUCEApplication@juce@@SGXXZ) referenced in function "public: bool __thiscall JuceAudioEngine::tryToQuitApplication(void)" (?tryToQuitApplication@JuceAudioEngine@@QAE_NXZ)
3>JuceAudioEngine.obj : error LNK2019: unresolved external symbol "public: static int __stdcall juce::DialogWindow::showModalDialog(class juce::String const &,class juce::Component *,class juce::Component *,class juce::Colour const &,bool,bool,bool)" (?showModalDialog@DialogWindow@juce@@SGHABVString@2@PAVComponent@2@1ABVColour@2@_N33@Z) referenced in function "private: void __thiscall JuceAudioEngine::showAudioSettings(void)" (?showAudioSettings@JuceAudioEngine@@AAEXXZ)
3>JuceFilterGraph.obj : error LNK2019: unresolved external symbol "public: static class juce::String const __stdcall juce::LocalisedStrings::translateWithCurrentMappings(char const *)" (?translateWithCurrentMappings@LocalisedStrings@juce@@SG?BVString@2@PBD@Z) referenced in function "public: unsigned int __thiscall JuceFilterGraph::addFilter(class juce::PluginDescription const *)" (?addFilter@JuceFilterGraph@@QAEIPBVPluginDescription@juce@@@Z)
3>JuceGraphDocument.obj : error LNK2019: unresolved external symbol "public: static class juce::Random & __stdcall juce::Random::getSystemRandom(void)" (?getSystemRandom@Random@juce@@SGAAV12@XZ) referenced in function "private: __thiscall PluginWindow::PluginWindow(class juce::Component * const,class juce::AudioProcessorGraph::Node *,bool)" (??0PluginWindow@@AAE@QAVComponent@juce@@PAVNode@AudioProcessorGraph@2@_N@Z)

Many thanks,

Xi G.


#2

Personally, I’d suggest not using it as a static library at all - if you just add the amalgamated files directly to your project, you’ll bypass any of these problems. I’ve been building all of my own projects like that for some time now and it’s vastly easier than messing about with linkers.

If you’re really determined to link it, the easiest way would probably be to just build the library with a different call type specified, but you say that’s not working?


#3

Thanks for the suggestions, Jules.

If I change the JUCE project dependency to build __stdcall, I get the following compile errors:

1>juce_PNGLoader.cpp
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\gui\graphics\imaging\image_file_formats\juce_pngloader.cpp(155) : error C2664: 'pnglibNamespace::png_set_error_fn' : cannot convert parameter 3 from 'void (__stdcall *)(pnglibNamespace::png_structp,pnglibNamespace::png_const_charp)' to 'pnglibNamespace::png_error_ptr'
1>        None of the functions with this name in scope match the target type
...

1>juce_win32_NativeCode.cpp
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows\juce_win32_asio.cpp(401) : error C2440: '=' : cannot convert from 'void (__stdcall *)(ASIOSampleRate) throw()' to 'void (__cdecl *)(ASIOSampleRate)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
...

1>juce_OggVorbisAudioFormat.cpp
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\audio\audio_file_formats\oggvorbis\libvorbis-1.1.2\lib\floor1.c(205) : error C2664: 'qsort' : cannot convert parameter 4 from 'int (__stdcall *)(const void *,const void *)' to 'int (__cdecl *)(const void *,const void *)'
1>        None of the functions with this name in scope match the target type
...

1>juce_FlacAudioFormat.cpp
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\audio\audio_file_formats\flac\libflac\format.c(295) : error C2664: 'qsort' : cannot convert parameter 4 from 'int (__stdcall *)(const void *,const void *)' to 'int (__cdecl *)(const void *,const void *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast

Is there any documentation on using the amalgamated? Since I’m not using the Jucer, the demo app isn’t a good example, and I haven’t found much info in the forums.

To attempt to use the amalagamted files, I have removed the JUCE project as a dependency and instead added juce_amalgamated.cpp, juce_amalgamated1.cpp, juce_amalgamated2.cpp, juce_amalgamated3.cpp, and juce_amalgamated4.cpp to my project.

It looks like I need to set my JUCE build config options in both juce_amalgamated.h and juce_amalgamated.cpp?

Simply trying to compile juce_amalgamated.cpp:

1>Compiling...
1>juce_amalgamated.cpp
1>..\LIB\juce\juce_1_51\juce\juce_amalgamated.cpp(104223) : error C2664: 'qsort' : cannot convert parameter 4 from 'int (__stdcall *)(const void *,const void *)' to 'int (__cdecl *)(const void *,const void *)'
...

(I’ve removed a long list of compile errors dealing with calling convention, leaving just the first for an example.

qsort is declared to take a __cdecl function as the fourth parameter, but the function in juce_amalgamated.cpp

qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_);

does not specify the calling convention, which means juce_amalgamated.cpp won’t build as anything but __cdecl.

It’s seeming that to use the JUCE code (either in a lib or in amalgamated form) in a non __cdecl project, I’ll need to make a lot of modifications to the JUCE code base.

Everything came together for me very quickly on Mac, and I was able to switch our audio engine and plugin hosting to use JUCE, but it’s not cooperating as nicely on Windows, and I’m unsure how to proceed.


#4

Interesting… I’ve tweaked a few functions to add explicit call types - try the latest version and see if that helps.

Re: getting started with the amalgamated stuff, sounds like you’re doing the right thing. If you generate a jucer project or look at the demo app, you’ll see the way it puts all the juce includes inside a folder, and that’s probably the best way to organise your own project.


#5

I figured out GIT and grabbed the tip. Seems like everything in JUCE is now compiling and linking with non__cdecl calling conventions, aside from the ASIO code.

Almost there. Thanks for the attention to this, Jules. :smiley:

1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(396) : error C2440: '=' : cannot convert from 'void (__stdcall *)(ASIOSampleRate)' to 'void (__cdecl *)(ASIOSampleRate)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(400) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(401) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(402) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(406) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(407) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(408) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(412) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(413) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(414) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1054) : error C2440: '=' : cannot convert from 'void (__stdcall *)(ASIOSampleRate)' to 'void (__cdecl *)(ASIOSampleRate)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1058) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1059) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1060) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1064) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1065) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1066) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1070) : error C2440: '=' : cannot convert from 'void (__stdcall *)(long,long)' to 'void (__cdecl *)(long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1071) : error C2440: '=' : cannot convert from 'long (__stdcall *)(long,long,void *,double *)' to 'long (__cdecl *)(long,long,void *,double *)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
1>c:\users\spuhl\documents\p4_workspaces\finalemainline_spuhl_macbookprowin7\private\finaledev\finale\lib\juce\juce_1_51\juce\src\native\windows/juce_win32_ASIO.cpp(1072) : error C2440: '=' : cannot convert from 'ASIOTime *(__stdcall *)(ASIOTime *,long,long)' to 'ASIOTime *(__cdecl *)(ASIOTime *,long,ASIOBool)'
1>        This conversion requires a reinterpret_cast, a C-style cast or function-style cast

#6

I don’t understand how you managed to get those errors… I looked at the ASIO stuff, but in the Steinberg headers there’s no calling convention declared - those functions should just take on whichever calling convention has been set for the whole project. I tried it myself as both __cdecl and __stdcall, and both compiled just fine.

(TBH I think it’s a bug in Steinberg’s code that they don’t set a calling convention, I don’t really understand how it could work properly without using the same one that their library is using)


#7

I’ll look more carefully at my side of this over the weekend. I was admittedly a bit code & meeting fried at the end of yesterday’s working day, and my attention to detail may have been suspect.


#8

Here’s what I have for ASIO/JUCE code:

asio.h (from ASIO SDK 2.2, the latest available on Steinberg’s 3rd Party Dev site)

juce_Win32_ASIO.h

juce_Win32_ASIO.cpp

#define JUCE_ASIOCALLBACK // should probably use this to define the callback type, but // the asio header doesn't actually specify a calling convention for the functions..

So, my ASIO header does define the ASIO functions/callbacks as __cdecl.

If I change the JUCE define JUCE_ASIOCALLBACK to be __cdecl to match my ASIO define, everything builds and links.


#9

Ah… I was using an out-of-date ASIO library. Ok, thanks, I’ll get the latest one and add that declaration…


#10

Ok, I’m having a bit of a WTF moment here… I just went to Steinberg’s site and downloaded ASIO SDK 2.2, and in my copy it says:

void (*sampleRateDidChange) (ASIOSampleRate sRate);


#11

WTF, indeed.

My existent ASIOSDK2 directory (which I would have downloaded from Steinberg last year or the year before) contains a pdf called ASIO SDK 2.2.pdf and the readme.txt also specifies ASIO SDK 2.2. This is the ASIO SDK contains the asio.h file which declares the ASIO callbacks to be __cdecl.

I have downloaded the supposed latest SDK from Steinberg as it currently is available on the website, and this directory also contains a pdf called ASIO SDK 2.2.pdf and the readme.txt also specifies ASIO SDK 2.2. The asio.h file, however, does not declare the callbacks to be __cdecl.

Both of these conflicting files are headed with the same comment.

(c) 1997 - 2005, Steinberg Media Technologies GmbH
ASIO Interface Specification v 2.1

Maybe the ASIO Dev list has some info on this. I wish I could remember my login info. It’s a shame that it’s a separate account from the Steinberg login. :roll:


#12

Looks like someone at Steinberg’s been a bit sloppy with the version control there…

Well, assuming that the correct calling convention is __cdecl, I’ve added that to my code. The only thing that can go wrong is if somebody with the non-specific ASIO SDK tries to build their juce project with __stdcall, but I can add a comment to explain what’s going on if they hit an error doing that.