New JUCE module format proposal!

You can add as many cpps as you like, but they have to be in the root folder of your module. (There’s no such thing as a cpp file that can’t be included via another one, so even if it’s a 3rd party file that’s in a subfolder, you can include it via a better-named wrapper cpp in the root folder)

Module CPP files
----------------

A module consists of a single header file and zero or more .cpp files. Fewer is better!

Ideally, a module could be header-only module, so that a project can use it by simply
including the master header file.

For various reasons it's usually necessary or preferable to have a simpler header and
some .cpp files that the user's project should compile as stand-alone compile units.
In this case you should ideally provide just a single cpp file in the module's root
folder, and this should internally include all your other cpps from their sub-folders,
so that only a single cpp needs to be added to the user's project in order to completely
compile the module.

In some cases (e.g. if your module internally relies on 3rd-party code which can't be
easily combined into a single compile-unit) then you may have more than one source file
here, but avoid this if possible, as it will add a burden for users who are manually
adding these files to their projects.

The names of these source files must begin with the name of the module, but they can have
a number or other suffix if there is more than one.

In order to specify that a source file should only be compiled on a specific platform,
then the filename can be suffixed with one of the following strings:

_OSX
_Windows
_Linux
_Android
_iOS

e.g.
juce_mymodule/juce_mymodule_1.cpp         <- compiled on all platforms
juce_mymodule/juce_mymodule_2.cpp         <- compiled on all platforms
juce_mymodule/juce_mymodule_OSX.cpp       <- compiled only on OSX
juce_mymodule/juce_mymodule_Windows.cpp   <- compiled only on Windows

Often this isn't necessary, as in most cases you can easily add checks inside the files
to do different things depending on the platform, but this may be handy just to avoid
clutter in user projects where files aren't needed.

To simplify the use of obj-C++ there's also a special-case rule: If the folder contains
both a .mm and a .cpp file whose names are otherwise identical, then on OSX/iOS the .mm
will be used and the cpp ignored. (And vice-versa for other platforms, of course).

super, thx - going to try it out right away!

Jules - in the JuceHeader.h file you have a handy using namespace juce.

But if I write some modules, which I have, there isn’t a handy way of adding other using directives here.

I guess the ideal solution would be for modules to specify the namespace they are using, and the projucer would offer to enable them all with using directives…

thoughts?

Couldn’t you do that by just adding the “using namespace” to the header of your own module?

We could have done the juce one that way too… May have been a better solution TBH.

Well, I sure can, but that brings my stuff into the global namespace for all the JUCE header files if my modules headers aren’t included last, which might be a bit dramatic…?

Yes, fair point… Another approach is to let the users add their own using declarations, which is probably what most modern C++ folks would recommend nowadays.

1 Like

Yeah, it’s what I’m doing for now anyway. Though I’m rather jealous of your global using…;0)

Is there some guide/tutorial/guideline on how to organize a module?

I have gathered a lot of classes over the years, that I’d wish to organize in one (or more) modules for convenience.

They are quite “external” from juce, meaning that they aren’t really fit for populating the juce namespace. I’d wish to have a sibling namespace instead

I don’t have any broad advice, but there shouldn’t be any problems using sibling namespaces.

i think that i would like see local copy option for modules to stay. It’s more easier then work in Linux environment anyway. if that option stays it wont harm any users who don’t use that option. But it may helpful for some cases.

I’m having a tough time creating my own modules using my own namespace. Is there a tutorial to follow for creating a module that lives inside a namespace other than ‘juce’? What if my module was written originally as part of a project and depends on “…/JuceLibraryCode/JuceHeader.h” ?

it seems like in the main module header file, you include all of the .h files for each sub-folder, and then in the main module.cpp file, you include all of the .cpp files for each sub-folder:

juce_audio_basics.h :

namespace juce {
  #include "buffers/juce_AudioDataConverters.h"
  #include "buffers/juce_FloatVectorOperations.h"
  #include ....
}

juce_audio_basics.cpp :

namespace juce {
  #include "buffers/juce_AudioDataConverters.cpp"
  #include "buffers/juce_FloatVectorOperations.cpp"
  #include ....
}

Also, where should I put my folder with my module? it’s pretty ugly to add this include at the top of my main module header file:

#include "../../JUCE-4.2.4/modules/juce_core/juce_core.h"

@jules @ed95 @t0m @Rail_Jon_Rogut @fabian i’m sure you guys are masters of this

When you’re including other modules, you shouldn’t use the full relative path, so to include juce_core, you’d do

#include <juce_core/juce_core.h>

That way it doesn’t matter where your module is relative to the others.

Please don’t @-include lots of people - the JUCE team will read every post.

your_module.h:

#include <juce_core/juce_core.h>

namespace mynamespace
{
    #include "namespacetest.h"
}

your_module.cpp:

#include "your_module.h"

namespace mynamespace
{
    #include "namespacetest.cpp"
}

namespacetest.h:

int somefunction();

namespacetest.cpp:

int somefunction()
{
    auto r = juce::Random();
    return r.nextInt();
}

Then, in code which uses your module:

auto i = mynamespace::somefunction();
1 Like

Do you see any drawback in opening and closing the namespace locally inside each .h and .cpp rather than globally in the module.h and module.cpp?

You could do it either way, though doing it globally means fewer repetitions of the same boilerplate code.

I think the module format is a great idea, but I ran into an issue working on varx: Having a single translation unit with all .cpp files in it means that you have to recompile everything when making a single change in one .cpp file. In my case this takes ~2 minutes. Also, as far as I know this impedes parallel compilation. So I ended up developing it as a separate project with separate translation units, and converted it back to the JUCE module format in the end.
What’s the best way to solve this? I noticed that the above docs discourage more than one translation unit per module…

Agreed. Even more, for a full DSP module, this could take dozens of minutes to have the full package (I won’t try to advise anyone to try to port Audio ToolKit, as it may even blow up the compiler itself for some crazy calls).
It would be nice to allow for static libraries to be added to the module, on top of the required headers, and nothing more.

I think you can add static libraries using the OSXLibs/windowsLibs/… values, though I’m not sure how the library search paths work in this case. But then you need to compile a static library for any platform/architecture your users might want, to include it in the module.

A module that would take care of this is a plus.
Then, the issue of the platform is not really relevant. It’s one for macOS and let’s say 2 for Windows (32 and 64bits). The other platforms can be compiled on their own if required. This way you still cover 99% of the audience and keep their compile time small.

You can include precompiled libraries in JUCE modules:

2 Likes