Is using global static Identifiers Safe?

I’m trying to figure out the simplest way to declare Identifiers for my project and make them easy to access throughout my code. Of course I’d also like this method to be safe!

Right now I’ve got a header filed called Ids.h that contains the following:

#pragma once

#include "JuceHeader.h"

#define MAKE_ID(name) const static Identifier name = #name;

namespace Ids
{

namespace Global
{
MAKE_ID (userName)
MAKE_ID (userEmail)
MAKE_ID (userAddress)
}

namespace Project
{
MAKE_ID (projectName)
MAKE_ID (projectDate)
}

}

#undef MAKE_ID

Then it’s super easy to just drop Ids.h into any other file to access the identifiers.

Setting breakpoints, on my MBP these get instantiated before initialiseJuce_GUI . I know global statics are risky, but Identifiers don’t rely on the MessageManager in any way right? So what would be the risk in declaring them as global statics?

I’m curious how others have approached generating Identifiers, if anybody would like to share :slight_smile:

I came up with a way to guarantee when my Identifiers get constructed:


// Ids.h
#pragma once

#include "JuceHeader.h"

#define MAKE_ID(name) const Identifier name = #name;

struct Ids
{
    struct Global
    {
        MAKE_ID (userName)
        MAKE_ID (userEmail)
        MAKE_ID (userAddress)
    } global;

    struct Project
    {
        MAKE_ID (projectName)
        MAKE_ID (projectDate)
    } project;
};

const Ids& ids();

#undef MAKE_ID

// Ids.cpp
const Ids& ids()
{
    static const Ids ids();
    return ids;
}

Then the Ids are accessed via ids().project.projectName

However, when confirming when these Identifiers would get instantiated, I found that there are 6 static Identifiers that are created in the JUCE startup code (with the modules that I’m using). Does that mean these are safe to use as statics? I’ve seen forum threads that recommend avoiding the String::empty static. Would that advice apply to this Id situation?

As long as you don’t use any of them inside code which runs from another static constructor, then it’s fine, and it seems fairly unlikely that you’d do that. Certainly where we’ve used them in e.g. the projucer, it’s stuff that will only be used when the app is fully initialised and running.

(The difference with some of the other objects like String::empty and File::nonexistent, which you should never use and should disable with JUCE_ALLOW_STATIC_NULL_VARIABLES=0, is that they were so common that there were situations where they did end up being called from static constructors, which caused some really nasty bugs)

Ah, thanks Jules! Good to know. Do you think there is any kind of performance gain in choosing either of the methods I posted above?

Well, yes - if you’re using a function-local static to hold them, then the compiler may (and probably will) generate a mutex to make accessing it thread-safe, so you can’t safely call it from an audio thread