Latest JUCE coding standards as of February 2025.
The JUCE codebase has a very strict and consistent coding style. The style evolved gradually over the years, incorporating generally acknowledged best-practice C++ advice, experience, and personal preference.
In response to requests for a set of rules that the codebase follows, this is an attempt to list some of the guidelines that we think are most important.
Some of these rules are universally accepted as basic C++ good practice. Some of it is JUCE-specific. A lot of it is just personal taste! We’re not claiming this to be a definitive set of rules by which you should live, and we’re not particularly interested in arguing or defending any of the points here – it's just a description of the way things ended up after many years of coding. YMMV!
This principle pretty much summarises the essence of what it means to write good code, in all languages, at all levels. Other people have explained it better than we can do here – google for D.R.Y. and you’ll find many tutorials. If you only pay attention to one piece of advice in this document, make it this one!
The following rules won't make any difference at all to what the code actually does, but aesthetics and consistency are really important in making your code understandable.
if (x == 0) { // No!
foobar();
return "zero";
}
if (x == 0) // Yes!
{
foobar();
return "zero";
}bool operator== (Foo))x = 1+y - 2*z / 3; // Bad
x = 1 + y - 2 * z / 3; // Good! operator should always be followed by a space, e.g. if (! foo)~ operator should be preceded by a space, but not followed by one.++ and -- operators should have no spaces between the operator and its operand.foo (x,y); // No
foo (x ,y); // No
foo (x , y); // No
foo (x, y); // Yesfoo (123);foo();foo[1]if, for, while, do statements when they’re preceded by another statement. E.g.{
int xyz = 123;
if (xyz != 0)
foo();
foobar();
}} (unless the next line is just another close-brace)if statements all-on-one-line…if (x == 1) return “one”;
if (x == 2) return “two”;
if (x == 3) return “three”;auto myLambda = [] { return 123; };
auto myLambda = [this, &x] (int z) -> float { return x + z; };
auto longerLambda = [] (int x, int y) -> int
{
// ...multiple lines of stuff...
};SomeObject* myObject = getAPointer();
SomeObject& myObject = getAReference();Yes - we know that many people would argue that a more technically correct layout for such a declaration would be:
SomeObject *myObject = getAPointer();
SomeObject &myObject = getAReference();But we think it makes more sense for the asterisk to stick to the type name, because the pointer-ness is a quality that belongs to the type, not to the variable. The only time that this can lead to any confusion is when you're declaring multiple pointers of the same type in the same statement – which leads on to the next rule…
SomeObject* p1, *p2;instead split them out onto separate lines and write the type name again, to make it quite clear what's going on, and avoid the danger of missing out any vital asterisks.
SomeObject* p1;
SomeObject* p2;Or better still, use a smart-pointer or typedef to create a type-name that doesn’t require the asterisk or ampersand!
const modifier before the type name, e.g.const Thing& t; // yes!
Thing const& t; // no!Both do the same job, but the former style is closer to the way you'd verbally describe the type in English, i.e. verb-before-noun order. There are arguments in favour of using a trailing const because it makes things easier in very complex type names, but if you have a type name that contains multiple levels of const modifier keywords, you should probably have used typedefs to simplify it into a more manageable typename anyway.
vector<int>template <typename Type1, typename Type2>auto xyz = foo + bar // This makes it clear at a glance
+ func (123) // that all the lines must be continuations of
- def + 4321; // the preceding ones.
auto xyz = foo + bar + // Not so good.. It takes more effort here
func (123) - // to see that "func" here is actually part
def + 4321; // of the previous line and not a new statement.
// Good:
auto t = AffineTransform::translation (x, y)
.scaled (2.0f)
.rotated (0.5f);
// Bad:
auto t = AffineTransform::translation (x, y).
scaled (2.0f).
rotated (0.5f);// where possible, rather than /* */, as this makes it easier to comment-out large blocks of code when debugging.// comment// yes!
//no!Multi-line comments are aligned vertically on the left, e.g.
/* This is correct
*/
/** This is also correct
*/
/* This is wrong!
*/0xabcdef0.0 // yes!
0.0f // yes!
0. // no!
0.f // no!
.1 // no!
.1f // no!String! For some reason I seem to constantly see this kind of bloat in people’s code, but can’t understand the reason.. e.g.String w = String (“World”); // Why say it twice!??
auto w = String (“World”); // This is OK, but still longer than needed
String w (“World”); // This is the most compact way to write it
auto b = String (“Hello “) + w; // NO! This is a waste of typing!
auto b = “Hello “ + w; // This does exactly the same thing (given
that w is a String, not a char*)
myVariableNameMyClassNameauto, and the name should reflect the purpose of the variable, not its type.JUCE_.enum class MyEnum
{
enumValue1 = 0,
enumValue2 = 1
};T or other one-character template parameters. It doesn't take much effort to give them a helpful name, and T can clash with macros defined in some careless 3rd party headers.const, make it const! Herb Sutter has an interesting take on the meaning of const with respect to the thread-safety of a class that's worth reading about here.override specifier, and NEVER give it a redundant virtual keyword!noexcept. Try to do this everywhere possible as it can have a dramatic effect on performance in some situations - apparently up to 10x in extreme cases.const makes the code more verbose, and that may add unnecessary visual clutter if the constness is unimportant or obvious. Or it could be a positive thing if it’s useful for the reader’s attention to be drawn to the fact that this variable is constant. A general rule is that in very short block of code, don’t worry about making local variables const. In longer blocks where a variable is used many times, you may want to make it const if you think it’s a helpful tip to the reader. Note that In almost all cases declaring a primitive value as const makes no difference whatsoever to the compiler’s code generation.constexpr!char* const foobar = getFoobar();.if (myPointer). Always avoid that implicit cast-to-bool by writing it more fully:if (myPointer != nullptr)if (! myPointer), instead always writeif (myPointer == nullptr)The reasoning here is that it’s more readable because it matches the way you’d read out the expression in English - i.e. “if the variable myPointer is null”.
static_cast can be a bit long-winded when you just want to trivially cast an int to a float. But whenever a pointer or template or non-primitive type is involved, always use modern casts. And when you're reinterpreting data, always use reinterpret_cast.juce::int64 (and other types) but this was added before C++11 introduced int64_t. We’d encourage using either of these in preference to long long.delete, deleteAndZero, etc. There are very very few situations where you can't use a smart pointer or some other automatic lifetime management class.new unless there's no alternative. Whenever you type new, always treat it as a failure to find a better solution. If a local variable can be allocated on the stack rather than the heap, then always do so.new or malloc to allocate a C++ array. Always prefer a juce::HeapBlock or some other container class.malloc or calloc at all!ScopedPointer or std::unique_ptr. Whenever possible, pass an object as a reference rather than a pointer. If possible, make it a const reference.= {}; syntax as your first choice for doing this. If for some reason that's not appropriate, use the zerostruct() function, or in case that isn't suitable, use zeromem(). Avoid memset().Component::deleteAllChildren() as a last resort – never use it if there's a cost-free alternative.juce::ScopedPointer class was written to be compatible with pre-C++11 compilers, so although it does offer C++11 move functionality for supported compilers, it's not as versatile as std::unique_ptr. So if you can use std::unique_ptr in your own code, that's probably a better bet. We may eventually migrate the JUCE codebase to std::unique_ptr.std::unique_ptr to indicate that the caller takes ownership. We will at some point be moving to that style, but in the meantime, functions are annotated to make clear how ownership is passed around.Special topic for this one, as it’s a common question and something that people do frequently, and is a bit fiddly to explain. In its current form, juce::String is ref-counted, so passing them by value is cheap.. However, it’s not as cheap as passing them by reference. When you want to pass a String into a function, you do it in various ways:
void foo (const String&);
void foo (String);
void foo (String&&);
void foo (StringRef);In most cases, it really doesn’t matter which one you choose, as the performance implications are going to be irrelevant most of the time. But if you want to choose the optimal method, the rules are (roughly):
std::move out of the parameter rather than copying from it.const String& or a String&&. You’d only need to go to this much trouble in really extreme situations though!String provides, then prefer to pass it as a StringRef. That way a string literal can be passed in without the overhead of creating a String object at all.String methods than StringRef provides, then just pass it as a const String&.struct rather than class since you’re generally going to have all public members, and this will save a line of code doing the initial public:class Thing : public Foo,
private Bar
{public/private/protected keyword for each inherited classFoo (const Foo&) = delete; syntax alongside your other constructors if that works better.JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR or JUCE_LEAK_DETECTOR macro.explicit. Obviously there are cases where you do want implicit conversion, but always think about it carefully before writing a non-explicit constructor.NULL, null, or 0 for a null-pointer! (And for god's sake don't be tempted to use 0L, which is the worst of the lot!) Always use nullptr!else statement after a return! The LLVM coding standards give a good explanation of this, but once you think about it, it's basic common sense. Since I first noticed this one, it has become one of my pet-hates when I see it done in other people's code!if (foobar())
return doSomething();
else // Never!
doSomethingElse();
if (foobar())
return doSomething();
doSomethingElse(); // Better!T() macro to wrap string literals, but that has been deprecated for many years now. Just write your strings as plain old C++ string literals and the JUCE String and StringRef classes will deal with them.CharPointer_UTF8 class, so that when cast to a String, everything's nice and clear about the format that's being used. The Projucer has a built-in tool that will convert unicode strings to valid C++ code and handle all this for you. Read more.#undef them after you've used them, if possible.++ or -- operators, never use post-increment if pre-increment could be used instead. Although it doesn't matter for primitive types, it's good practice to pre-increment since this can be much more efficient for more complex objects. In particular, if you're writing a for loop, always use pre-increment, e.g. for (int = 0; i < 10; ++i)auto* f = getFoo();
if (f != nullptr)
f->doSomething();
// ...lots of intervening code...
f->doSomething(); // oops! f may be null!...instead, always prefer to write it like this, which reduces the scope of the pointer, making it impossible to write code that accidentally uses a null pointer:
if (auto* f = getFoo())
f->doSomething();
// f is out-of-scope here, so a null-pointer dereference is impossible.(This also results in cleaner, more compact code).
const Foo&. This is usually the right thing to do for complex objects (e.g. Array, String, etc), but when you pass a reference, it prevents the compiler from using a whole slew of optimisation techniques on the call-site. For example, it means that often there's no way for the compiler to really know whether the function will modify the original value (via const_cast) or whether it will modify a memory address which is an offset from the location of the object. So, the best-practice advice from the guys who write the optimisers is: Always stick to pass-by-value if possible, and only use references if the price of calling the copy constructor is very high. This is particularly true in the case of small objects whose overall size is actually not much bigger than the size of a pointer. Some juce classes which should always be passed by value include: Point, Time, RelativeTime, Colour, all of the CharPointer_XYZ classes, Identifier, ModifierKeys, JustificationType, Range, PixelRGB, PixelARGB, Rectangle.fabs, sqrtf, powf etc, instead always use the polymorphic functions std::abs, std::sqrt, std::sin, std::cos, std::pow, etc.int as their index type, which differs from the standard library’s use of size_t, but it seems that over the years, the C++ standards committee have gradually come to feel that using an unsigned type was a mistake. However, the mismatch does make it a bit annoying to have to convert index values when interoperating with the STL. (But since a lot of loop iteration now happens with range-based-for, this isn’t quite so bad any more).unsigned as a type on its own - always write unsigned int if that’s what you mean. The adjective on its own just seems like an unfinished sentence.int8, uint8, int16, uint16, int32, uint32, int64, uint64 - these are what we suggest using when a specific bit size is needed. Since the standard library has introduced std::uint32_t etc we also sometimes use those, but the juce ones are slightly shorter and have never caused problems with name clashes. auto x = 0.0f; // OK: obviously a float
auto x = 0.0; // OK: obviously a double
auto x = 0; // Not OK! Nothing makes it obvious that by '0' you mean a signed
// int rather than an unsigned int, long, int64 etc.
for (int i = 0; i < someNumber; ++i) // OK: clear that you mean a signed int
bool someCondition = false; // OK: clearer than using auto
auto someResult = thisReturnsABool(); // Use auto if the RHS is an expression