Skip to content
Peter Jonas edited this page Oct 13, 2025 · 4 revisions

To keep the source consistent, readable, modifiable, and easy to merge, we use a fairly rigid coding style. All patches are expected to conform to this style, with the exception of third-party modules, or patches to existing files that are already using a different style.

MuseScore's C++ style and conventions are mostly based on the Qt code style and conventions, but there are some differences.

The guidelines for other languages are based on common practice in those languages, defaulting to a similar style to our C++ style when no consensus is available.

C++

The style described here is valid for the master branch (and all branches derived from it) since MuseScore 4.0. The 3.x branch (and its derivatives) use the old coding style.

On the master branch, this style is enforced in pull requests by the codestyle CI check, which uses Uncrustify to format the code according to this config file, and gives an error if any style violations were detected.

You can run Uncrustify on your own machine to have your code formatted to this style automatically.

Indentation

  • 4 spaces are used for indentation
  • Spaces, not tabs!

Namespace

  • All namespaces must be child to the global namespace mu
  • Namespaces names must be lowercase so they can be easily distinguished from class names
 // Wrong
 namespace Cool {
     ...
 }

 // Correct
 namespace mu::cool {
     ...
 }

Includes

To keep compile times to a minimum, you should only #include what you actually use.

Tip

If you delete code, check whether the remaining includes are still needed. Qt Creator highlights includes that are not used.

Important

Wherever practicable, avoid using #include in header files. Instead, use a forward declaration in the header and put the #include in the implementation file. This helps because headers may themselves be included in other files, causing compile times to grow exponentially.

Includes should be specified in this order:

  1. Corresponding header file (only relevant in implementation files)
  2. System
  3. Qt
  4. Framework
  5. IOC
    1. Framework
    2. Other modules
    3. Current module
  6. Current module (Note: files from other modules should only be included via framework or IOC)
  7. Current class

Leave a blank line between each category of include. Includes in the same category should be specified in alphabetical order to reduce the likelihood of merge conflicts.

// System
#include <string>

// Qt
#include <QObject>
#include <QRect>

// Framework
#include "actions/actionable.h"
#include "async/asyncable.h"

// IOC
#include "ioc.h"
#include "global/isomeclass.h"      // Framework
#include "context/iglobalcontext.h" // Other module
#include "ianotherclass.h"          // Current module

// Current module
#include "types.h"

Header and implementation

  • Header files use the .h extension. Source/implementation files use .cpp.
  • Each header and implementation file must have license text at the top.
  • The header file must have a #pragma once guard immediately below the license.
  • Header files should only contain declarations, not definitions/implementations, except when necessary (e.g. function-template definitions).

Important

Don't put a singleton's implementation in the header file.

The class declaration should be in this order:

  1. Licence
  2. Guard (we use #pragma once)
  3. Includes
  4. Forward declarations
  5. Namespace
  6. Class declaration
  7. Qt macro (Q_OBJECT, Q_PROPERTY, Q_ENUMS...)
  8. Public methods (first constructor and destructor if they public)
  9. Public slots
  10. Signals
  11. Protected
  12. Private slots
  13. Private methods
  14. Private members
 // Correct
{LICENCE}

#pragma once

{INCLUDES}

namespace mu::cool {
class Rect : public QObject
{
    Q_OBJECT
public:
    explicit Rect(QObject* parent = nullptr);
    ~Rect();

    int width() const;
    int height() const;

    int square() const;

public slots:
    void setWidth(int w);
    void setHeight(int h);

signals:
    void squareChanged(int sq);

private:
    void updateSquare();

    QRect m_rect;
    int m_square = 0;
};
}
// Correct

{LICENCE}

#include "rect.h"

Rect::Rect(QObject* parent)
    : QObject(parent)
{
}

Rect::~Rect()
{
}

void Rect::updateSquare()
{
    int s = width() * height();
    if (s != m_square) {
        m_square = s;
        emit squareChanged(s);
    }
}

void Rect::setWidth(int w)
{
    m_rect.setWidth(w);
    updateSquare();
}

int Rect::width() const
{
    return m_rect.width();
}

void Rect::setHeight(int h)
{
    m_rect.setHeight(h);
    updateSquare();
}

int Rect::height() const
{
    return m_rect.height();
}

int Rect::square() const
{
    return m_square;
}
  • If there is initialization in constructors, always put a colon on a new line
// Wrong
ClassName::ClassName(int arg) : Base(), m_param(arg) {}

ClassName::ClassName(int arg) : Base(), m_param(arg)
{}

ClassName::ClassName(int arg) :
    Base(), m_param(arg)
{
}

// Correct
ClassName::ClassName(int arg)
    : Base(), m_param(arg)
{  
}

Declaring variables

  • Declare each variable on a separate line
  • Avoid short or meaningless names (e.g. "a", "rbarr", "nughdeget")
  • Single character variable names are only okay for counters and temporaries, where the purpose of the variable is obvious
  • Wait when declaring a variable until it is needed
  • For pointers or references, no space between the '*' or '&' and type (different from Qt)
  • Always initialize the variable with the initial value.
  • Use nullptr, rather than 0, to initialize pointers.
 // Wrong
 int a, b;
 char *c, *d;
 const Type &val = ...
 void func(const Type &val);

 // Correct
 int height = 0;
 int width = 0;
 char* nameOfThis = nullptr;
 char* nameOfThat = nullptr;
 const Type& val = ...
 void foo(const Type& val);
  • Variables and functions start with a lower-case letter. Each consecutive word in a variable's name starts with an upper-case letter (camelCase)
  • Constants all uppercase letters and underscore to separate words
  • Avoid abbreviations
 // Wrong
 short Cntr;
 int panel_height = 0;
 char ITEM_DELIM = ' ';

 // Correct
 short counter = 0;
 int panelHeight = 0;
 const char ITEM_DELIMITER = ' ';
  • Classes and structures always start with an upper-case letter.
  • Acronyms are camel-cased (e.g. XmlReader, not XMLReader).
  • Data members of classes are named like ordinary nonmember variables, but with a prefix m_.
  • Public members of struct (POD) are named like ordinary nonmember variables, with out any prefix.
 // Wrong
 class counter
 {
 ...
 private:
     int count;
 }

 class HTMLParser
 {
 ...
 private:
     char* _data;
 }

 // Correct
 class Counter
 {
 ...
 private:
     int m_count = 0;
 }

 class HtmlParser
 {
 ...
 private:
     char* m_data = nullptr;
 }

Whitespace

  • Use blank lines to group statements together where suited
  • Always use only one blank line
  • Always use a single space after a keyword and before a curly brace:
 // Wrong
 if(foo){
     ...
 }

 // Correct
 if (foo) {
     ...
 }
  • Surround binary operators with spaces
  • Leave a space after each comma
  • No space after a cast, avoid C-style casts when possible
 // Wrong
 char *blockOfMemory = (char* ) malloc(data.size());

 // Correct
 char* blockOfMemory = reinterpret_cast<char*>(malloc(data.size()));

Use safer type conversion when possible:

// Wrong
Chord* chord = static_cast<Chord*>(e);

// Correct
Chord* chord = toChord(e);

Braces

  • Use attached braces: The opening brace goes on the same line as the start of the statement. If the closing brace is followed by another keyword, it goes into the same line as well:
 // Wrong
 if (codec)
 {
     ...
 }
 else
 {
     ...
 }

 if (codec) {
     ...
     }

 // Correct
 if (codec) {
     ...
 } else {
     ...
 }
  • Exception: Function implementations (but not lambdas) and class declarations always have the left brace on the start of a line:
 static void foo(int g)
 {
     ...
 }

 class Moo
 {
     ...
 };
  • Do not put multiple statements on one line
  • If the body of the expression consists of one line, add braces anyway (different from Qt)
 // Wrong
 if (foo) bar();

 if (foo)
     bar();

 // Correct
 if (foo) {
     bar();
 }
  • Do not put 'else' after jump statements:
 // Wrong
 if (thisOrThat)
     return;
 else
     somethingElse();

 // Correct
 if (thisOrThat) {
     return;
 }

 somethingElse();

Parentheses

  • Use parentheses to group expressions:
 // Wrong
 if (a && b || c)

 // Correct
 if ((a && b) || c)

 // Wrong
 a + b & c

 // Correct
 (a + b) & c

Line breaks

  • Keep lines shorter than 120 characters; wrap if necessary
  • Commas go at the end of wrapped lines; operators start at the beginning of the new lines. An operator at the end of the line is easy to miss if the editor is too narrow.
 // Wrong
 if (longExpression +
     otherLongExpression +
     otherOtherLongExpression) {
 }

 // Correct
 if (longExpression
     + otherLongExpression
     + otherOtherLongExpression) {
 }

Casts

Important

Never use C-style casts in C++ code.

int foo = (int)5.3;      // Wrong
Dog* dog = (Dog*)animal; // Wrong

static_cast

Fundamental types

Always use static_cast when converting between fundamental data types, such as:

  • Integral types (bool, char, int, long, size_t, etc.)
  • Floating-point types (float, double, etc.)

Also use static_cast with:

  • Aliases of fundamental types (e.g. qreal and staff_idx_t)
  • Enumerated types (enum and enum class)
auto foo = static_cast<int>(5.3); // Valid: foo is 5
auto bar = static_cast<float>(2); // Valid: bar is 2.0

Important

Be careful when casting to a smaller or lower-precision type, such as from int to char, or from double to float. The conversion will succeed, but some information is lost in the process.

Similarly, be careful when converting from signed to unsigned, or from unsigned to signed. These cover different ranges of possible values, so conversion can give unexpected results in either direction, although technically no information is lost (assuming both types are otherwise the same).

Converting from float to int discards the fractional portion like in the example above. Also, the integer portion may overflow or underflow since floating-point types can represent a greater range of values than integral types with the same bit width. Doing the reverse, i.e. converting from int to float, may also lose information if the integer in question is of sufficient magnitude. A 32-bit floating point number can only represent integers continuously in the range ±2^24.

Composite types

static_cast is also the preferred option for converting pointers of composite types (i.e. class and struct). Use it whenever you can be certain that it will succeed.

At compile time, static_cast gives an error if neither type is an ancestor (via inheritance) of the other type.

auto dog = new Dog();
auto cat = static_cast<Cat*>(dog); // Compiler error!
// Error because Cat class is neither an ancestor nor a descendent of Dog class.

The error is useful, but its absence doesn't guarantee success. The program can give very strange behavior at runtime if you down-cast to a derived type when the object in question isn't actually an instance of the derived type.

// Dog class inherits from Animal class (directly or indirectly).
// This means every Dog is an Animal, but not every Animal is a Dog.

auto dog1 = getRandomDog();
auto animal1 = static_cast<Animal*>(dog1); // Up-cast, always safe.
animal1->move(); // Calls Animal::move() instead of Dog::move().

auto animal2 = getRandomAnimal();
auto dog2 = static_cast<Dog*>(animal2); // Down-cast, potentially unsafe.
dog2->fetch(); // Dog::fetch(), gives strange result if animal2 isn't a Dog.

The strange behavior can be predicted (see reinterpret_cast) but it's probably not what you want. Instead, you can avoid it by manually checking one of the object's properties prior to conversion.

if (animal2->sound() == "Woof!") {          // Definitely a Dog, so now
    auto dog2 = static_cast<Dog*>(animal2); // it's safe to down-cast and
    dog2->fetch();                          // access Dog-specific members.
}

static_cast performs no checks at runtime, so it has little to no impact on performance. However, manual checks may impact performance.

Note

If the manual check involves calling a virtual method, or anything that's slow to compute, you might as well use dynamic_cast instead.

dynamic_cast

If the type conversion isn't guaranteed to succeed, you could use static_cast with a manual check as mentioned above, or one of the following options.

When converting to a more specific subclass of MuseScore's EngravingObject, use the dedicated isType() and toType() functions:

if (chordrest->isChord()) {
    Chord* ch = toChord(chordrest);
    // Do something with `ch`
}

When converting to a more specific subclass of QObject, use Qt's dedicated qobject_cast, which returns nullptr at runtime if the cast is not possible:

if (auto *dialog = qobject_cast<QDialog*>(widget)) {
    // Do something with `dialog`
}

Otherwise, there is dynamic_cast, which performs a runtime check via RTTI and returns nullptr if the cast is not possible.

if (auto dog = dynamic_cast<Dog*>(animal)) {
    // Do something with `dog`
}

However, RTTI is slow, so the other options are preferred where possible. Also, RTTI relies on the types in question having at least one virtual method. Fortunately, the compiler gives an error if this condition is not satisfied. It also gives errors in all situations that would give errors with static_cast.

reinterpret_cast

Use reinterpret_cast when neither type is an ancestor of the other type. It performs no checks and has no performance overhead.

auto dog = new Dog();
auto cat = reinterpret_cast<Cat*>(dog); // No error.
std::cout << cat->sound(); // Could print "Woof!", "Meow!", junk data, or crash.
// Technically, the actual outcome could be predicted if we
// had complete knowledge of how both types are implemented.

reinterpret_cast is equivalent to a C-style cast, but it sends a different message to other developers:

  • Using a C-style cast means "I'm a C programmer and I don't know how to write C++ code."

  • Whereas using reinterpret_cast means "Trust me, I know what I'm doing. I tried all other options and none of them work here."

Most programmers will never have to use reinterpret_cast, and nor should they! It's really best avoided.

auto

When the type is repeated in the line (e.g. after a cast), use the auto keyword to avoid repetition.

// Wrong (too verbose)
AudioPluginsScannerMock* mock = dynamic_cast<AudioPluginsScannerMock*>(scanner.get());

// Correct (more readable)
auto mock = dynamic_cast<AudioPluginsScannerMock*>(scanner.get());

When the type is long (30+ chars?), e.g. due to long nesting and templateisation: If it's recurrent, create a descriptive alias with "using" keyword.

using ClipConstHolders = std::vector<std::shared_ptr<const ClipInterface> >;

If it only happens once, or e.g. if it is a verbose iterator:

IteratorRange<IntervalIterator<WaveClipChannel> >

auto will keep the code more readable:

for (const auto& clip : Intervals()) {
(...)
}

General exceptions

  • When strictly following a rule makes your code look bad, feel free to break it.
  • If there is a dispute in any given code, create a pull request with the proposed changes to the style code.

Tip

Don't fight the automatic code formatter (i.e. Uncrustify for C++ code). Either reconfigure it in a PR to give the desired output, or learn to deal with its current output. It's rarely worth sacrificing the overall consistency and readibility these tools provide, particularly for viewing diffs and resolving merge conflicts, in order to micromanage the few places where they fall short.

Qml

In general, the same style as for C++

  • Property names as well as C ++ variable names
  • Private property must be a separate object
 // Wrong
 Item {
     property var cntr
     property var internal_data

     ...
 }

 // Correct
 Item {

     property var counter: 0

     QObject {
         id: prv
         property var data: null
     }

     ...
 }
  • The opening brace goes on the same line as the start of the statement (with no exceptions)
 // Wrong
 Rectangle
 {
     width: 40
     height: 20
     ...
 }

 function do_something()
 {
     ...
 }

 // Correct
 Rectangle {

     width: 40
     height: 20
     ...
 }

 function doSomeThing() {
     ...
 }
  • This is the order of properties in a QML item
Object {
    id: someID

    // newline here
    
    anchors.someProperty: ...
    anchors.another: ...
    // Or:
    Layout.someProperty: ...

    // newline here

    someRegularProperty: ...
    anotherOne: ...

    // newline here

    navigation.someProperty: ...

    // newline here

    onSomeSignal: {
        doSomething()
    }

    onSomeSignalWithParameter: function(param) {
        soSomethingWith(param)
    }
}

CMake

//! TODO need to write like this

Bash

In general, a mix of our C++ style and Google's shell style, with some modifications.

#!/usr/bin/env bash

((${BASH_VERSION%%.*} >= 4)) || { echo >&2 "$0: Error: Please upgrade Bash."; exit 1; }

set -euxo pipefail          # omit 'x' in most scripts (it's mainly for debugging)

readonly FOO_BAR="Foo Bar"  # global shell constant
export BAR_BAZ="Bar Baz"    # environment variable

my_num=3                    # global shell variable

if (($# >= 1)); then
    # Remember to print messages on STDERR.
    echo >&2 "Do something"
    my_num=7                # modify the variable
fi

function print_something()
{
    local arg1="$1"     # local shell variable
    local -r arg2="$2"  # local shell constant
    shift 2
    # Prefer 'printf' to 'echo' when arguments are unknown.
    printf >&2 '%s\n' "${arg1}" "${arg2}" "$@"
}

print_something "one" "two" "three"

Python

Use PascalCase for class names and snake_case for everything else, including methods and variables within classes, i.e. FooBar.baz_qux().

Clone this wiki locally