musescore / musescore/MuseScore

Refactor MusicXML export function parameters as class member variables

Open
#31,277 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dev MusicXML tech debt
Dominant language
C++
Stars
15.1k
Forks
3.3k
Avg merge
2d 2h
Merged PRs (30d)
91

Description

The ExportMusicXml class has become a spaghetti junction of function parameters:

https://github.com/musescore/MuseScore/blob/da02a55d8c55ab8aa6f9674a9d80d471a55586e2/src/importexport/musicxml/internal/musicxml/export/exportmusicxml.cpp#L434-L445

The mess of parameters is repeated every time a function is:

If we make repeated parameters member variables of the ExportMusicXml class, it would make the whole of exportmusicxml.cpp much easier to read and maintain. For example, the above extract would become:

    // Methods (i.e. member functions)
    void findAndExportClef();
    void exportDefaultClef();
    void writeElement(EngravingItem* el);
    void writeMeasureTracks();
    void writeMeasureStaves();
    void writeMeasure();
    void repeatAtMeasureStart();
    void repeatAtMeasureStop();

    // Member variables
    Measure* m_measure;
    int m_partIdx;
    staff_idx_t m_staffIdx;
    track_idx_t m_track;
    MeasureNumberStateHandler m_mnsh;
    MeasurePrintContext m_mpc;
    FigBassMap m_fbMap;
    std::set<const Spanner*> m_spannersStopped;

The downside of using member variables is that methods are no longer stateless and purely functional (i.e. members can accessed or modified in ways – or at times – that are not valid). However, this is mush less of a concern when the relevant members are declared private with no external getters or setters, as would be the case here.

Furthermore, using member variables makes for cleaner, DRYer code, and it prevents the same variable names being used to mean different things in different functions. This all helps to make mistakes less likely in the first place.


The offending parameters are:

// MAIN PARAMETERS
// These should become class members.
Measure* measure;     // aka 'm'
int partIdx;          // aka 'partIndex'
staff_idx_t staffIdx; // aka 'staff' (but that's confusing because there's a 'Staff' class)
track_idx_t track;    // Not 'trackIdx' because there is no 'Track' class.

// PER-PART PARAMETERS
// These should become members and must be reset for each part.
MeasureNumberStateHandler mnsh;
MeasurePrintContext mpc;
FigBassMap fbMap;
std::set<const Spanner*> spannersStopped;

// DERIVED PARAMETERS
// We can either get rid of these or turn them into members
// depending on what gives better readability or performance.
Part* part                  = m_score->parts().at(partIdx); // aka 'p'
int nstaves                 = part->nstaves();
bool useDrumset             = part->instrument()->useDrumset();
track_idx_t partStartTrack  = part->startTrack();           // aka 'strack'
track_idx_t partEndTrack    = part->endTrack();             // aka 'etrack'
staff_idx_t startStaffIdx   = track2staff(partStartTrack);  // aka 'startStaff', 'staffCount'
staff_idx_t endStaffIdx     = startStaffIdx + nstaves;      // aka 'endStaff'
staff_idx_t partRelStaffNo  = (nstaves == 1) ? 0 : (staffIdx - startStaffIdx);
bool isLastStaffOfPart      = staffIdx == endStaffIdx - 1;

// MULTIPLE MEANINGS
// Some parameter names are used to mean different things in different functions.
// If converted to members the names must be unique, which prevents confusion.
track_idx_t staffStartTrack   = staffIdx * VOICES;          // aka 'strack'
track_idx_t staffEndTrack     = staffStartTrack + VOICES;   // aka 'etrack'

// ALREADY A MEMBER VARIABLE
// These members are being passed in as parameters unnecessarily.
Attributes m_attr;
XmlWriter m_xml;

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/importexport/musicxml/internal/musicxml/export/exportmusicxml.cpp, comparing the declarations around L434-L445 with the definition around L8297-L8304 and call around L8455. Trace which per-part parameters must be reset, then confirm the refactor removes the repeated parameters while preserving MusicXML export behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.