In C++, many libraries can aid in implementing an IoT application, but most are low-level. For a high-level SDK, Macchina.io is an excellent choice, especially if you seek a robust framework that simplifies IoT application creation.
Macchina is not only a perfect solution for IoT applications but it’s also a well designed and implemented project, so the SDK users could easily understand and customize its behavior.
Let’s take a look inside the Macchina source code using CppDepend and discover some facts about its design and implementation.
Clean Design
When designing a C++ project with a well-organized folder structure, it’s important to consider modularity, maintainability, and scalability. Organizing your code by folders allows you to separate concerns, making it easier to navigate, modify, and extend.
As we can discover from the following DSM. The Macchina SDK is well structured using folders:
The technical layer based on the POCO library is isolated in the platform folder. The POCO C++ Libraries are powerful cross-platform open-source C++ libraries for building network- and internet-based applications that run on desktop, server, mobile, IoT, and embedded systems.
Here’s the dependency graph of some POCO libraries.
Let’s take a look inside the Foundation project to explore its structure:
The namespaces in the source code are used for 3 differents goals:
1- Modularize the application
Modern C++ librarires use extensively the namespaces to modalirize their code base, and they use the “Namespace-by-feature” approach. Namespace-by-feature uses namespaces to reflect the feature set. It places all items related to a single feature (and only that feature) into a single namespace. This results in namespaces with high cohesion and high modularity, and with minimal coupling between namespaces. Items that work closely together are placed next to each other.
2- Anonymous namespace.
Namespace with no name avoids making global static variable. The “anonymous” namespace you have created will only be accessible within the file you created it in.
3- Hiding details by convention
In C++ where there’s no way to hide public types to the library user (In C# the “internal” keyword did the job). It’s interesting to find a way to inform the library user that he dont need to use directly some specific types because they concern only the implementation.
A common idiom in modern C++, pioneered by the developers of the Boost libraries, is to separate symbols that form part of the implementation of your module (that is, don’t form part of the public API) but that have to be publicly available into a separate sub-namespace, by convention named detail.
The namespaces Poco.Details and Poco.Dynamic.Impl are used to hide the implmentation by convention.
Loose coupling enforced for more flexibility
Loose coupling is desirable because changes in one area of an application will require fewer changes in other parts of the system. In the long run, this can save significant time, effort, and costs associated with modifying and adding new features to the application.
Loose coupling can be achieved by using abstract classes or generic types and methods.
The Macchina source code contains more than 300 abstract classes:
For example, the client authenticator class is an abstract class that allows for multiple implementations, providing flexibility in the user authentication feature.
High cohesion is enforced
The single responsibility principle states that a class should not have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0–1]. The LCOM HS (HS stands for Henderson-Sellers) takes its values in the range [0–2]. A LCOM HS value highest than 1 should be considered alarming. Here are to compute LCOM metrics:
LCOM = 1 — (sum(MF)/M*F)
LCOM HS = (M — sum(MF)/F)(M-1)
Where:
- M is the number of methods in class (both static and instance methods are counted, it includes also constructors, properties getters/setters, events add/remove methods).
- F is the number of instance fields in the class.
- MF is the number of methods of the class accessing a particular instance field.
- Sum(MF) is the sum of MF over all instance fields of the class.
The underlying idea behind these formulas can be stated as follow: a class is utterly cohesive if all its methods use all its methods use all its instance fields, which means that sum(MF)=M*F and then LCOM = 0 and LCOMHS = 0.
LCOMHS value higher than 1 should be considered alarming.
Clean Implementation
Few big types in the code base
Big types are extremely complex to develop and maintain. Thes kind of classes could controls too many other classes in the system and has grown beyond all logic to become The Class That Does Everything.
In the Macchina SDK source code only few types are big.
Methods are small and easy to understand
Many metrics exist to detect complex functions, NBLinesOfCode,Number of parameters and number of local variables are the basic ones.
There are other interesting metrics to detect complex functions:
- Cyclomatic complexity is a popular procedural software metric equal to the number of decisions that can be taken in a procedure.
- Nesting Depth is a metric defined on methods that is relative to the maximum depth of the more nested scope in a method body.
- Max Nested loop is equals the maximum level of loop nesting in a function.
The max value tolerated for these metrics depends more on the team choices, there are no standard values.
In the Macchina SDK source code only few methods could be considered as complex.
Code is maintainable
The Maintainability Index is a software metric used to measure how easily a codebase can be maintained over time. It gives a quantifiable score based on various factors like code complexity, size, and readability, helping developers understand how changes in the code might affect its long-term maintainability.
Let’s discover the maintainability of the Machhina SDK methods using the CppDepend Treemap:
In the Metric View, the code base is represented through a Treemap. Treemapping is a method for displaying tree-structured data by using nested rectangles. The tree structure used in CppDepend treemap is the usual code hierarchy:
- C/C++ projects contain namespaces,
- Namespaces contain types,
- Types contains methods and fields.
The option Size of the treemap determines the size of rectangles which is by default proportional to the number of Lines of Code, and a code metric can be represented by coloring code elements rectangle, in our case the color is related to the maintainability.
As we can see only few methods are in red, and most of them concerns the test methods.
Conclusion
Macchina is known for its ease of use in developing IoT applications. Its implementation is clean, easy to understand, and customizable, making it one of the best choices for IoT applications.