bkryza / clang-uml

Customizable automatic UML diagram generator for C++ based on Clang.
Apache License 2.0
592 stars 40 forks source link

How to include the namespaces with methods? #279

Closed rqhu1995 closed 3 months ago

rqhu1995 commented 3 months ago

I used clang-uml to generate the class diagram. Inside the classes, some methods defined in the namespaces are used. However, I found clang-uml would only include those namespaces with entities like struct or variables, etc. I created a demo to show my case here.

Project Structure

.
├── NameSpaces
│   ├── NSNoStruct.cpp
│   ├── NSNoStruct.h
│   ├── NSWithStruct.cpp
│   └── NSWithStruct.h
├── build
│   ├── .clang-uml
│   ├── Makefile
│   ├── cmake_install.cmake
│   ├── compile_commands.json
│   ├── diagrams
│   │   └── myproject_class.puml
│   └── main
└── main.cpp

.clang-uml

compilation_database_dir: .
output_directory: diagrams
diagrams:
  myproject_class:
    type: class
    glob:
      - ../NameSpaces/*.cpp
      - ../NameSpaces/*.h
      - ../main.cpp
    generate_packages: true
    package_type: directory

compile_commands.json:

[
{
  "directory": "/home/runqiu/projects/clanguml-demo/build",
  "command": "/usr/bin/c++  -I/home/runqiu/projects/clanguml-demo/NameSpace -g -std=gnu++20 -o CMakeFiles/main.dir/main.cpp.o -c /home/runqiu/projects/clanguml-demo/main.cpp",
  "file": "/home/runqiu/projects/clanguml-demo/main.cpp"
},
{
  "directory": "/home/runqiu/projects/clanguml-demo/build",
  "command": "/usr/bin/c++  -I/home/runqiu/projects/clanguml-demo/NameSpace -g -std=gnu++20 -o CMakeFiles/main.dir/NameSpaces/NSNoStruct.cpp.o -c /home/runqiu/projects/clanguml-demo/NameSpaces/NSNoStruct.cpp",
  "file": "/home/runqiu/projects/clanguml-demo/NameSpaces/NSNoStruct.cpp"
},
{
  "directory": "/home/runqiu/projects/clanguml-demo/build",
  "command": "/usr/bin/c++  -I/home/runqiu/projects/clanguml-demo/NameSpace -g -std=gnu++20 -o CMakeFiles/main.dir/NameSpaces/NSWithStruct.cpp.o -c /home/runqiu/projects/clanguml-demo/NameSpaces/NSWithStruct.cpp",
  "file": "/home/runqiu/projects/clanguml-demo/NameSpaces/NSWithStruct.cpp"
}
]

The demo source code:

NameSoaces/NSNoStruct.h

namespace NSNoStruct {
    void printNSNoStruct();
}

NameSoaces/NSNoStruct.cpp

#include <iostream>
namespace NSNoStruct {
    void printNSNoStruct() {
        std::cout << "Nothing to print" << std::endl;
    }
}

NameSpaces/NSWithStruct.h

#include <iostream>
#include "NSWithStruct.h"
namespace NSWithStruct {
    void createAndPrintStruct() {
        NSStruct s;
        std::cout << "a: " << s.a << ", b: " << s.b << std::endl;
    }
}

NameSpaces/NSWithStruct.cpp

#include <iostream>
#include "NSWithStruct.h"
namespace NSWithStruct {
    void createAndPrintStruct() {
        NSStruct s;
        std::cout << "a: " << s.a << ", b: " << s.b << std::endl;
    }
}

main.cpp

#include "NameSpaces/NSWithStruct.h"
#include "NameSpaces/NSNoStruct.h"
class TestClass {
public:
    int testClsMember = 0;
    TestClass() {
    }
    void setValue(int value) {
        testClsMember = value;
    }
    int getValue() {
        return testClsMember;
    }
    void TestNS() {
        NSWithStruct::createAndPrintStruct();
        NSNoStruct::printNSNoStruct();
    }
};

int main() {
    TestClass tc;
    tc.TestNS();
    return 0;
}

The output UML:

@startuml
package [..] as C_0001898935928681923136 {
package [NameSpaces] as C_0001777514856996079053 {
class "NSWithStruct::NSStruct" as C_0000814493620853568515
class C_0000814493620853568515 {
__
+a : int
+b : int
}
}
class "TestClass" as C_0001430318903375991351
class C_0001430318903375991351 {
+TestClass() : void
..
+TestNS() : void
+getValue() : int
+setValue(int value) : void
__
+testClsMember : int
}
}

'Generated with clang-uml, version 0.5.2
'LLVM version Ubuntu clang version 15.0.7
@enduml

image

There are two points that I would like to see in the UML but were currently not presented:

  1. I can only see the namespace NSWithStruct and only with the struct defined inside, but I can't see the methods in this namespace. For the other namespace NSNoStruct, I can't seem to make it included in the UML at all.
  2. I also want to present the relationship that the class TestClass uses these two namespaces, which currently is also not shown.
bkryza commented 3 months ago

@rqhu1995 Thanks for a detailed issue description. One thing I'm missing is the source file with declaration of NSStruct...

rqhu1995 commented 3 months ago

@rqhu1995 Thanks for a detailed issue description. One thing I'm missing is the source file with declaration of NSStruct...

Thank you for the prompt reply.

Sorry that I put the wrong header file for NSWithStruct. The declaration for NSStruct is inside the header file NSWithStruct.h, as follows:

namespace NSWithStruct {
    struct NSStruct {
        int a = 0;
        int b = 0;
    };
    void createAndPrintStruct();
}

It's just a struct with two variables initialized with 0.

bkryza commented 3 months ago

@rqhu1995 Below are my suggestions to your issues:

  1. I can only see the namespace NSWithStruct and only with the struct defined inside, but I can't see the methods in this namespace. For the other namespace NSNoStruct, I can't seem to make it included in the UML at all.

In C++ methods can only belong to a class or a struct, not to a namespace. What you have in your namespace are so-called free functions, which are not included in UML class diagrams (at least not at the moment). This means that any namespace not containing any classess, structs or enums is considered empty. In class diagrams generated by clang-uml, empty packages are not rendered, as this was causing a lot of clutter in various diagrams.

  1. I also want to present the relationship that the class TestClass uses these two namespaces, which currently is also not shown.

There is also no such relationship currently to present that a class uses a namespace - only relationships between classes.

Another thing is that you were generating packages from directories, however at the moment in clang-uml a class diagram can only contain packages of 1 kind simultanously, so if you want to see namespaces as packages you need to set in the config file:

   package_type: namespace

Things you can try to do:

  1. Use plantuml block in .clang-uml config to add any custom relationships in the diagram, e.g.:
    
    compilation_database_dir: .
    output_directory: diagrams
    diagrams:
    myproject_class:
    type: class
    glob:
      - ../NameSpaces/*.cpp
      - ../NameSpaces/*.h
      - ../main.cpp
    generate_packages: true
    package_type: namespace
    plantuml:
      before:
        - package [NSNoStruct] { }
      after:
        - 'NSNoStruct <-- {{ alias("TestClass") }}'
        - '{{ alias("NSWithStruct::NSStruct") }} <-- {{ alias("TestClass") }}'
which will give you the following diagram:
```plantuml
@startuml
package [NSNoStruct] { }
package [NSWithStruct] as C_0001873361144925008425 {
class "NSStruct" as C_0000814493620853568515
class C_0000814493620853568515 {
__
+a : int
+b : int
}
}
class "TestClass" as C_0001430318903375991351
class C_0001430318903375991351 {
+TestClass() : void
..
+TestNS() : void
+getValue() : int
+setValue(int value) : void
__
+testClsMember : int
}
NSNoStruct <-- C_0001430318903375991351
C_0000814493620853568515 <-- C_0001430318903375991351

'Generated with clang-uml, version 0.5.2-30-g2e861ee
'LLVM version Ubuntu clang version 17.0.6 (++20231209124227+6009708b4367-1~exp1~20231209124336.77)
@enduml
  1. You can refactor your code to use static methods instead of free functions, e.g.:
    
    // NameSpaces/NSNoStruct.h
    #pragma once

namespace NSNoStruct { struct impl { static void printNSNoStruct(); }; }

and then adjust code elsewhere and you should get a diagram like this:

```plantuml
@startuml
package [NSNoStruct] as C_0000378662955336434099 {
class "impl" as C_0001108148502093189503
class C_0001108148502093189503 {
{static} +printNSNoStruct() : void
__
}
}
package [NSWithStruct] as C_0001873361144925008425 {
class "NSStruct" as C_0000814493620853568515
class C_0000814493620853568515 {
__
+a : int
+b : int
}
}
class "TestClass" as C_0001430318903375991351
class C_0001430318903375991351 {
+TestClass() : void
..
+TestNS() : void
+getValue() : int
+setValue(int value) : void
__
+testClsMember : int
}

'Generated with clang-uml, version 0.5.2-30-g2e861ee
'LLVM version Ubuntu clang version 17.0.6 (++20231209124227+6009708b4367-1~exp1~20231209124336.77)
@enduml
rqhu1995 commented 3 months ago

Thank you for the detailed response, especially for the suggestions on replacing the free methods in namespaces with static methods. I believe such design of methods affiliation in C++ and uml has already indicated that my current choice of using namespace with only free methods is not a proper design of code, which requires refactoring.