written by iosonopersia, August 2017
MK4duo is a Marlin fork that wants to provide a cleaner, simpler to understand code to its user-base. It was the first Marlin fork to exploit the possibility of recursive subfolder compilation offered by the Arduino IDE starting from version 1.6.3.
Code was separated between different subfolders in order to make clear to the programmers which thematic group a file belongs to.
The work needed to clean-up MK4duo code isn't finished yet: this is the reason why it's very important that everyone who's willing to contribute to this project knows well how we want to achieve that goal.
Remember: a cleaner code is a more robust code!
If you feel that your contribution still needs some work, or that there are sections in the firmware code that need to be fixed/completed, please add a comment in the right place starting with the TODO tag, like this one:
// TODO: we should implement counterclockwise functionality for this function
.
TODO tags can be pretty useful: it's possible, for example, to extract the complete list of TODOs simply by running a command like git grep "TODO"
in the repository folder.
Try to minimize the number of variables used and to define them inside the function scope: this way, when the function will return, that memory will be freed. For coding standards you can refer to the good Marlin documentation written by thinkyhead: Coding Standards.
MK4duo code is spread across a lot of src/ subfolders. Each one is named after its role in the code: this makes really intuitive finding the part of the code you think should be modified. Dividing code files in subfolders is an important step in achieving our goal but we shouldn't abuse of this method: a lot of folders are confusing just like a lot of files!
As for now, we think we've reached a good balance. If you think something should be changed, feel free to contact the developers!
Digging deeper in MK4duo code, we encounter the problem of code refactoring. MK4duo contains a lot of functions: following the C coding style won't help us cleaning our code. Actually, we need to provide a gerarchical code structure to our functions. Two main possibilities are offered by the C++ language: namespaces and classes.
Since our need is simply to collect functions in thematic groups, the better way would be to use namespaces. The only problem here is that with this tecnique it's difficult to hide private variables and functions, since namespaces are thought to expose groups of public variables and functions.
This is why we went the other way around, choosing to use classes: this makes really simple to separate in a clear way public elements from the private ones, enforcing information hiding and encapsulation paradigms. However, since every variable and every method in our classes is marked as static (remember that this is NOT an OOP project, we use classes ONLY to separate similar functions), we do not make use of keywords such as new and delete. Let me explain this just another time: our classes DO NOT define objects, they ONLY do the job of namespaces but better (in this case, of course).
- For every class we have a CLASSNAME.h file in which we put the declaration of the class (everything there is static!) and a CLASSNAME.cpp in which we write the body of every function declared in the header file (except for inline functions);
- Public variables and functions should be accessed using the C++ scope resolution operator: CLASSNAME::aVariable , CLASSNAME::aFunction();
- No object should be initialised with new nor used to access public elements of a class (for example like this: CLASSNAME obj; obj.something;), since for every object (initialised or not) at least one pointer is needed (and we don't want to waste memory, right?).
Before the release of MK4duo version 4.3.25, code for gcode execution was something like this:
switch (gcode_number) {
case 0:
case 1: gcode_G0_G1(); break;
[...]
#if ENABLED(SOME_FEATURE)
case 98: gcode_G98(); break;
#endif
case 99: gcode_G99(); break;
}
Not to mention the mcode-related switch (mcodes are defined in the range between 0 and 999!!!). This caused a huge and confusing code to be mantained and controlled carefully, because that's a critical section of the firmware.
In version 4.3.25 we overcame this problem, which still affects Marlin. We've collected every gcode in an array containing pointers to the gcode handler functions: more structure equals less code! That gave us the opportunity to replace those big switch statements with a simple and fast binary search algorithm which finds the correct gcode handler function pointer inside that array.
This improved code readability, length (and of course PROGMEM usage), efficiency and speed, while using only some additional bytes of SRAM. Always remember this as an example of how the code can be improved a lot avoiding big and complex switch statements!
MK4duo is already full of macros, since it embraces conditional compiling. A lot of other macros are used to prevent code duplication and others provide some useful services too. But they make the code less readable: we try to avoid and delete useless macros, and you should try also!