How does JUCE accomplish it's multi-platform magick?


#1

I would like to understand how Juce holds together.

I start out by guessing what the structure might be:

Say we have a platform independent VSyncCallback() function -- you pass in a function pointer and it executes that function on every vsync. I'm just trying to think of an example...

And each OS will have a different way of implementing this.

So I would expect something like:

Graphics/interfaceToNative.h -- containing VSyncCallback() declaration

so this would contain declarations for every function that is platform specific

And then:

Graphics/Native/iOS/graphics.cpp

Graphics/Native/Windows/graphics.cpp

etc, each of which would contain a definition for VSyncCallback(), which would call the appropriate API-call for that specific OS

And I would imagine each one to have some kind of:

#ifdef PLATFORM_WINDOWS
    #include win_graphics.h
    void VSyncCallback() {
        return VSyncCallback_IN_WINDOWS();
    }
#endif

wrapping round the whole file.

And then IntroJucer would need to set the appropriate flag e.g.

#define PLATFORM_WINDOWS

For the Windows build, etc.

Even better idea, IntroJucer can just include the appropriate .cpp files for the target platform. No need for fiddling with #define

Problem isn't yet over, as IntroJucer still needs to fish out the appropriate .dll/.lib/.a for that particular operating system that contains the VSyncCallback_IN_WINDOWS() function


Now I start looking through the code; I open up the hello world project in Xcode as I'm using OS X.

And I can see something like this I think.

I can see

/JUCE-master/modules/juce_core/native

/JUCE-master/modules/juce_graphics/native

etc.

So now I'm going to try to find two different platforms implementing the same function

Juce Modules/juce_graphics/native/juce_android_Fonts.cpp

struct DefaultFontNames
{
    DefaultFontNames()
        : defaultSans  ("sans"),
          defaultSerif ("serif"),
          defaultFixed ("monospace"),
          defaultFallback ("sans")
    {
    }

and...

Juce Modules/juce_graphics/native/juce_linux_Fonts.cpp

struct DefaultFontNames
{
    DefaultFontNames()
        : defaultSans  (getDefaultSansSerifFontName()),
          defaultSerif (getDefaultSerifFontName()),
          defaultFixed (getDefaultMonospacedFontName())
    {
    }

Now does this mean that at some previous level there was some kind of:

struct DefaultFontNames {

    // OVERRIDE PER PLATFORM
}

construct?

I would be looking for a Juce Modules/juce_graphics/fonts.h I guess, which might also contain documentation for what DefaultFontNames does.

Aha!  Got a Juce Modules/juce_graphics/fonts/juce_font.h that seems to be doing this. Searching for DefaultFontNames doesn't bring up any matches.  That maybe that wasn't a good example, as it is kind of trivial and self-documenting.

All the same, if it is hidden in per-platform files, this wouldn't be good as it wouldn't be easy to find. So maybe it is somewhere else?

pi@piBookAir.local ~ /Users/pi/Desktop/JUCE-master:
 ⤐  grep "DefaultFontNames" . -R
Binary file ./extras/Demo/Builds/MacOSX/build/Debug/JuceDemo.app/Contents/MacOS/JuceDemo matches
Binary file ./extras/example projects/Builds/Android/libs/armeabi/libjuce_jni.so matches
Binary file ./extras/example projects/Builds/Android/obj/local/armeabi/libjuce_jni.so matches
Binary file ./extras/example projects/Builds/Android/obj/local/armeabi/objs/juce_jni/__/__/__/__/__/modules/juce_graphics/juce_graphics.o matches
Binary file ./extras/example projects/Builds/MacOSX/build/Debug/HelloWorld.app/Contents/MacOS/HelloWorld matches
Binary file ./extras/Introjucer/Builds/MacOSX/build/Debug/Introjucer.app/Contents/MacOS/Introjucer matches
./modules/juce_graphics/native/juce_android_Fonts.cpp:struct DefaultFontNames
./modules/juce_graphics/native/juce_android_Fonts.cpp:    DefaultFontNames()
./modules/juce_graphics/native/juce_android_Fonts.cpp:    static DefaultFontNames defaultNames;
./modules/juce_graphics/native/juce_linux_Fonts.cpp:struct DefaultFontNames
./modules/juce_graphics/native/juce_linux_Fonts.cpp:    DefaultFontNames()
./modules/juce_graphics/native/juce_linux_Fonts.cpp:    JUCE_DECLARE_NON_COPYABLE (DefaultFontNames)
./modules/juce_graphics/native/juce_linux_Fonts.cpp:    static DefaultFontNames defaultNames;
./modules/juce_graphics/native/juce_mac_Fonts.mm:struct DefaultFontNames
./modules/juce_graphics/native/juce_mac_Fonts.mm:    DefaultFontNames()
./modules/juce_graphics/native/juce_mac_Fonts.mm:    static DefaultFontNames defaultNames;
./modules/juce_graphics/native/juce_win32_Fonts.cpp:struct DefaultFontNames
./modules/juce_graphics/native/juce_win32_Fonts.cpp:    DefaultFontNames()
./modules/juce_graphics/native/juce_win32_Fonts.cpp:    static DefaultFontNames defaultNames;
 ✔

humm... This appears to be the case. Do JUCE developers typically need to trawl through native modules to see what is on offer? Or did I just pick an odd one out? :)

Might someone with experience and expertise provide a simple walk-through that exposes the basic structure of of JUCE?

Or is it documented somewhere already?


#2

Hard to know how to answer this! Almost all the platform-specific code tends to be inside the subfolders called "native" in each module, and the filenames have the OS name in them. The internal implementations for each OS will often be very different, but the outward behaviour should be the same!


#3

 

Juce holds together with duct tape, magic, and a hell of a lot of stellar work from Sir Jules ... woot!