Major Changes to our C/C++ System Model

This article was updated on 2026-10-27 to reflect changes in version 26.6.1.201 of Sonargraph and higher.

When we added C/C++ to Sonargraph our work was heavily influenced by John Lakos famous book “Large Scale C++ Design”. In this book Mr. Lakos presented a solid system to define the architecture of C++ systems based on components. A component in the most simple case is the combination of a header file and a source file, e.g. User.h and User.cpp. The header declares all elements that can be used from outside of the component, while the source file contains the implementation of the functionality. This unites the header and the source file into a logical component, that is better suited for dependency analysis.

But this approach also has some downsides. For starters, since a component can contain several files that could be coming from different directories, where do you anchor them in the navigation view? We decided to anchor them in the directory of the source file. This works reasonably well, but there are cases when it does not. For example, especially in C++, you can have header only components. E.g. a class that only has inline methods and therefore does not need a source file. This component will be anchored in the directory of the header file, usually an include directory.

Now imagine a typical C++ module with a source directory and an include directory for the public header files. Now it is easy to imagine a case where components from the source directory are using a header only component from the include directory and this component again depends on another component in the source directory. This would create a cyclic dependency between the source directory and the include directory, although in real life there is no cyclic dependency.

This is shown in the screenshot above. This cycle only exists because of the way we create components. If we decide to skip component creation and let each header and source file be their own component we get a cycle free picture:

On the other hand, if we dig a little deeper into the source directory, the model using component construction reveals real component cycles:

Those cycles will be invisible in a physical exploration view when we skip component construction.

Here the cycles disappeared because the source files do not have direct dependencies between them. If a source file calls a function implemented in another source, the dependency points to the declaration in the associated header file. Since most calls happen from source files this picture is not suitable for detecting cyclic dependencies. For that you would have to use the logical exploration view:

In the logical view declarations and implementations are melted together. But the advantage here is that all the cycles shown here are real. With component construction some of the cycles you would see in the physical exploration view are not real, as explained above.

Another advantage of skipping component construction comes when we want to create a physical architectural model. Lets assume we have 3 files:

UserPrivate.h
User.h
User.cpp

The header ending with “Private” contains the private interface of the things implemented in User.Cpp. Now you could create an architecture model that takes this into consideration:

artifact User
{
    include "User*"

    interface Public
    {
        include "User.h"
    }
    interface Private
    {
        include "User.h"
        include "UserPrivate.h"
    }
}

This would not have been possible in the old model where all three files would have ended up in the same component. Please notice, that with the new model the component names contain the file extension. In the old model extensions were not needed since headers and sources would be melted together into a singe component.

And with Sonargraph 26.6.1 we added a third option – dependencies point to definitions. In that model all dependencies do not point to declarations as long as a definition is available in the system. Undefined elements declared in an internal header file get a warning marker. This model again allows you to detect cycles comfortably, but it takes away the obvious advantages of the declaration based model with respect to architecture definition. To avoid the creation of false cycles we are marking all dependencies between a header file and its implementation files as irrelevant for architecture and cycle detection. That means they will not show up in the exploration or cycle view. You can still them in the outgoing parser dependencies slave view under a source view. This option also provides another feature. If you are using a class name “in name only”, e.g. you are not depending on the inner structure of it, this dependency will also be marked as irrelevant.

As you can see cyclic dependencies are now again clearly visible, as with the component based approach.

So since all three approaches have pros and cons we let you decide, which model you prefer. By default old systems created before the release of Sonargraph 26.3 would have components enabled, while new systems would default to the middle option: “Dependencies point to declarations”. You can change this system setting via the System/Configure dialog. Changing it requires a complete re-parse of the system.

If you are unsure about the architecture filter names used in the new model please remember, that you can always see the filter names in the properties view:

For every selected source and header files you can see where there are assigned and which filter name to use.

If you have questions or suggestions about this new feature, please use the comment section below.

Leave a Reply

Your email address will not be published. Required fields are marked *