Merge pull request #8 from bkryza/fix-nested-templates-skipping-excluded-containers
Fix nested templates skipping excluded containers
6
.github/workflows/build.yml
vendored
@@ -10,12 +10,8 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install add-apt-repository
|
|
||||||
run: sudo apt-get install software-properties-common
|
|
||||||
- name: Add llvm repository
|
|
||||||
run: sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' && sudo apt update
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: sudo apt-get install ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev
|
run: sudo apt install ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev
|
||||||
- name: Build and unit test
|
- name: Build and unit test
|
||||||
run: |
|
run: |
|
||||||
make debug
|
make debug
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
@@ -17,6 +17,36 @@ diagrams:
|
|||||||
|
|
||||||
```
|
```
|
||||||
## Source code
|
## Source code
|
||||||
|
File t00018_impl.cc
|
||||||
|
```cpp
|
||||||
|
#include "t00018_impl.h"
|
||||||
|
#include "t00018.h"
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00018 {
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
widget::widget(int n)
|
||||||
|
: n(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void widget::draw(const clanguml::t00018::widget &w) const
|
||||||
|
{
|
||||||
|
if (w.shown())
|
||||||
|
std::cout << "drawing a const widget " << n << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void widget::draw(const clanguml::t00018::widget &w)
|
||||||
|
{
|
||||||
|
if (w.shown())
|
||||||
|
std::cout << "drawing a non-const widget " << n << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
File t00018_impl.h
|
File t00018_impl.h
|
||||||
```cpp
|
```cpp
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -101,36 +131,6 @@ widget &widget::operator=(widget &&) = default;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
|
||||||
File t00018_impl.cc
|
|
||||||
```cpp
|
|
||||||
#include "t00018_impl.h"
|
|
||||||
#include "t00018.h"
|
|
||||||
|
|
||||||
namespace clanguml {
|
|
||||||
namespace t00018 {
|
|
||||||
namespace impl {
|
|
||||||
|
|
||||||
widget::widget(int n)
|
|
||||||
: n(n)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void widget::draw(const clanguml::t00018::widget &w) const
|
|
||||||
{
|
|
||||||
if (w.shown())
|
|
||||||
std::cout << "drawing a const widget " << n << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
void widget::draw(const clanguml::t00018::widget &w)
|
|
||||||
{
|
|
||||||
if (w.shown())
|
|
||||||
std::cout << "drawing a non-const widget " << n << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
```
|
||||||
## Generated UML diagrams
|
## Generated UML diagrams
|
||||||

|

|
||||||
|
|||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
@@ -22,25 +22,21 @@ diagrams:
|
|||||||
|
|
||||||
```
|
```
|
||||||
## Source code
|
## Source code
|
||||||
File t00019_layer2.h
|
File t00019.cc
|
||||||
```cpp
|
```cpp
|
||||||
#pragma once
|
#include "t00019_base.h"
|
||||||
|
#include "t00019_layer1.h"
|
||||||
|
#include "t00019_layer2.h"
|
||||||
|
#include "t00019_layer3.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace t00019 {
|
namespace t00019 {
|
||||||
|
|
||||||
template <typename LowerLayer> class Layer2 : public LowerLayer {
|
class A {
|
||||||
|
public:
|
||||||
using LowerLayer::LowerLayer;
|
std::unique_ptr<Layer1<Layer2<Layer3<Base>>>> layers;
|
||||||
|
|
||||||
using LowerLayer::m1;
|
|
||||||
|
|
||||||
using LowerLayer::m2;
|
|
||||||
|
|
||||||
int all_calls_count() const
|
|
||||||
{
|
|
||||||
return LowerLayer::m1_calls() + LowerLayer::m2_calls();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,26 +94,6 @@ template <typename LowerLayer> class Layer1 : public LowerLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
|
||||||
File t00019.cc
|
|
||||||
```cpp
|
|
||||||
#include "t00019_base.h"
|
|
||||||
#include "t00019_layer1.h"
|
|
||||||
#include "t00019_layer2.h"
|
|
||||||
#include "t00019_layer3.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace clanguml {
|
|
||||||
namespace t00019 {
|
|
||||||
|
|
||||||
class A {
|
|
||||||
public:
|
|
||||||
std::unique_ptr<Layer1<Layer2<Layer3<Base>>>> layers;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
```
|
||||||
File t00019_layer3.h
|
File t00019_layer3.h
|
||||||
```cpp
|
```cpp
|
||||||
@@ -155,6 +131,30 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
File t00019_layer2.h
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00019 {
|
||||||
|
|
||||||
|
template <typename LowerLayer> class Layer2 : public LowerLayer {
|
||||||
|
|
||||||
|
using LowerLayer::LowerLayer;
|
||||||
|
|
||||||
|
using LowerLayer::m1;
|
||||||
|
|
||||||
|
using LowerLayer::m2;
|
||||||
|
|
||||||
|
int all_calls_count() const
|
||||||
|
{
|
||||||
|
return LowerLayer::m1_calls() + LowerLayer::m2_calls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
## Generated UML diagrams
|
## Generated UML diagrams
|
||||||

|

|
||||||
|
|||||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
@@ -41,7 +41,7 @@ struct D {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct R {
|
struct R {
|
||||||
A<B<C<D>>> abc;
|
A<B<std::unique_ptr<C<D>>>> abc;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace t00033
|
} // namespace t00033
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -62,8 +62,9 @@ std::string full_name(const cppast::cpp_type &t,
|
|||||||
const cppast::cpp_entity_index &idx, bool inside_class)
|
const cppast::cpp_entity_index &idx, bool inside_class)
|
||||||
{
|
{
|
||||||
std::string t_ns;
|
std::string t_ns;
|
||||||
if (!inside_class)
|
if (!inside_class) {
|
||||||
t_ns = ns(t, idx);
|
t_ns = ns(cppast::remove_cv(unreferenced(t)), idx);
|
||||||
|
}
|
||||||
|
|
||||||
auto t_name = cppast::to_string(t);
|
auto t_name = cppast::to_string(t);
|
||||||
|
|
||||||
@@ -121,12 +122,13 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx)
|
|||||||
if (static_cast<const cppast::cpp_template_instantiation_type &>(t)
|
if (static_cast<const cppast::cpp_template_instantiation_type &>(t)
|
||||||
.primary_template()
|
.primary_template()
|
||||||
.get(idx)
|
.get(idx)
|
||||||
.size() > 0)
|
.size() > 0) {
|
||||||
return ns(
|
return ns(
|
||||||
static_cast<const cppast::cpp_template_instantiation_type &>(t)
|
static_cast<const cppast::cpp_template_instantiation_type &>(t)
|
||||||
.primary_template()
|
.primary_template()
|
||||||
.get(idx)[0]
|
.get(idx)[0]
|
||||||
.get());
|
.get());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,10 +206,13 @@ public:
|
|||||||
std::set<std::string> rendered_relations;
|
std::set<std::string> rendered_relations;
|
||||||
|
|
||||||
std::stringstream all_relations_str;
|
std::stringstream all_relations_str;
|
||||||
|
std::set<std::string> unique_relations;
|
||||||
for (const auto &r : c.relationships) {
|
for (const auto &r : c.relationships) {
|
||||||
if (!m_config.should_include_relationship(name(r.type)))
|
if (!m_config.should_include_relationship(name(r.type)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
LOG_DBG("== Processing relationship {}", to_string(r.type));
|
||||||
|
|
||||||
std::stringstream relstr;
|
std::stringstream relstr;
|
||||||
std::string destination;
|
std::string destination;
|
||||||
try {
|
try {
|
||||||
@@ -229,6 +232,8 @@ public:
|
|||||||
destination = r.destination;
|
destination = r.destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DBG("=== Destination is: {}", destination);
|
||||||
|
|
||||||
std::string puml_relation;
|
std::string puml_relation;
|
||||||
if (!r.multiplicity_source.empty())
|
if (!r.multiplicity_source.empty())
|
||||||
puml_relation += "\"" + r.multiplicity_source + "\" ";
|
puml_relation += "\"" + r.multiplicity_source + "\" ";
|
||||||
@@ -248,12 +253,18 @@ public:
|
|||||||
rendered_relations.emplace(r.label);
|
rendered_relations.emplace(r.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unique_relations.count(relstr.str()) == 0) {
|
||||||
|
unique_relations.emplace(relstr.str());
|
||||||
|
|
||||||
relstr << '\n';
|
relstr << '\n';
|
||||||
|
|
||||||
|
LOG_DBG("=== Adding relation {}", relstr.str());
|
||||||
|
|
||||||
all_relations_str << relstr.str();
|
all_relations_str << relstr.str();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
LOG_ERROR("=== Skipping {} relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
to_string(r.type), c.full_name(uns), destination, e.what());
|
to_string(r.type), c.full_name(uns), destination, e.what());
|
||||||
}
|
}
|
||||||
@@ -291,7 +302,8 @@ public:
|
|||||||
ostr << relstr.str();
|
ostr << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
LOG_ERROR("Skipping inheritance relation from {} to {} due "
|
LOG_ERROR(
|
||||||
|
"=== Skipping inheritance relation from {} to {} due "
|
||||||
"to: {}",
|
"to: {}",
|
||||||
b.name, c.name, e.what());
|
b.name, c.name, e.what());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ struct diagram {
|
|||||||
if (!has_class(c.usr))
|
if (!has_class(c.usr))
|
||||||
classes.emplace_back(std::move(c));
|
classes.emplace_back(std::move(c));
|
||||||
else
|
else
|
||||||
LOG_DBG("Class {} already in the model", c.name);
|
LOG_DBG("Class {} ({}) already in the model", c.name, c.usr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_enum(enum_ &&e)
|
void add_enum(enum_ &&e)
|
||||||
@@ -350,6 +350,8 @@ struct diagram {
|
|||||||
std::string to_alias(const std::vector<std::string> &using_namespaces,
|
std::string to_alias(const std::vector<std::string> &using_namespaces,
|
||||||
const std::string &full_name) const
|
const std::string &full_name) const
|
||||||
{
|
{
|
||||||
|
LOG_DBG("Looking for alias for {}", full_name);
|
||||||
|
|
||||||
for (const auto &c : classes) {
|
for (const auto &c : classes) {
|
||||||
if (c.full_name(using_namespaces) == full_name) {
|
if (c.full_name(using_namespaces) == full_name) {
|
||||||
return c.alias();
|
return c.alias();
|
||||||
|
|||||||
@@ -513,36 +513,25 @@ bool tu_visitor::process_field_with_template_instantiation(
|
|||||||
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
||||||
class_ &c, class_member &m, cppast::cpp_access_specifier_kind as)
|
class_ &c, class_member &m, cppast::cpp_access_specifier_kind as)
|
||||||
{
|
{
|
||||||
LOG_DBG("Processing field with template instatiation type {}",
|
LOG_DBG("Processing field with template instantiation type {}",
|
||||||
cppast::to_string(tr));
|
cppast::to_string(tr));
|
||||||
|
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
const auto &template_instantiation_type =
|
const auto &template_instantiation_type =
|
||||||
static_cast<const cppast::cpp_template_instantiation_type &>(tr);
|
static_cast<const cppast::cpp_template_instantiation_type &>(tr);
|
||||||
if (template_instantiation_type.primary_template()
|
|
||||||
.get(ctx.entity_index)
|
|
||||||
.size()) {
|
|
||||||
// Here we need the name of the primary template with full namespace
|
|
||||||
// prefix to apply config inclusion filters
|
|
||||||
auto primary_template_name = cx::util::full_name(ctx.namespace_,
|
|
||||||
template_instantiation_type.primary_template()
|
|
||||||
.get(ctx.entity_index)[0]
|
|
||||||
.get());
|
|
||||||
|
|
||||||
LOG_DBG("Maybe building instantiation for: {}{}", primary_template_name,
|
|
||||||
cppast::to_string(tr));
|
|
||||||
|
|
||||||
if (ctx.config.should_include(primary_template_name)) {
|
|
||||||
const auto &unaliased =
|
const auto &unaliased =
|
||||||
static_cast<const cppast::cpp_template_instantiation_type &>(
|
static_cast<const cppast::cpp_template_instantiation_type &>(
|
||||||
resolve_alias(template_instantiation_type));
|
resolve_alias(template_instantiation_type));
|
||||||
|
|
||||||
class_ tinst = build_template_instantiation(unaliased);
|
class_ tinst = build_template_instantiation(unaliased);
|
||||||
|
tinst.usr = tinst.full_name(ctx.config.using_namespace);
|
||||||
|
|
||||||
// Infer the relationship of this field to the template
|
// Infer the relationship of this field to the template
|
||||||
// instantiation
|
// instantiation
|
||||||
class_relationship rr;
|
class_relationship rr;
|
||||||
rr.destination = tinst.usr;
|
rr.destination = tinst.full_name(ctx.config.using_namespace);
|
||||||
if (mv.type().kind() == cppast::cpp_type_kind::pointer_t ||
|
if (mv.type().kind() == cppast::cpp_type_kind::pointer_t ||
|
||||||
mv.type().kind() == cppast::cpp_type_kind::reference_t)
|
mv.type().kind() == cppast::cpp_type_kind::reference_t)
|
||||||
rr.type = relationship_t::kAssociation;
|
rr.type = relationship_t::kAssociation;
|
||||||
@@ -563,6 +552,7 @@ bool tu_visitor::process_field_with_template_instantiation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.config.should_include(tinst.name)) {
|
||||||
LOG_DBG("Adding field instantiation relationship {} {} {} : {}",
|
LOG_DBG("Adding field instantiation relationship {} {} {} : {}",
|
||||||
rr.destination, model::class_diagram::to_string(rr.type), c.usr,
|
rr.destination, model::class_diagram::to_string(rr.type), c.usr,
|
||||||
rr.label);
|
rr.label);
|
||||||
@@ -571,12 +561,11 @@ bool tu_visitor::process_field_with_template_instantiation(
|
|||||||
|
|
||||||
res = true;
|
res = true;
|
||||||
|
|
||||||
LOG_DBG("Created template instantiation: {}, {}", tinst.name,
|
LOG_DBG(
|
||||||
tinst.usr);
|
"Created template instantiation: {}, {}", tinst.name, tinst.usr);
|
||||||
|
|
||||||
ctx.d.add_class(std::move(tinst));
|
ctx.d.add_class(std::move(tinst));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -747,8 +736,13 @@ void tu_visitor::process_template_method(
|
|||||||
if (m.skip())
|
if (m.skip())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
std::set<std::string> template_parameter_names;
|
||||||
|
for (const auto &template_parameter : mf.parameters()) {
|
||||||
|
template_parameter_names.emplace(template_parameter.name());
|
||||||
|
}
|
||||||
|
|
||||||
for (auto ¶m : mf.function().parameters())
|
for (auto ¶m : mf.function().parameters())
|
||||||
process_function_parameter(param, m, c);
|
process_function_parameter(param, m, c, template_parameter_names);
|
||||||
|
|
||||||
LOG_DBG("Adding template method: {}", m.name);
|
LOG_DBG("Adding template method: {}", m.name);
|
||||||
|
|
||||||
@@ -824,7 +818,8 @@ void tu_visitor::process_destructor(const cppast::cpp_destructor &mf, class_ &c,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void tu_visitor::process_function_parameter(
|
void tu_visitor::process_function_parameter(
|
||||||
const cppast::cpp_function_parameter ¶m, class_method &m, class_ &c)
|
const cppast::cpp_function_parameter ¶m, class_method &m, class_ &c,
|
||||||
|
const std::set<std::string> &template_parameter_names)
|
||||||
{
|
{
|
||||||
method_parameter mp;
|
method_parameter mp;
|
||||||
mp.name = param.name();
|
mp.name = param.name();
|
||||||
@@ -838,7 +833,7 @@ void tu_visitor::process_function_parameter(
|
|||||||
const auto ¶m_type =
|
const auto ¶m_type =
|
||||||
cppast::remove_cv(cx::util::unreferenced(param.type()));
|
cppast::remove_cv(cx::util::unreferenced(param.type()));
|
||||||
if (param_type.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
if (param_type.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
// Template instantiation parameters are not fully prefixed
|
// TODO: Template instantiation parameters are not fully prefixed
|
||||||
// so we have to deduce the correct namespace prefix of the
|
// so we have to deduce the correct namespace prefix of the
|
||||||
// template which is being instantiated
|
// template which is being instantiated
|
||||||
mp.type = cppast::to_string(param.type());
|
mp.type = cppast::to_string(param.type());
|
||||||
@@ -893,20 +888,58 @@ void tu_visitor::process_function_parameter(
|
|||||||
if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
auto &template_instantiation_type =
|
auto &template_instantiation_type =
|
||||||
static_cast<const cppast::cpp_template_instantiation_type &>(t);
|
static_cast<const cppast::cpp_template_instantiation_type &>(t);
|
||||||
|
|
||||||
if (template_instantiation_type.primary_template()
|
if (template_instantiation_type.primary_template()
|
||||||
.get(ctx.entity_index)
|
.get(ctx.entity_index)
|
||||||
.size()) {
|
.size()) {
|
||||||
|
|
||||||
// Here we need the name of the primary template with full
|
// Here we need the name of the primary template with full
|
||||||
// namespace prefix to apply config inclusion filters
|
// namespace prefix to apply config inclusion filters
|
||||||
auto primary_template_name = cx::util::full_name(ctx.namespace_,
|
auto primary_template_name = cx::util::full_name(ctx.namespace_,
|
||||||
template_instantiation_type.primary_template()
|
template_instantiation_type.primary_template()
|
||||||
.get(ctx.entity_index)[0]
|
.get(ctx.entity_index)[0]
|
||||||
.get());
|
.get());
|
||||||
|
// Now check if the template arguments of this function param
|
||||||
|
// are a subset of the method template params - if yes this is
|
||||||
|
// not an instantiation but just a reference to an existing
|
||||||
|
// template
|
||||||
|
bool template_is_not_instantiation{false};
|
||||||
|
for (const auto &template_argument :
|
||||||
|
template_instantiation_type.arguments().value()) {
|
||||||
|
const auto template_argument_name =
|
||||||
|
cppast::to_string(template_argument.type().value());
|
||||||
|
if (template_parameter_names.count(template_argument_name) >
|
||||||
|
0) {
|
||||||
|
template_is_not_instantiation = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DBG("Maybe building instantiation for: {}",
|
LOG_DBG("Maybe building instantiation for: {}",
|
||||||
primary_template_name);
|
primary_template_name);
|
||||||
|
|
||||||
if (ctx.config.should_include(primary_template_name)) {
|
if (ctx.config.should_include(primary_template_name)) {
|
||||||
|
|
||||||
|
if (template_is_not_instantiation) {
|
||||||
|
LOG_DBG("Template is not an instantiation - "
|
||||||
|
"only adding reference to template {}",
|
||||||
|
cx::util::full_name(
|
||||||
|
cppast::remove_cv(t), ctx.entity_index, false));
|
||||||
|
class_relationship rr;
|
||||||
|
rr.destination = cx::util::full_name(
|
||||||
|
cppast::remove_cv(t), ctx.entity_index, false);
|
||||||
|
rr.type = relationship_t::kDependency;
|
||||||
|
rr.label = "";
|
||||||
|
LOG_DBG("Adding field template dependency relationship "
|
||||||
|
"{} {} {} "
|
||||||
|
": {}",
|
||||||
|
rr.destination,
|
||||||
|
model::class_diagram::to_string(rr.type), c.usr,
|
||||||
|
rr.label);
|
||||||
|
c.add_relationship(std::move(rr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// First check if tinst already exists
|
||||||
class_ tinst = build_template_instantiation(
|
class_ tinst = build_template_instantiation(
|
||||||
template_instantiation_type);
|
template_instantiation_type);
|
||||||
|
|
||||||
@@ -914,8 +947,8 @@ void tu_visitor::process_function_parameter(
|
|||||||
rr.destination = tinst.usr;
|
rr.destination = tinst.usr;
|
||||||
rr.type = relationship_t::kDependency;
|
rr.type = relationship_t::kDependency;
|
||||||
rr.label = "";
|
rr.label = "";
|
||||||
LOG_DBG(
|
LOG_DBG("Adding field dependency relationship {} {} {} "
|
||||||
"Adding field dependency relationship {} {} {} : {}",
|
": {}",
|
||||||
rr.destination,
|
rr.destination,
|
||||||
model::class_diagram::to_string(rr.type), c.usr,
|
model::class_diagram::to_string(rr.type), c.usr,
|
||||||
rr.label);
|
rr.label);
|
||||||
@@ -926,6 +959,7 @@ void tu_visitor::process_function_parameter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m.parameters.emplace_back(std::move(mp));
|
m.parameters.emplace_back(std::move(mp));
|
||||||
}
|
}
|
||||||
@@ -1045,8 +1079,11 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
{
|
{
|
||||||
bool found{false};
|
bool found{false};
|
||||||
|
|
||||||
LOG_DBG("Finding relationships for type {}, {}", cppast::to_string(t_),
|
const auto fn =
|
||||||
t_.kind());
|
cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index, false);
|
||||||
|
|
||||||
|
LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_),
|
||||||
|
t_.kind(), fn);
|
||||||
|
|
||||||
relationship_t relationship_type = relationship_hint;
|
relationship_t relationship_type = relationship_hint;
|
||||||
const auto &t = cppast::remove_cv(cx::util::unreferenced(t_));
|
const auto &t = cppast::remove_cv(cx::util::unreferenced(t_));
|
||||||
@@ -1088,8 +1125,6 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
cppast::to_string(t), relationship_t::kAggregation);
|
cppast::to_string(t), relationship_t::kAggregation);
|
||||||
|
|
||||||
// Check if t_ has an alias in the alias index
|
// Check if t_ has an alias in the alias index
|
||||||
const auto fn =
|
|
||||||
cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index, false);
|
|
||||||
if (ctx.has_type_alias(fn)) {
|
if (ctx.has_type_alias(fn)) {
|
||||||
LOG_DBG("Find relationship in alias of {} | {}", fn,
|
LOG_DBG("Find relationship in alias of {} | {}", fn,
|
||||||
cppast::to_string(ctx.get_type_alias(fn).get()));
|
cppast::to_string(ctx.get_type_alias(fn).get()));
|
||||||
@@ -1132,6 +1167,29 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
found = find_relationships(args[0u].type().value(), relationships,
|
found = find_relationships(args[0u].type().value(), relationships,
|
||||||
relationship_t::kAggregation);
|
relationship_t::kAggregation);
|
||||||
}
|
}
|
||||||
|
else if (ctx.config.should_include(fn)) {
|
||||||
|
LOG_DBG("User defined template instantiation: {} | {}",
|
||||||
|
cppast::to_string(t_), cppast::to_string(t_.canonical()));
|
||||||
|
|
||||||
|
if (relationship_type != relationship_t::kNone)
|
||||||
|
relationships.emplace_back(
|
||||||
|
cppast::to_string(t), relationship_type);
|
||||||
|
else
|
||||||
|
relationships.emplace_back(
|
||||||
|
cppast::to_string(t), relationship_t::kAggregation);
|
||||||
|
|
||||||
|
// Check if t_ has an alias in the alias index
|
||||||
|
if (ctx.has_type_alias(fn)) {
|
||||||
|
LOG_DBG("Find relationship in alias of {} | {}", fn,
|
||||||
|
cppast::to_string(ctx.get_type_alias(fn).get()));
|
||||||
|
found = find_relationships(ctx.get_type_alias(fn).get(),
|
||||||
|
relationships, relationship_type);
|
||||||
|
if (found)
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
for (const auto &arg : args) {
|
for (const auto &arg : args) {
|
||||||
if (arg.type()) {
|
if (arg.type()) {
|
||||||
@@ -1146,23 +1204,33 @@ bool tu_visitor::find_relationships(const cppast::cpp_type &t_,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class_ tu_visitor::build_template_instantiation(
|
class_ tu_visitor::build_template_instantiation(
|
||||||
const cppast::cpp_template_instantiation_type &t)
|
const cppast::cpp_template_instantiation_type &t,
|
||||||
|
std::optional<clanguml::model::class_diagram::class_ *> parent)
|
||||||
{
|
{
|
||||||
class_ tinst;
|
class_ tinst;
|
||||||
std::string full_template_name;
|
std::string full_template_name;
|
||||||
|
|
||||||
std::deque<std::tuple<std::string, int, bool>> template_base_params{};
|
std::deque<std::tuple<std::string, int, bool>> template_base_params{};
|
||||||
|
|
||||||
|
// Determine the full template name
|
||||||
if (t.primary_template().get(ctx.entity_index).size()) {
|
if (t.primary_template().get(ctx.entity_index).size()) {
|
||||||
const auto &primary_template_ref =
|
const auto &primary_template_ref =
|
||||||
static_cast<const cppast::cpp_class_template &>(
|
static_cast<const cppast::cpp_class_template &>(
|
||||||
t.primary_template().get(ctx.entity_index)[0].get())
|
t.primary_template().get(ctx.entity_index)[0].get())
|
||||||
.class_();
|
.class_();
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
LOG_DBG("Template parent is {}",
|
||||||
|
(*parent)->full_name(ctx.config.using_namespace));
|
||||||
|
else
|
||||||
|
LOG_DBG("Template parent is empty");
|
||||||
|
|
||||||
full_template_name =
|
full_template_name =
|
||||||
cx::util::full_name(ctx.namespace_, primary_template_ref);
|
cx::util::full_name(ctx.namespace_, primary_template_ref);
|
||||||
|
|
||||||
LOG_DBG("Found template instantiation: {} ({}) ..|> {}, {}",
|
LOG_DBG("Found template instantiation: "
|
||||||
|
"type={}, canonical={}, primary_template={}, full_"
|
||||||
|
"template={}",
|
||||||
cppast::to_string(t), cppast::to_string(t.canonical()),
|
cppast::to_string(t), cppast::to_string(t.canonical()),
|
||||||
t.primary_template().name(), full_template_name);
|
t.primary_template().name(), full_template_name);
|
||||||
|
|
||||||
@@ -1233,10 +1301,12 @@ class_ tu_visitor::build_template_instantiation(
|
|||||||
full_template_name = cppast::to_string(t);
|
full_template_name = cppast::to_string(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Building template instantiation for {}", full_template_name);
|
||||||
|
|
||||||
// Extract namespace from base template name
|
// Extract namespace from base template name
|
||||||
auto ns_toks = clanguml::util::split(
|
std::vector<std::string> ns_toks;
|
||||||
tinst.base_template_usr.substr(0, tinst.base_template_usr.find('<')),
|
ns_toks = clanguml::util::split(
|
||||||
"::");
|
full_template_name.substr(0, full_template_name.find('<')), "::");
|
||||||
|
|
||||||
std::string ns;
|
std::string ns;
|
||||||
if (ns_toks.size() > 1) {
|
if (ns_toks.size() > 1) {
|
||||||
@@ -1244,10 +1314,18 @@ class_ tu_visitor::build_template_instantiation(
|
|||||||
"{}::", fmt::join(ns_toks.begin(), ns_toks.end() - 1, "::"));
|
"{}::", fmt::join(ns_toks.begin(), ns_toks.end() - 1, "::"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Template namespace is {}", ns);
|
||||||
|
|
||||||
tinst.name = ns + util::split(cppast::to_string(t), "<")[0];
|
tinst.name = ns + util::split(cppast::to_string(t), "<")[0];
|
||||||
|
|
||||||
tinst.is_template_instantiation = true;
|
tinst.is_template_instantiation = true;
|
||||||
|
|
||||||
|
tinst.usr = tinst.full_name(ctx.config.using_namespace);
|
||||||
|
if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") ==
|
||||||
|
std::string::npos) {
|
||||||
|
tinst.usr = ns + tinst.usr;
|
||||||
|
}
|
||||||
|
|
||||||
// Process template argumetns
|
// Process template argumetns
|
||||||
int arg_index{0};
|
int arg_index{0};
|
||||||
bool variadic_params{false};
|
bool variadic_params{false};
|
||||||
@@ -1258,37 +1336,63 @@ class_ tu_visitor::build_template_instantiation(
|
|||||||
ct.type = cppast::to_string(targ.type().value());
|
ct.type = cppast::to_string(targ.type().value());
|
||||||
|
|
||||||
LOG_DBG("Template argument is a type {}", ct.type);
|
LOG_DBG("Template argument is a type {}", ct.type);
|
||||||
|
|
||||||
auto fn = cx::util::full_name(
|
auto fn = cx::util::full_name(
|
||||||
cppast::remove_cv(cx::util::unreferenced(targ.type().value())),
|
cppast::remove_cv(cx::util::unreferenced(targ.type().value())),
|
||||||
ctx.entity_index, false);
|
ctx.entity_index, false);
|
||||||
|
|
||||||
if (ctx.config.should_include(fn)) {
|
|
||||||
|
|
||||||
if (targ.type().value().kind() ==
|
if (targ.type().value().kind() ==
|
||||||
cppast::cpp_type_kind::template_instantiation_t) {
|
cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
class_ nested_tinst =
|
|
||||||
build_template_instantiation(static_cast<
|
|
||||||
const cppast::cpp_template_instantiation_type &>(
|
|
||||||
targ.type().value()));
|
|
||||||
|
|
||||||
fn = util::split(fn, "<")[0];
|
const auto &nested_template_parameter = static_cast<
|
||||||
|
const cppast::cpp_template_instantiation_type &>(
|
||||||
|
targ.type().value());
|
||||||
|
|
||||||
class_relationship tinst_dependency;
|
class_relationship tinst_dependency;
|
||||||
tinst_dependency.type = relationship_t::kDependency;
|
tinst_dependency.type = relationship_t::kDependency;
|
||||||
tinst_dependency.label = "";
|
tinst_dependency.label = "";
|
||||||
|
|
||||||
|
std::string nnn{"empty"};
|
||||||
|
if (parent)
|
||||||
|
nnn = (*parent)->name;
|
||||||
|
|
||||||
|
class_ nested_tinst =
|
||||||
|
build_template_instantiation(nested_template_parameter,
|
||||||
|
ctx.config.should_include(tinst.usr)
|
||||||
|
? std::make_optional(&tinst)
|
||||||
|
: parent);
|
||||||
|
|
||||||
tinst_dependency.destination =
|
tinst_dependency.destination =
|
||||||
nested_tinst.full_name(ctx.config.using_namespace);
|
nested_tinst.full_name(ctx.config.using_namespace);
|
||||||
|
|
||||||
|
auto nested_tinst_usr = nested_tinst.usr;
|
||||||
|
|
||||||
|
if (ctx.config.should_include(fn)) {
|
||||||
|
ctx.d.add_class(std::move(nested_tinst));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.config.should_include(tinst.usr)) {
|
||||||
LOG_DBG("Creating nested template dependency to template "
|
LOG_DBG("Creating nested template dependency to template "
|
||||||
"instantiation {} -> {}",
|
"instantiation {}, {} -> {}",
|
||||||
tinst.full_name(ctx.config.using_namespace),
|
fn, tinst.full_name(ctx.config.using_namespace),
|
||||||
tinst_dependency.destination);
|
tinst_dependency.destination);
|
||||||
|
|
||||||
tinst.add_relationship(std::move(tinst_dependency));
|
tinst.add_relationship(std::move(tinst_dependency));
|
||||||
|
}
|
||||||
|
else if (parent) {
|
||||||
|
LOG_DBG("Creating nested template dependency to parent "
|
||||||
|
"template "
|
||||||
|
"instantiation {}, {} -> {}",
|
||||||
|
fn, (*parent)->full_name(ctx.config.using_namespace),
|
||||||
|
tinst_dependency.destination);
|
||||||
|
|
||||||
ctx.d.add_class(std::move(nested_tinst));
|
(*parent)->add_relationship(std::move(tinst_dependency));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("No nested template dependency to template "
|
||||||
|
"instantiation: {}, {} -> {}",
|
||||||
|
fn, tinst.full_name(ctx.config.using_namespace),
|
||||||
|
tinst_dependency.destination);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (targ.type().value().kind() ==
|
else if (targ.type().value().kind() ==
|
||||||
cppast::cpp_type_kind::user_defined_t) {
|
cppast::cpp_type_kind::user_defined_t) {
|
||||||
@@ -1301,14 +1405,17 @@ class_ tu_visitor::build_template_instantiation(
|
|||||||
cx::util::unreferenced(targ.type().value())),
|
cx::util::unreferenced(targ.type().value())),
|
||||||
ctx.entity_index, false);
|
ctx.entity_index, false);
|
||||||
|
|
||||||
LOG_DBG(
|
LOG_DBG("Creating nested template dependency to user defined "
|
||||||
"Creating nested template dependency to user defined "
|
|
||||||
"type {} -> {}",
|
"type {} -> {}",
|
||||||
tinst.full_name(ctx.config.using_namespace),
|
tinst.full_name(ctx.config.using_namespace),
|
||||||
tinst_dependency.destination);
|
tinst_dependency.destination);
|
||||||
|
|
||||||
|
if (ctx.config.should_include(fn)) {
|
||||||
tinst.add_relationship(std::move(tinst_dependency));
|
tinst.add_relationship(std::move(tinst_dependency));
|
||||||
}
|
}
|
||||||
|
else if (parent) {
|
||||||
|
(*parent)->add_relationship(std::move(tinst_dependency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (targ.expression()) {
|
else if (targ.expression()) {
|
||||||
@@ -1356,12 +1463,16 @@ class_ tu_visitor::build_template_instantiation(
|
|||||||
tinst.templates.emplace_back(std::move(ct));
|
tinst.templates.emplace_back(std::move(ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now update usr with the template arguments of the
|
||||||
|
// instantiations... (there must be a better way)
|
||||||
tinst.usr = tinst.full_name(ctx.config.using_namespace);
|
tinst.usr = tinst.full_name(ctx.config.using_namespace);
|
||||||
if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") ==
|
if (tinst.usr.substr(0, tinst.usr.find('<')).find("::") ==
|
||||||
std::string::npos) {
|
std::string::npos) {
|
||||||
tinst.usr = ns + tinst.usr;
|
tinst.usr = ns + tinst.usr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Setting tinst usr to {}", tinst.usr);
|
||||||
|
|
||||||
// Add instantiation relationship to primary template of this
|
// Add instantiation relationship to primary template of this
|
||||||
// instantiation
|
// instantiation
|
||||||
class_relationship r;
|
class_relationship r;
|
||||||
|
|||||||
@@ -195,7 +195,8 @@ public:
|
|||||||
|
|
||||||
void process_function_parameter(const cppast::cpp_function_parameter ¶m,
|
void process_function_parameter(const cppast::cpp_function_parameter ¶m,
|
||||||
clanguml::model::class_diagram::class_method &m,
|
clanguml::model::class_diagram::class_method &m,
|
||||||
clanguml::model::class_diagram::class_ &c);
|
clanguml::model::class_diagram::class_ &c,
|
||||||
|
const std::set<std::string> &template_parameter_names = {});
|
||||||
|
|
||||||
bool find_relationships(const cppast::cpp_type &t,
|
bool find_relationships(const cppast::cpp_type &t,
|
||||||
std::vector<std::pair<std::string,
|
std::vector<std::pair<std::string,
|
||||||
@@ -220,7 +221,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
clanguml::model::class_diagram::class_ build_template_instantiation(
|
clanguml::model::class_diagram::class_ build_template_instantiation(
|
||||||
const cppast::cpp_template_instantiation_type &t);
|
const cppast::cpp_template_instantiation_type &t,
|
||||||
|
std::optional<clanguml::model::class_diagram::class_ *> parent = {});
|
||||||
|
|
||||||
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
|
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ TEST_CASE("t00013", "[test-case][class]")
|
|||||||
REQUIRE_THAT(
|
REQUIRE_THAT(
|
||||||
puml, IsAggregation(_A("R"), _A("E<std::string>"), "-estring"));
|
puml, IsAggregation(_A("R"), _A("E<std::string>"), "-estring"));
|
||||||
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F<T>")));
|
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F<T>")));
|
||||||
REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F<T>"), _A("ABCD::F<int>")));
|
REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F<T>"), _A("F<int>")));
|
||||||
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F<int>")));
|
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("F<int>")));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct D {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct R {
|
struct R {
|
||||||
A<B<C<D>>> abc;
|
A<B<std::unique_ptr<C<D>>>> abc;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace t00033
|
} // namespace t00033
|
||||||
|
|||||||
@@ -48,13 +48,18 @@ TEST_CASE("t00033", "[test-case][class]")
|
|||||||
REQUIRE_THAT(puml, IsClass(_A("D")));
|
REQUIRE_THAT(puml, IsClass(_A("D")));
|
||||||
REQUIRE_THAT(puml, IsClass(_A("R")));
|
REQUIRE_THAT(puml, IsClass(_A("R")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, IsDependency(_A("A<B<C<D>>>"), _A("B<C<D>>")));
|
REQUIRE_THAT(puml,
|
||||||
REQUIRE_THAT(puml, IsDependency(_A("B<C<D>>"), _A("C<D>")));
|
IsDependency(
|
||||||
|
_A("A<B<std::unique_ptr<C<D>>>>"), _A("B<std::unique_ptr<C<D>>>")));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, IsDependency(_A("B<std::unique_ptr<C<D>>>"), _A("C<D>")));
|
||||||
REQUIRE_THAT(puml, IsDependency(_A("C<D>"), _A("D")));
|
REQUIRE_THAT(puml, IsDependency(_A("C<D>"), _A("D")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, IsInstantiation(_A("C<T>"), _A("C<D>")));
|
REQUIRE_THAT(puml, IsInstantiation(_A("C<T>"), _A("C<D>")));
|
||||||
REQUIRE_THAT(puml, IsInstantiation(_A("B<T>"), _A("B<C<D>>")));
|
REQUIRE_THAT(
|
||||||
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<B<C<D>>>")));
|
puml, IsInstantiation(_A("B<T>"), _A("B<std::unique_ptr<C<D>>>")));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, IsInstantiation(_A("A<T>"), _A("A<B<std::unique_ptr<C<D>>>>")));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||