Added option to skip redundant dependency relationships
This commit is contained in:
@@ -83,7 +83,7 @@ Nowadays, this file can be generated rather easily using multiple methods:
|
||||
|
||||
### Invocation
|
||||
|
||||
By default, `config-uml` will assume that the configuration file `.clang-uml`
|
||||
By default, `clang-uml` will assume that the configuration file `.clang-uml`
|
||||
and compilation database `compile_commands.json` files are in the
|
||||
current directory, so if they are in the top level directory of a project,
|
||||
simply run:
|
||||
|
||||
@@ -37,7 +37,8 @@ diagrams:
|
||||
```
|
||||
|
||||
## Classes and their properties
|
||||
The basic class diagram generated by `clang-uml` and rendered using PlantUML looks like this:
|
||||
The basic class diagram generated by `clang-uml` and rendered using PlantUML
|
||||
looks like this:
|
||||
|
||||

|
||||
|
||||
@@ -45,12 +46,15 @@ Member types and method return types are rendered at the end after `:` sign.
|
||||
|
||||
Static methods and members are underlined.
|
||||
|
||||
In case method argument lists are too long and not required for diagram readability, they can be suppressed completely
|
||||
or abbreviated by setting `generate_method_arguments` option to either `none`, `abbreviated` or `full` (default).
|
||||
In case method argument lists are too long and not required for diagram
|
||||
readability, they can be suppressed completely or abbreviated by setting
|
||||
`generate_method_arguments` option to either `none`, `abbreviated` or `full`
|
||||
(default).
|
||||
|
||||
|
||||
### Excluding private or protected members from the diagram
|
||||
In order to only include public members in the class diagrams, we can add the following inclusion filters:
|
||||
In order to only include public members in the class diagrams, we can add the
|
||||
following inclusion filters:
|
||||
```yaml
|
||||
include:
|
||||
access:
|
||||
@@ -68,7 +72,8 @@ To render only classes without any properties an exclusion filter can be added:
|
||||
|
||||
## Relationships
|
||||
|
||||
The following table presents the PlantUML arrows representing each relationship in the class diagrams.
|
||||
The following table presents the PlantUML arrows representing each relationship
|
||||
in the class diagrams.
|
||||
|
||||
| UML | PlantUML |
|
||||
| ---- | --- |
|
||||
@@ -81,17 +86,18 @@ The following table presents the PlantUML arrows representing each relationship
|
||||
| Nesting (inner class/enum) |  |
|
||||
|
||||
|
||||
By default, a member from which a relationship has been added to the diagram between 2 classes will also be rendered
|
||||
inside the class. This behaviour can be however disabled by adding the following option to the
|
||||
diagram definition:
|
||||
By default, a member from which a relationship has been added to the diagram
|
||||
between 2 classes will also be rendered inside the class. This behaviour can be
|
||||
however disabled by adding the following option to the diagram definition:
|
||||
```yaml
|
||||
include_relations_also_as_members: false
|
||||
```
|
||||
|
||||
### Relationships to classes in containers or smart pointers
|
||||
`clang-uml` will automatically detect class members as well as method arguments, which reference or own
|
||||
values of types relevant for a given diagram but wrapped in smart pointers or containers and still generate
|
||||
relationship between these classes, for instance the following code:
|
||||
`clang-uml` will automatically detect class members as well as method arguments,
|
||||
which reference or own values of types relevant for a given diagram but wrapped
|
||||
in smart pointers or containers and still generate relationship between these
|
||||
classes, for instance the following code:
|
||||
|
||||
```cpp
|
||||
class A { };
|
||||
@@ -114,9 +120,9 @@ results in the following diagram:
|
||||
|
||||
## Inheritance diagrams
|
||||
|
||||
A common type of class diagram is an inheritance diagram, where only subclasses of a specific base class are
|
||||
included and only the inheritance relationships are rendered. This can be easily achieved in `clang-uml` through
|
||||
inclusion filters:
|
||||
A common type of class diagram is an inheritance diagram, where only subclasses
|
||||
of a specific base class are included and only the inheritance relationships are
|
||||
rendered. This can be easily achieved in `clang-uml` through inclusion filters:
|
||||
```yaml
|
||||
include:
|
||||
subclasses:
|
||||
@@ -126,9 +132,10 @@ inclusion filters:
|
||||
```
|
||||
|
||||
## Including packages in the diagram
|
||||
By default, `clang-uml` will render all element names including a namespace (relative to `using_namespace` property),
|
||||
e.g. `ns1::ns2::MyClass`.
|
||||
In order to generate packages in the diagram for each namespace instead, the following option must be set to `true`:
|
||||
By default, `clang-uml` will render all element names including a namespace
|
||||
(relative to `using_namespace` property), e.g. `ns1::ns2::MyClass`.
|
||||
In order to generate packages in the diagram for each namespace instead, the
|
||||
following option must be set to `true`:
|
||||
|
||||
```yaml
|
||||
generate_packages: true
|
||||
@@ -138,8 +145,9 @@ which results in the following diagram:
|
||||
|
||||

|
||||
|
||||
In case the code base is structured based on subdirectory instead of namespaces, packages can be generated
|
||||
based on the location of a given declaration in the filesystem tree, by adding also the following option:
|
||||
In case the code base is structured based on subdirectory instead of namespaces,
|
||||
packages can be generated based on the location of a given declaration in the
|
||||
filesystem tree, by adding also the following option:
|
||||
|
||||
```yaml
|
||||
package_type: directory
|
||||
@@ -149,13 +157,15 @@ which results in the following diagram:
|
||||
|
||||

|
||||
|
||||
> In this case make sure that the root path of the configuration file is properly configured
|
||||
> for your project, if necessary add `relative_to` option to denote the root path
|
||||
> against which all relative paths in the config file are calculated.
|
||||
> In this case make sure that the root path of the configuration file is
|
||||
> properly configured for your project, if necessary add `relative_to` option to
|
||||
> denote the root path against which all relative paths in the config file are
|
||||
> calculated.
|
||||
|
||||
## Class context diagram
|
||||
Sometimes it's helpful to generate a class diagram depicting only direct relationships of a given class, e.g.
|
||||
within the classes' documentation page, this can be easily achieved using `context` inclusion filter:
|
||||
Sometimes it's helpful to generate a class diagram depicting only direct
|
||||
relationships of a given class, e.g. within the classes' documentation page,
|
||||
this can be easily achieved using `context` inclusion filter:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
@@ -164,23 +174,25 @@ within the classes' documentation page, this can be easily achieved using `conte
|
||||
```
|
||||
|
||||
## Disabling dependency relationships
|
||||
In many cases, dependency relationships between classes can clutter the diagram too much, for instance consider this
|
||||
diagram:
|
||||
Dependency relationships are inferred whenever a class uses another class, thus
|
||||
often dependency relationship will be rendered in addition to other
|
||||
relationships such as association or inheritance. By default, `clang-uml` will
|
||||
remove these redundant dependency relationships, however if it is necessary to
|
||||
retain them it can be done using the following option:
|
||||
|
||||

|
||||
```yaml
|
||||
skip_redundant_dependencies: false
|
||||
```
|
||||
|
||||
where the dependency relationships do not bring much information into the diagram. In such cases it might
|
||||
be useful to disable dependency relationships for this diagram completely using the following exclusion filter:
|
||||
In many cases, dependency relationships between classes can clutter the diagram
|
||||
too much. In such cases it might be useful to disable dependency relationships
|
||||
completely for this diagram completely using the following exclusion filter:
|
||||
```yaml
|
||||
exclude:
|
||||
relationships:
|
||||
- dependency
|
||||
```
|
||||
|
||||
Dependency relationships are inferred whenever a class uses another class, thus often dependency relationship
|
||||
will be rendered in addition to other relationships such as association or inheritance. In the future there might
|
||||
be an option to remove the redundant dependency relationships from the diagram automatically.
|
||||
|
||||
It is also possible to only disable dependency relationships generated from
|
||||
template arguments to other templates. By default, the following code:
|
||||
|
||||
|
||||
@@ -208,6 +208,33 @@ inja::json diagram::context() const
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void diagram::remove_redundant_dependencies()
|
||||
{
|
||||
using common::id_t;
|
||||
using common::model::relationship;
|
||||
using common::model::relationship_t;
|
||||
|
||||
for (auto &c : element_view<class_>::view()) {
|
||||
std::set<id_t> dependency_relationships_to_remove;
|
||||
|
||||
for (auto &r : c.get().relationships()) {
|
||||
if (r.type() != relationship_t::kDependency)
|
||||
dependency_relationships_to_remove.emplace(r.destination());
|
||||
}
|
||||
|
||||
for (const auto &base : c.get().parents()) {
|
||||
dependency_relationships_to_remove.emplace(base.id());
|
||||
}
|
||||
|
||||
util::erase_if(c.get().relationships(),
|
||||
[&dependency_relationships_to_remove](const auto &r) {
|
||||
return r.type() == relationship_t::kDependency &&
|
||||
dependency_relationships_to_remove.count(r.destination()) >
|
||||
0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace clanguml::class_diagram::model
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
@@ -227,6 +227,11 @@ public:
|
||||
*/
|
||||
bool has_element(diagram_element::id_t id) const override;
|
||||
|
||||
/**
|
||||
* @brief Remove redundant dependency relationships
|
||||
*/
|
||||
void remove_redundant_dependencies();
|
||||
|
||||
/**
|
||||
* @brief Return the elements JSON context for inja templates.
|
||||
*
|
||||
|
||||
@@ -2061,6 +2061,9 @@ void translation_unit_visitor::finalize()
|
||||
{
|
||||
add_incomplete_forward_declarations();
|
||||
resolve_local_to_global_ids();
|
||||
if (config().skip_redundant_dependencies()) {
|
||||
diagram().remove_redundant_dependencies();
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::extract_constrained_template_param_name(
|
||||
|
||||
@@ -45,6 +45,13 @@ public:
|
||||
*/
|
||||
const reference_vector<T> &view() const { return elements_; }
|
||||
|
||||
/**
|
||||
* @brief Get collection of reference to diagram elements
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
reference_vector<T> &view() { return elements_; }
|
||||
|
||||
/**
|
||||
* @brief Get typed diagram element by id
|
||||
* @param id Global id of a diagram element
|
||||
|
||||
@@ -189,6 +189,7 @@ void inheritable_diagram_options::inherit(
|
||||
package_type.override(parent.package_type);
|
||||
generate_template_argument_dependencies.override(
|
||||
parent.generate_template_argument_dependencies);
|
||||
skip_redundant_dependencies.override(parent.skip_redundant_dependencies);
|
||||
generate_links.override(parent.generate_links);
|
||||
generate_system_headers.override(parent.generate_system_headers);
|
||||
git.override(parent.git);
|
||||
|
||||
@@ -440,6 +440,8 @@ struct inheritable_diagram_options {
|
||||
"package_type", package_type_t::kNamespace};
|
||||
option<bool> generate_template_argument_dependencies{
|
||||
"generate_template_argument_dependencies", true};
|
||||
option<bool> skip_redundant_dependencies{
|
||||
"skip_redundant_dependencies", true};
|
||||
option<generate_links_config> generate_links{"generate_links"};
|
||||
option<git_config> git{"git"};
|
||||
option<layout_hints> layout{"layout"};
|
||||
|
||||
@@ -160,6 +160,7 @@ types:
|
||||
generate_packages: !optional bool
|
||||
package_type: !optional package_type_t
|
||||
generate_template_argument_dependencies: !optional bool
|
||||
skip_redundant_dependencies: !optional bool
|
||||
member_order: !optional member_order_t
|
||||
group_methods: !optional bool
|
||||
type_aliases: !optional map_t<string;string>
|
||||
@@ -292,6 +293,7 @@ root:
|
||||
group_methods: !optional bool
|
||||
package_type: !optional package_type_t
|
||||
generate_template_argument_dependencies: !optional bool
|
||||
skip_redundant_dependencies: !optional bool
|
||||
)";
|
||||
|
||||
} // namespace clanguml::config
|
||||
@@ -552,6 +552,7 @@ template <> struct convert<class_diagram> {
|
||||
get_option(node, rhs.generate_packages);
|
||||
get_option(node, rhs.package_type);
|
||||
get_option(node, rhs.generate_template_argument_dependencies);
|
||||
get_option(node, rhs.skip_redundant_dependencies);
|
||||
get_option(node, rhs.relationship_hints);
|
||||
get_option(node, rhs.type_aliases);
|
||||
get_option(node, rhs.relative_to);
|
||||
@@ -757,6 +758,7 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.generate_packages);
|
||||
get_option(node, rhs.package_type);
|
||||
get_option(node, rhs.generate_template_argument_dependencies);
|
||||
get_option(node, rhs.skip_redundant_dependencies);
|
||||
get_option(node, rhs.generate_links);
|
||||
get_option(node, rhs.generate_system_headers);
|
||||
get_option(node, rhs.git);
|
||||
|
||||
@@ -313,6 +313,7 @@ YAML::Emitter &operator<<(
|
||||
out << c.member_order;
|
||||
out << c.package_type;
|
||||
out << c.generate_template_argument_dependencies;
|
||||
out << c.skip_redundant_dependencies;
|
||||
}
|
||||
else if (dynamic_cast<const sequence_diagram *>(&c) != nullptr) {
|
||||
out << c.combine_free_functions_into_file_participants;
|
||||
|
||||
@@ -5,6 +5,7 @@ diagrams:
|
||||
type: class
|
||||
glob:
|
||||
- ../../tests/t00031/t00031.cc
|
||||
skip_redundant_dependencies: false
|
||||
using_namespace:
|
||||
- clanguml::t00031
|
||||
include:
|
||||
|
||||
@@ -25,6 +25,8 @@ struct R {
|
||||
/// @uml{style[#green,dashed,thickness=4]}
|
||||
std::vector<B> bbb;
|
||||
|
||||
void add_b(B b) { bbb.push_back(b); }
|
||||
|
||||
/// @uml{style[#blue,dotted,thickness=8]}
|
||||
C<int> ccc;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ TEST_CASE("t00031", "[test-case][class]")
|
||||
REQUIRE_THAT(puml,
|
||||
IsCompositionWithStyle(
|
||||
_A("R"), _A("B"), "+bbb", "#green,dashed,thickness=4"));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B")));
|
||||
REQUIRE_THAT(puml,
|
||||
IsAggregationWithStyle(
|
||||
_A("R"), _A("C<int>"), "+ccc", "#blue,dotted,thickness=8"));
|
||||
|
||||
@@ -53,6 +53,14 @@ TEST_CASE("t00032", "[test-case][class]")
|
||||
puml, IsBaseClass(_A("B"), _A("Overload<TBase,int,A,B,C>")));
|
||||
REQUIRE_THAT(
|
||||
puml, IsBaseClass(_A("C"), _A("Overload<TBase,int,A,B,C>")));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsDependency(_A("Overload<TBase,int,A,B,C>"), _A("TBase")));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsDependency(_A("Overload<TBase,int,A,B,C>"), _A("A")));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsDependency(_A("Overload<TBase,int,A,B,C>"), _A("B")));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsDependency(_A("Overload<TBase,int,A,B,C>"), _A("C")));
|
||||
|
||||
save_puml(
|
||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
|
||||
Reference in New Issue
Block a user