Initial support for concept dependency relationships in class diagrams
This commit is contained in:
@@ -102,6 +102,23 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const
|
|||||||
m_generated_aliases.emplace(e.alias());
|
m_generated_aliases.emplace(e.alias());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generator::generate_alias(const concept_ &c, std::ostream &ostr) const
|
||||||
|
{
|
||||||
|
print_debug(c, ostr);
|
||||||
|
|
||||||
|
if (m_config.generate_packages())
|
||||||
|
ostr << "annotation"
|
||||||
|
<< " \"" << c.name();
|
||||||
|
else
|
||||||
|
ostr << "annotation"
|
||||||
|
<< " \"" << render_name(c.full_name());
|
||||||
|
|
||||||
|
ostr << "\" as " << c.alias() << '\n';
|
||||||
|
|
||||||
|
// Register the added alias
|
||||||
|
m_generated_aliases.emplace(c.alias());
|
||||||
|
}
|
||||||
|
|
||||||
void generator::generate(const class_ &c, std::ostream &ostr) const
|
void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
namespace plantuml_common = clanguml::common::generators::plantuml;
|
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||||
@@ -279,6 +296,26 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
|||||||
generate_member_notes(ostr, method, c.alias());
|
generate_member_notes(ostr, method, c.alias());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generator::generate(const concept_ &c, std::ostream &ostr) const
|
||||||
|
{
|
||||||
|
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||||
|
|
||||||
|
std::string class_type{"annotation"};
|
||||||
|
|
||||||
|
ostr << class_type << " " << c.alias() << " <<concept>>";
|
||||||
|
|
||||||
|
if (m_config.generate_links) {
|
||||||
|
common_generator<diagram_config, diagram_model>::generate_link(ostr, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c.style().empty())
|
||||||
|
ostr << " " << c.style();
|
||||||
|
|
||||||
|
ostr << " {" << '\n';
|
||||||
|
|
||||||
|
ostr << "}" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
void generator::generate_member_notes(std::ostream &ostr,
|
void generator::generate_member_notes(std::ostream &ostr,
|
||||||
const class_element &member, const std::string &alias) const
|
const class_element &member, const std::string &alias) const
|
||||||
{
|
{
|
||||||
@@ -309,6 +346,11 @@ void generator::generate_relationships(std::ostream &ostr) const
|
|||||||
generate_relationships(*enm, ostr);
|
generate_relationships(*enm, ostr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||||
|
if (m_model.should_include(*cpt)) {
|
||||||
|
generate_relationships(*cpt, ostr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +451,82 @@ void generator::generate_relationships(
|
|||||||
ostr << all_relations_str.str();
|
ostr << all_relations_str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void generator::generate_relationships(
|
||||||
|
const concept_ &c, std::ostream &ostr) const
|
||||||
|
{
|
||||||
|
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Process relationships
|
||||||
|
//
|
||||||
|
std::set<std::string> rendered_relations;
|
||||||
|
|
||||||
|
std::stringstream all_relations_str;
|
||||||
|
std::set<std::string> unique_relations;
|
||||||
|
|
||||||
|
for (const auto &r : c.relationships()) {
|
||||||
|
if (!m_model.should_include(r.type()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOG_DBG("== Processing relationship {}",
|
||||||
|
plantuml_common::to_plantuml(r.type(), r.style()));
|
||||||
|
|
||||||
|
std::stringstream relstr;
|
||||||
|
clanguml::common::id_t destination{0};
|
||||||
|
try {
|
||||||
|
destination = r.destination();
|
||||||
|
|
||||||
|
std::string puml_relation;
|
||||||
|
if (!r.multiplicity_source().empty())
|
||||||
|
puml_relation += "\"" + r.multiplicity_source() + "\" ";
|
||||||
|
|
||||||
|
puml_relation += plantuml_common::to_plantuml(r.type(), r.style());
|
||||||
|
|
||||||
|
if (!r.multiplicity_destination().empty())
|
||||||
|
puml_relation += " \"" + r.multiplicity_destination() + "\"";
|
||||||
|
|
||||||
|
std::string target_alias;
|
||||||
|
try {
|
||||||
|
target_alias = m_model.to_alias(destination);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
LOG_DBG("Failed to find alias to {}", destination);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_generated_aliases.find(target_alias) ==
|
||||||
|
m_generated_aliases.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
relstr << c.alias() << " " << puml_relation << " " << target_alias;
|
||||||
|
|
||||||
|
if (!r.label().empty()) {
|
||||||
|
relstr << " : " << plantuml_common::to_plantuml(r.access())
|
||||||
|
<< r.label();
|
||||||
|
rendered_relations.emplace(r.label());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unique_relations.count(relstr.str()) == 0) {
|
||||||
|
unique_relations.emplace(relstr.str());
|
||||||
|
|
||||||
|
relstr << '\n';
|
||||||
|
|
||||||
|
LOG_DBG("=== Adding relation {}", relstr.str());
|
||||||
|
|
||||||
|
all_relations_str << relstr.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error::uml_alias_missing &e) {
|
||||||
|
LOG_DBG("=== Skipping {} relation from {} to {} due "
|
||||||
|
"to: {}",
|
||||||
|
plantuml_common::to_plantuml(r.type(), r.style()),
|
||||||
|
c.full_name(), destination, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ostr << all_relations_str.str();
|
||||||
|
}
|
||||||
|
|
||||||
void generator::generate(const enum_ &e, std::ostream &ostr) const
|
void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
ostr << "enum " << e.alias();
|
ostr << "enum " << e.alias();
|
||||||
@@ -534,6 +652,20 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto *cpt = dynamic_cast<concept_ *>(subpackage.get()); cpt) {
|
||||||
|
if (m_model.should_include(*subpackage)) {
|
||||||
|
auto together_group =
|
||||||
|
m_config.get_together_group(cpt->full_name(false));
|
||||||
|
if (together_group) {
|
||||||
|
together_group_stack_.group_together(
|
||||||
|
together_group.value(), cpt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
generate_alias(*cpt, ostr);
|
||||||
|
generate(*cpt, ostr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.generate_packages()) {
|
if (m_config.generate_packages()) {
|
||||||
@@ -552,6 +684,10 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
|||||||
generate_alias(*enm, ostr);
|
generate_alias(*enm, ostr);
|
||||||
generate(*enm, ostr);
|
generate(*enm, ostr);
|
||||||
}
|
}
|
||||||
|
if (auto *cpt = dynamic_cast<concept_ *>(e); cpt) {
|
||||||
|
generate_alias(*cpt, ostr);
|
||||||
|
generate(*cpt, ostr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ostr << "}\n";
|
ostr << "}\n";
|
||||||
@@ -589,6 +725,12 @@ void generator::generate_relationships(
|
|||||||
dynamic_cast<enum_ &>(*subpackage), ostr);
|
dynamic_cast<enum_ &>(*subpackage), ostr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (dynamic_cast<concept_ *>(subpackage.get()) != nullptr) {
|
||||||
|
if (m_model.should_include(*subpackage)) {
|
||||||
|
generate_relationships(
|
||||||
|
dynamic_cast<concept_ &>(*subpackage), ostr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +790,20 @@ void generator::generate_top_level_elements(std::ostream &ostr) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||||
|
if (m_model.should_include(*cpt)) {
|
||||||
|
auto together_group =
|
||||||
|
m_config.get_together_group(cpt->full_name(false));
|
||||||
|
if (together_group) {
|
||||||
|
together_group_stack_.group_together(
|
||||||
|
together_group.value(), cpt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
generate_alias(*cpt, ostr);
|
||||||
|
generate(*cpt, ostr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,6 +822,10 @@ void generator::generate_groups(std::ostream &ostr) const
|
|||||||
generate_alias(*enm, ostr);
|
generate_alias(*enm, ostr);
|
||||||
generate(*enm, ostr);
|
generate(*enm, ostr);
|
||||||
}
|
}
|
||||||
|
if (auto *cpt = dynamic_cast<concept_ *>(e); cpt) {
|
||||||
|
generate_alias(*cpt, ostr);
|
||||||
|
generate(*cpt, ostr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ostr << "}\n";
|
ostr << "}\n";
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "class_diagram/model/class.h"
|
#include "class_diagram/model/class.h"
|
||||||
|
#include "class_diagram/model/concept.h"
|
||||||
#include "class_diagram/model/diagram.h"
|
#include "class_diagram/model/diagram.h"
|
||||||
#include "class_diagram/model/enum.h"
|
#include "class_diagram/model/enum.h"
|
||||||
#include "class_diagram/visitor/translation_unit_visitor.h"
|
#include "class_diagram/visitor/translation_unit_visitor.h"
|
||||||
@@ -47,6 +48,7 @@ using common_generator =
|
|||||||
|
|
||||||
using clanguml::class_diagram::model::class_;
|
using clanguml::class_diagram::model::class_;
|
||||||
using clanguml::class_diagram::model::class_element;
|
using clanguml::class_diagram::model::class_element;
|
||||||
|
using clanguml::class_diagram::model::concept_;
|
||||||
using clanguml::class_diagram::model::enum_;
|
using clanguml::class_diagram::model::enum_;
|
||||||
using clanguml::common::model::access_t;
|
using clanguml::common::model::access_t;
|
||||||
using clanguml::common::model::package;
|
using clanguml::common::model::package;
|
||||||
@@ -65,6 +67,8 @@ public:
|
|||||||
|
|
||||||
void generate_alias(const enum_ &e, std::ostream &ostr) const;
|
void generate_alias(const enum_ &e, std::ostream &ostr) const;
|
||||||
|
|
||||||
|
void generate_alias(const concept_ &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
void generate(const class_ &c, std::ostream &ostr) const;
|
void generate(const class_ &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
void generate_top_level_elements(std::ostream &ostr) const;
|
void generate_top_level_elements(std::ostream &ostr) const;
|
||||||
@@ -77,6 +81,10 @@ public:
|
|||||||
|
|
||||||
void generate_relationships(const enum_ &c, std::ostream &ostr) const;
|
void generate_relationships(const enum_ &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
|
void generate(const concept_ &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
|
void generate_relationships(const concept_ &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
void generate(const package &p, std::ostream &ostr) const;
|
void generate(const package &p, std::ostream &ostr) const;
|
||||||
|
|
||||||
void generate_relationships(const package &p, std::ostream &ostr) const;
|
void generate_relationships(const package &p, std::ostream &ostr) const;
|
||||||
|
|||||||
@@ -79,9 +79,6 @@ public:
|
|||||||
|
|
||||||
bool is_abstract() const;
|
bool is_abstract() const;
|
||||||
|
|
||||||
bool is_concept() const { return is_concept_; }
|
|
||||||
void is_concept(bool concept_) { is_concept_ = concept_; }
|
|
||||||
|
|
||||||
void find_relationships(
|
void find_relationships(
|
||||||
std::vector<std::pair<std::string, common::model::relationship_t>>
|
std::vector<std::pair<std::string, common::model::relationship_t>>
|
||||||
&nested_relationships);
|
&nested_relationships);
|
||||||
@@ -95,7 +92,6 @@ private:
|
|||||||
bool is_template_instantiation_{false};
|
bool is_template_instantiation_{false};
|
||||||
bool is_alias_{false};
|
bool is_alias_{false};
|
||||||
bool is_union_{false};
|
bool is_union_{false};
|
||||||
bool is_concept_{false};
|
|
||||||
std::vector<class_member> members_;
|
std::vector<class_member> members_;
|
||||||
std::vector<class_method> methods_;
|
std::vector<class_method> methods_;
|
||||||
std::vector<class_parent> bases_;
|
std::vector<class_parent> bases_;
|
||||||
|
|||||||
72
src/class_diagram/model/concept.cc
Normal file
72
src/class_diagram/model/concept.cc
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* src/class_diagram/model/concept.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "concept.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace clanguml::class_diagram::model {
|
||||||
|
|
||||||
|
concept_::concept_(const common::model::namespace_ &using_namespace)
|
||||||
|
: element{using_namespace}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const concept_ &l, const concept_ &r)
|
||||||
|
{
|
||||||
|
return l.id() == r.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string concept_::full_name_no_ns() const
|
||||||
|
{
|
||||||
|
using namespace clanguml::util;
|
||||||
|
|
||||||
|
std::ostringstream ostr;
|
||||||
|
|
||||||
|
ostr << name();
|
||||||
|
|
||||||
|
render_template_params(ostr, using_namespace(), false);
|
||||||
|
|
||||||
|
return ostr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string concept_::full_name(bool relative) const
|
||||||
|
{
|
||||||
|
using namespace clanguml::util;
|
||||||
|
using clanguml::common::model::namespace_;
|
||||||
|
|
||||||
|
std::ostringstream ostr;
|
||||||
|
|
||||||
|
ostr << name_and_ns();
|
||||||
|
|
||||||
|
render_template_params(ostr, using_namespace(), relative);
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
if (relative)
|
||||||
|
res = using_namespace().relative(ostr.str());
|
||||||
|
else
|
||||||
|
res = ostr.str();
|
||||||
|
|
||||||
|
if (res.empty())
|
||||||
|
return "<<anonymous>>";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
59
src/class_diagram/model/concept.h
Normal file
59
src/class_diagram/model/concept.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* src/class_diagram/model/concept.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/model/element.h"
|
||||||
|
#include "common/model/stylable_element.h"
|
||||||
|
#include "common/model/template_parameter.h"
|
||||||
|
#include "common/model/template_trait.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml::class_diagram::model {
|
||||||
|
|
||||||
|
struct requires_expression {
|
||||||
|
common::model::template_parameter parameter;
|
||||||
|
std::vector<std::string> requirements;
|
||||||
|
};
|
||||||
|
|
||||||
|
class concept_ : public common::model::element,
|
||||||
|
public common::model::stylable_element,
|
||||||
|
public common::model::template_trait {
|
||||||
|
public:
|
||||||
|
concept_(const common::model::namespace_ &using_namespace);
|
||||||
|
|
||||||
|
concept_(const concept_ &) = delete;
|
||||||
|
concept_(concept_ &&) noexcept = default;
|
||||||
|
concept_ &operator=(const concept_ &) = delete;
|
||||||
|
concept_ &operator=(concept_ &&) = delete;
|
||||||
|
|
||||||
|
std::string type_name() const override { return "concept"; }
|
||||||
|
|
||||||
|
friend bool operator==(const concept_ &l, const concept_ &r);
|
||||||
|
|
||||||
|
std::string full_name(bool relative = true) const override;
|
||||||
|
|
||||||
|
std::string full_name_no_ns() const override;
|
||||||
|
|
||||||
|
std::vector<std::string> requires_expression_;
|
||||||
|
|
||||||
|
std::string full_name_;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -32,6 +32,11 @@ const common::reference_vector<class_> &diagram::classes() const
|
|||||||
|
|
||||||
const common::reference_vector<enum_> &diagram::enums() const { return enums_; }
|
const common::reference_vector<enum_> &diagram::enums() const { return enums_; }
|
||||||
|
|
||||||
|
const common::reference_vector<concept_> &diagram::concepts() const
|
||||||
|
{
|
||||||
|
return concepts_;
|
||||||
|
}
|
||||||
|
|
||||||
common::model::diagram_t diagram::type() const
|
common::model::diagram_t diagram::type() const
|
||||||
{
|
{
|
||||||
return common::model::diagram_t::kClass;
|
return common::model::diagram_t::kClass;
|
||||||
@@ -48,6 +53,11 @@ common::optional_ref<clanguml::common::model::diagram_element> diagram::get(
|
|||||||
|
|
||||||
res = get_enum(full_name);
|
res = get_enum(full_name);
|
||||||
|
|
||||||
|
if (res.has_value())
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = get_concept(full_name);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +73,11 @@ common::optional_ref<clanguml::common::model::diagram_element> diagram::get(
|
|||||||
|
|
||||||
res = get_enum(id);
|
res = get_enum(id);
|
||||||
|
|
||||||
|
if (res.has_value())
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = get_concept(id);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +93,12 @@ bool diagram::has_enum(const enum_ &e) const
|
|||||||
[&e](const auto &ee) { return ee.get().full_name() == e.full_name(); });
|
[&e](const auto &ee) { return ee.get().full_name() == e.full_name(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool diagram::has_concept(const concept_ &c) const
|
||||||
|
{
|
||||||
|
return std::any_of(concepts_.cbegin(), concepts_.cend(),
|
||||||
|
[&c](const auto &cc) { return cc.get() == c; });
|
||||||
|
}
|
||||||
|
|
||||||
common::optional_ref<class_> diagram::get_class(const std::string &name) const
|
common::optional_ref<class_> diagram::get_class(const std::string &name) const
|
||||||
{
|
{
|
||||||
for (const auto &c : classes_) {
|
for (const auto &c : classes_) {
|
||||||
@@ -126,6 +147,32 @@ common::optional_ref<enum_> diagram::get_enum(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common::optional_ref<concept_> diagram::get_concept(
|
||||||
|
const std::string &name) const
|
||||||
|
{
|
||||||
|
for (const auto &c : concepts_) {
|
||||||
|
const auto full_name = c.get().full_name(false);
|
||||||
|
|
||||||
|
if (full_name == name) {
|
||||||
|
return {c};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
common::optional_ref<concept_> diagram::get_concept(
|
||||||
|
clanguml::common::model::diagram_element::id_t id) const
|
||||||
|
{
|
||||||
|
for (const auto &c : concepts_) {
|
||||||
|
if (c.get().id() == id) {
|
||||||
|
return {c};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
bool diagram::add_package(std::unique_ptr<common::model::package> &&p)
|
bool diagram::add_package(std::unique_ptr<common::model::package> &&p)
|
||||||
{
|
{
|
||||||
LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
|
LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
|
||||||
@@ -173,8 +220,8 @@ bool diagram::add_class(std::unique_ptr<class_> &&c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::runtime_error &e) {
|
catch (const std::runtime_error &e) {
|
||||||
LOG_WARN("Cannot add template specialization {} with id {} due to: {}",
|
LOG_WARN(
|
||||||
name, id, e.what());
|
"Cannot add concept {} with id {} due to: {}", name, id, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +254,55 @@ bool diagram::add_enum(std::unique_ptr<enum_> &&e)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool diagram::add_concept(std::unique_ptr<concept_> &&c)
|
||||||
|
{
|
||||||
|
const auto base_name = c->name();
|
||||||
|
const auto full_name = c->full_name(false);
|
||||||
|
|
||||||
|
LOG_DBG("Adding concept: {}::{}, {}", c->get_namespace().to_string(),
|
||||||
|
base_name, full_name);
|
||||||
|
|
||||||
|
if (util::contains(base_name, "::"))
|
||||||
|
throw std::runtime_error("Name cannot contain namespace: " + base_name);
|
||||||
|
|
||||||
|
if (util::contains(base_name, "*"))
|
||||||
|
throw std::runtime_error("Name cannot contain *: " + base_name);
|
||||||
|
|
||||||
|
const auto ns = c->get_relative_namespace();
|
||||||
|
auto name = base_name;
|
||||||
|
auto name_with_ns = c->name_and_ns();
|
||||||
|
auto name_and_ns = ns | name;
|
||||||
|
auto &cc = *c;
|
||||||
|
auto id = cc.id();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!has_concept(cc)) {
|
||||||
|
if (add_element(ns, std::move(c)))
|
||||||
|
concepts_.push_back(std::ref(cc));
|
||||||
|
|
||||||
|
const auto &el = get_element<concept_>(name_and_ns).value();
|
||||||
|
|
||||||
|
if ((el.name() != name) || !(el.get_relative_namespace() == ns))
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Invalid element stored in the diagram tree");
|
||||||
|
|
||||||
|
LOG_DBG("Added concept {} ({} - [{}])", base_name, full_name, id);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &e) {
|
||||||
|
LOG_WARN(
|
||||||
|
"Cannot add concept {} with id {} due to: {}", name, id, e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Concept {} ({} - [{}]) already in the model", base_name, full_name,
|
||||||
|
id);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void diagram::get_parents(
|
void diagram::get_parents(
|
||||||
clanguml::common::reference_set<class_> &parents) const
|
clanguml::common::reference_set<class_> &parents) const
|
||||||
{
|
{
|
||||||
@@ -257,6 +353,11 @@ std::string diagram::to_alias(
|
|||||||
return e.get().alias();
|
return e.get().alias();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto &c : concepts_) {
|
||||||
|
if (c.get().id() == id)
|
||||||
|
return c.get().alias();
|
||||||
|
}
|
||||||
|
|
||||||
throw error::uml_alias_missing(fmt::format("Missing alias for {}", id));
|
throw error::uml_alias_missing(fmt::format("Missing alias for {}", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "common/model/nested_trait.h"
|
#include "common/model/nested_trait.h"
|
||||||
#include "common/model/package.h"
|
#include "common/model/package.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "concept.h"
|
||||||
#include "enum.h"
|
#include "enum.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -54,10 +55,14 @@ public:
|
|||||||
|
|
||||||
const common::reference_vector<enum_> &enums() const;
|
const common::reference_vector<enum_> &enums() const;
|
||||||
|
|
||||||
|
const common::reference_vector<concept_> &concepts() const;
|
||||||
|
|
||||||
bool has_class(const class_ &c) const;
|
bool has_class(const class_ &c) const;
|
||||||
|
|
||||||
bool has_enum(const enum_ &e) const;
|
bool has_enum(const enum_ &e) const;
|
||||||
|
|
||||||
|
bool has_concept(const concept_ &e) const;
|
||||||
|
|
||||||
common::optional_ref<class_> get_class(const std::string &name) const;
|
common::optional_ref<class_> get_class(const std::string &name) const;
|
||||||
|
|
||||||
common::optional_ref<class_> get_class(
|
common::optional_ref<class_> get_class(
|
||||||
@@ -68,10 +73,17 @@ public:
|
|||||||
common::optional_ref<enum_> get_enum(
|
common::optional_ref<enum_> get_enum(
|
||||||
clanguml::common::model::diagram_element::id_t id) const;
|
clanguml::common::model::diagram_element::id_t id) const;
|
||||||
|
|
||||||
|
common::optional_ref<concept_> get_concept(const std::string &name) const;
|
||||||
|
|
||||||
|
common::optional_ref<concept_> get_concept(
|
||||||
|
clanguml::common::model::diagram_element::id_t id) const;
|
||||||
|
|
||||||
bool add_class(std::unique_ptr<class_> &&c);
|
bool add_class(std::unique_ptr<class_> &&c);
|
||||||
|
|
||||||
bool add_enum(std::unique_ptr<enum_> &&e);
|
bool add_enum(std::unique_ptr<enum_> &&e);
|
||||||
|
|
||||||
|
bool add_concept(std::unique_ptr<concept_> &&e);
|
||||||
|
|
||||||
bool add_package(std::unique_ptr<common::model::package> &&p);
|
bool add_package(std::unique_ptr<common::model::package> &&p);
|
||||||
|
|
||||||
std::string to_alias(
|
std::string to_alias(
|
||||||
@@ -90,6 +102,8 @@ private:
|
|||||||
common::reference_vector<class_> classes_;
|
common::reference_vector<class_> classes_;
|
||||||
|
|
||||||
common::reference_vector<enum_> enums_;
|
common::reference_vector<enum_> enums_;
|
||||||
|
|
||||||
|
common::reference_vector<concept_> concepts_;
|
||||||
};
|
};
|
||||||
} // namespace clanguml::class_diagram::model
|
} // namespace clanguml::class_diagram::model
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "translation_unit_visitor.h"
|
#include "translation_unit_visitor.h"
|
||||||
#include "common/clang_utils.h"
|
#include "common/clang_utils.h"
|
||||||
|
|
||||||
|
#include <clang/AST/ExprConcepts.h>
|
||||||
#include <clang/Basic/FileManager.h>
|
#include <clang/Basic/FileManager.h>
|
||||||
#include <clang/Lex/Preprocessor.h>
|
#include <clang/Lex/Preprocessor.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
@@ -284,7 +285,7 @@ bool translation_unit_visitor::VisitClassTemplateDecl(
|
|||||||
// Override the id with the template id, for now we don't care about the
|
// Override the id with the template id, for now we don't care about the
|
||||||
// underlying templated class id
|
// underlying templated class id
|
||||||
|
|
||||||
process_template_parameters(*cls, *c_ptr);
|
process_template_parameters(*cls, *c_ptr, *c_ptr);
|
||||||
|
|
||||||
const auto cls_full_name = c_ptr->full_name(false);
|
const auto cls_full_name = c_ptr->full_name(false);
|
||||||
const auto id = common::to_id(cls_full_name);
|
const auto id = common::to_id(cls_full_name);
|
||||||
@@ -293,6 +294,15 @@ bool translation_unit_visitor::VisitClassTemplateDecl(
|
|||||||
|
|
||||||
set_ast_local_id(cls->getID(), id);
|
set_ast_local_id(cls->getID(), id);
|
||||||
|
|
||||||
|
llvm::SmallVector<const clang::Expr *, 24> constraints{};
|
||||||
|
if (cls->hasAssociatedConstraints()) {
|
||||||
|
cls->getAssociatedConstraints(constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto *expr : constraints) {
|
||||||
|
find_relationships_in_constraint_expression(*c_ptr, expr);
|
||||||
|
}
|
||||||
|
|
||||||
if (!cls->getTemplatedDecl()->isCompleteDefinition()) {
|
if (!cls->getTemplatedDecl()->isCompleteDefinition()) {
|
||||||
forward_declarations_.emplace(id, std::move(c_ptr));
|
forward_declarations_.emplace(id, std::move(c_ptr));
|
||||||
return true;
|
return true;
|
||||||
@@ -367,6 +377,155 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt)
|
||||||
|
{
|
||||||
|
// Skip system headers
|
||||||
|
if (source_manager().isInSystemHeader(cpt->getSourceRange().getBegin()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!diagram().should_include(cpt->getQualifiedNameAsString()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG_DBG("= Visiting concept (isType: {}) declaration {} at {}",
|
||||||
|
cpt->isTypeConcept(), cpt->getQualifiedNameAsString(),
|
||||||
|
cpt->getLocation().printToString(source_manager()));
|
||||||
|
|
||||||
|
auto concept_model = create_concept_declaration(cpt);
|
||||||
|
|
||||||
|
if (!concept_model)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto concept_id = concept_model->id();
|
||||||
|
|
||||||
|
set_ast_local_id(cpt->getID(), concept_id);
|
||||||
|
|
||||||
|
process_template_parameters(*cpt, *concept_model);
|
||||||
|
|
||||||
|
if (const auto *constraint =
|
||||||
|
clang::dyn_cast<clang::RequiresExpr>(cpt->getConstraintExpr());
|
||||||
|
constraint) {
|
||||||
|
|
||||||
|
auto constraint_source = common::to_string(constraint);
|
||||||
|
|
||||||
|
LOG_DBG("== Processing constraint: '{}'", constraint_source);
|
||||||
|
|
||||||
|
for (const auto *requirement : constraint->getRequirements()) {
|
||||||
|
LOG_DBG("== Processing requirement: '{}'", requirement->getKind());
|
||||||
|
}
|
||||||
|
|
||||||
|
// process 'requires (...)' declaration
|
||||||
|
for (const auto *decl : constraint->getBody()->decls()) {
|
||||||
|
// decl->dump();
|
||||||
|
|
||||||
|
if (const auto *parm_var_decl =
|
||||||
|
clang::dyn_cast<clang::ParmVarDecl>(decl);
|
||||||
|
parm_var_decl) {
|
||||||
|
parm_var_decl->getQualifiedNameAsString();
|
||||||
|
|
||||||
|
LOG_DBG("=== Processing parameter variable declaration: {}, {}",
|
||||||
|
parm_var_decl->getQualifiedNameAsString(),
|
||||||
|
common::to_string(
|
||||||
|
parm_var_decl->getType(), cpt->getASTContext()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG(
|
||||||
|
"=== Processing some other declaration: {}", decl->getID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process concept body requirements '{ }' if any
|
||||||
|
for (const auto *req : constraint->getRequirements()) {
|
||||||
|
if (req->getKind() == clang::concepts::Requirement::RK_Simple) {
|
||||||
|
const auto *simple_req =
|
||||||
|
clang::dyn_cast<clang::concepts::ExprRequirement>(req);
|
||||||
|
LOG_DBG("=== Processing expression requirement: {}",
|
||||||
|
common::to_string(simple_req->getExpr()));
|
||||||
|
}
|
||||||
|
else if (req->getKind() == clang::concepts::Requirement::RK_Type) {
|
||||||
|
const auto *type_req =
|
||||||
|
clang::dyn_cast<clang::concepts::TypeRequirement>(req);
|
||||||
|
LOG_DBG(
|
||||||
|
"=== Processing type requirement: {}", type_req->getKind());
|
||||||
|
}
|
||||||
|
else if (req->getKind() ==
|
||||||
|
clang::concepts::Requirement::RK_Nested) {
|
||||||
|
const auto *nested_req =
|
||||||
|
clang::dyn_cast<clang::concepts::NestedRequirement>(req);
|
||||||
|
LOG_DBG("=== Processing nested requirement: {}",
|
||||||
|
common::to_string(nested_req->getConstraintExpr()));
|
||||||
|
}
|
||||||
|
else if (req->getKind() ==
|
||||||
|
clang::concepts::Requirement::RK_Compound) {
|
||||||
|
const auto *nested_req =
|
||||||
|
clang::dyn_cast<clang::concepts::ExprRequirement>(req);
|
||||||
|
LOG_DBG("=== Processing compound requirement: {}",
|
||||||
|
common::to_string(nested_req->getExpr()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpt->getConstraintExpr())
|
||||||
|
find_relationships_in_constraint_expression(
|
||||||
|
*concept_model, cpt->getConstraintExpr());
|
||||||
|
|
||||||
|
if (diagram_.should_include(*concept_model)) {
|
||||||
|
LOG_DBG("Adding concept {} with id {}", concept_model->full_name(false),
|
||||||
|
concept_model->id());
|
||||||
|
|
||||||
|
diagram_.add_concept(std::move(concept_model));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("Skipping concept {} with id {}", concept_model->full_name(),
|
||||||
|
concept_model->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::find_relationships_in_constraint_expression(
|
||||||
|
clanguml::common::model::element &c, const clang::Expr *expr)
|
||||||
|
{
|
||||||
|
if (expr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (const auto *concept_specialization =
|
||||||
|
clang::dyn_cast<clang::ConceptSpecializationExpr>(expr);
|
||||||
|
concept_specialization) {
|
||||||
|
if (concept_specialization->getNamedConcept() &&
|
||||||
|
diagram().should_include(concept_specialization->getNamedConcept()
|
||||||
|
->getQualifiedNameAsString())) {
|
||||||
|
auto target_id = get_ast_local_id(
|
||||||
|
concept_specialization->getNamedConcept()->getID())
|
||||||
|
.value();
|
||||||
|
|
||||||
|
for (const auto ta :
|
||||||
|
concept_specialization->getTemplateArguments()) {
|
||||||
|
if (ta.getKind() == clang::TemplateArgument::Template)
|
||||||
|
ta.getAsTemplateOrTemplatePattern().dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
c.add_relationship({relationship_t::kDependency, target_id});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (const auto *constraint =
|
||||||
|
clang::dyn_cast<clang::RequiresExpr>(expr);
|
||||||
|
constraint) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else if (const auto *binop = clang::dyn_cast<clang::BinaryOperator>(expr);
|
||||||
|
binop) {
|
||||||
|
find_relationships_in_constraint_expression(c, binop->getLHS());
|
||||||
|
find_relationships_in_constraint_expression(c, binop->getRHS());
|
||||||
|
}
|
||||||
|
else if (const auto *unop = clang::dyn_cast<clang::UnaryOperator>(expr);
|
||||||
|
unop) {
|
||||||
|
find_relationships_in_constraint_expression(c, unop->getSubExpr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
||||||
{
|
{
|
||||||
// Skip system headers
|
// Skip system headers
|
||||||
@@ -440,6 +599,37 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<clanguml::class_diagram::model::concept_>
|
||||||
|
translation_unit_visitor::create_concept_declaration(clang::ConceptDecl *cpt)
|
||||||
|
{
|
||||||
|
assert(cpt != nullptr);
|
||||||
|
|
||||||
|
auto concept_ptr{
|
||||||
|
std::make_unique<model::concept_>(config_.using_namespace())};
|
||||||
|
auto &concept_model = *concept_ptr;
|
||||||
|
|
||||||
|
auto qualified_name = cpt->getQualifiedNameAsString();
|
||||||
|
|
||||||
|
if (!diagram().should_include(qualified_name))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto ns = common::get_template_namespace(*cpt);
|
||||||
|
|
||||||
|
concept_model.set_name(cpt->getNameAsString());
|
||||||
|
concept_model.set_namespace(ns);
|
||||||
|
concept_model.set_id(common::to_id(concept_model.full_name(false)));
|
||||||
|
|
||||||
|
process_comment(*cpt, concept_model);
|
||||||
|
set_source_location(*cpt, concept_model);
|
||||||
|
|
||||||
|
if (concept_model.skip())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
concept_model.set_style(concept_model.style_spec());
|
||||||
|
|
||||||
|
return concept_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<class_> translation_unit_visitor::create_record_declaration(
|
std::unique_ptr<class_> translation_unit_visitor::create_record_declaration(
|
||||||
clang::RecordDecl *rec)
|
clang::RecordDecl *rec)
|
||||||
{
|
{
|
||||||
@@ -490,7 +680,7 @@ std::unique_ptr<class_> translation_unit_visitor::create_class_declaration(
|
|||||||
if (!diagram().should_include(qualified_name))
|
if (!diagram().should_include(qualified_name))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto ns = common::get_tag_namespace(*cls);
|
auto ns{common::get_tag_namespace(*cls)};
|
||||||
|
|
||||||
process_record_parent(cls, c, ns);
|
process_record_parent(cls, c, ns);
|
||||||
|
|
||||||
@@ -531,7 +721,8 @@ void translation_unit_visitor::process_record_parent(
|
|||||||
// regular class
|
// regular class
|
||||||
id_opt = get_ast_local_id(local_id);
|
id_opt = get_ast_local_id(local_id);
|
||||||
|
|
||||||
// If not, check if the parent template declaration is in the model
|
// If not, check if the parent template declaration is in the
|
||||||
|
// model
|
||||||
if (!id_opt) {
|
if (!id_opt) {
|
||||||
if (parent_record_decl->getDescribedTemplate() != nullptr) {
|
if (parent_record_decl->getDescribedTemplate() != nullptr) {
|
||||||
local_id =
|
local_id =
|
||||||
@@ -600,7 +791,8 @@ void translation_unit_visitor::process_class_declaration(
|
|||||||
|
|
||||||
bool translation_unit_visitor::process_template_parameters(
|
bool translation_unit_visitor::process_template_parameters(
|
||||||
const clang::TemplateDecl &template_declaration,
|
const clang::TemplateDecl &template_declaration,
|
||||||
common::model::template_trait &c)
|
common::model::template_trait &c,
|
||||||
|
common::optional_ref<common::model::element> templated_element)
|
||||||
{
|
{
|
||||||
LOG_DBG("Processing class {} template parameters...",
|
LOG_DBG("Processing class {} template parameters...",
|
||||||
common::get_qualified_name(template_declaration));
|
common::get_qualified_name(template_declaration));
|
||||||
@@ -621,6 +813,16 @@ bool translation_unit_visitor::process_template_parameters(
|
|||||||
ct.set_default_value("");
|
ct.set_default_value("");
|
||||||
ct.is_variadic(template_type_parameter->isParameterPack());
|
ct.is_variadic(template_type_parameter->isParameterPack());
|
||||||
|
|
||||||
|
if (template_type_parameter->getTypeConstraint()) {
|
||||||
|
util::apply_if_not_null(
|
||||||
|
template_type_parameter->getTypeConstraint()
|
||||||
|
->getNamedConcept(),
|
||||||
|
[&ct](const clang::ConceptDecl *named_concept) mutable {
|
||||||
|
ct.set_concept_constraint(
|
||||||
|
named_concept->getQualifiedNameAsString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
c.add_template(std::move(ct));
|
c.add_template(std::move(ct));
|
||||||
}
|
}
|
||||||
else if (clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
|
else if (clang::dyn_cast_or_null<clang::NonTypeTemplateParmDecl>(
|
||||||
@@ -2405,6 +2607,17 @@ void translation_unit_visitor::resolve_local_to_global_ids()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const auto &cpt : diagram().concepts()) {
|
||||||
|
for (auto &rel : cpt.get().relationships()) {
|
||||||
|
const auto maybe_local_id = rel.destination();
|
||||||
|
if (get_ast_local_id(maybe_local_id)) {
|
||||||
|
LOG_DBG("= Resolved instantiation destination from local "
|
||||||
|
"id {} to global id {}",
|
||||||
|
maybe_local_id, *get_ast_local_id(maybe_local_id));
|
||||||
|
rel.set_destination(*get_ast_local_id(maybe_local_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void translation_unit_visitor::finalize()
|
void translation_unit_visitor::finalize()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "class_diagram/model/class.h"
|
#include "class_diagram/model/class.h"
|
||||||
|
#include "class_diagram/model/concept.h"
|
||||||
#include "class_diagram/model/diagram.h"
|
#include "class_diagram/model/diagram.h"
|
||||||
#include "common/model/enums.h"
|
#include "common/model/enums.h"
|
||||||
#include "common/model/template_trait.h"
|
#include "common/model/template_trait.h"
|
||||||
@@ -83,12 +84,7 @@ public:
|
|||||||
|
|
||||||
virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls);
|
virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls);
|
||||||
|
|
||||||
// bool TraverseTypeConstraint(const clang::TypeConstraint *C);
|
virtual bool TraverseConceptDecl(clang::ConceptDecl *cpt);
|
||||||
// bool TraverseConceptRequirement(clang::concepts::Requirement *R);
|
|
||||||
// bool TraverseConceptTypeRequirement(clang::concepts::TypeRequirement *R);
|
|
||||||
// bool TraverseConceptExprRequirement(clang::concepts::ExprRequirement *R);
|
|
||||||
// bool TraverseConceptNestedRequirement(
|
|
||||||
// clang::concepts::NestedRequirement *R);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get diagram model reference
|
* @brief Get diagram model reference
|
||||||
@@ -121,6 +117,9 @@ private:
|
|||||||
std::unique_ptr<clanguml::class_diagram::model::class_>
|
std::unique_ptr<clanguml::class_diagram::model::class_>
|
||||||
create_record_declaration(clang::RecordDecl *rec);
|
create_record_declaration(clang::RecordDecl *rec);
|
||||||
|
|
||||||
|
std::unique_ptr<clanguml::class_diagram::model::concept_>
|
||||||
|
create_concept_declaration(clang::ConceptDecl *cpt);
|
||||||
|
|
||||||
void process_class_declaration(const clang::CXXRecordDecl &cls,
|
void process_class_declaration(const clang::CXXRecordDecl &cls,
|
||||||
clanguml::class_diagram::model::class_ &c);
|
clanguml::class_diagram::model::class_ &c);
|
||||||
|
|
||||||
@@ -141,7 +140,8 @@ private:
|
|||||||
|
|
||||||
bool process_template_parameters(
|
bool process_template_parameters(
|
||||||
const clang::TemplateDecl &template_declaration,
|
const clang::TemplateDecl &template_declaration,
|
||||||
clanguml::common::model::template_trait &t);
|
clanguml::common::model::template_trait &t,
|
||||||
|
common::optional_ref<common::model::element> templated_element = {});
|
||||||
|
|
||||||
void process_template_specialization_argument(
|
void process_template_specialization_argument(
|
||||||
const clang::ClassTemplateSpecializationDecl *cls,
|
const clang::ClassTemplateSpecializationDecl *cls,
|
||||||
@@ -249,6 +249,9 @@ private:
|
|||||||
const std::set<std::string> &template_parameter_names,
|
const std::set<std::string> &template_parameter_names,
|
||||||
const clang::TemplateSpecializationType &template_instantiation_type);
|
const clang::TemplateSpecializationType &template_instantiation_type);
|
||||||
|
|
||||||
|
void find_relationships_in_constraint_expression(
|
||||||
|
clanguml::common::model::element &c, const clang::Expr *expr);
|
||||||
|
|
||||||
void process_unexposed_template_specialization_parameters(
|
void process_unexposed_template_specialization_parameters(
|
||||||
const std::string &tspec,
|
const std::string &tspec,
|
||||||
clanguml::common::model::template_parameter &tp,
|
clanguml::common::model::template_parameter &tp,
|
||||||
|
|||||||
@@ -90,6 +90,14 @@ model::namespace_ get_tag_namespace(const clang::TagDecl &declaration)
|
|||||||
return ns;
|
return ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model::namespace_ get_template_namespace(const clang::TemplateDecl &declaration)
|
||||||
|
{
|
||||||
|
model::namespace_ ns{declaration.getQualifiedNameAsString()};
|
||||||
|
ns.pop_back();
|
||||||
|
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_tag_name(const clang::TagDecl &declaration)
|
std::string get_tag_name(const clang::TagDecl &declaration)
|
||||||
{
|
{
|
||||||
auto base_name = declaration.getNameAsString();
|
auto base_name = declaration.getNameAsString();
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ template <typename T> std::string get_qualified_name(const T &declaration)
|
|||||||
|
|
||||||
model::namespace_ get_tag_namespace(const clang::TagDecl &declaration);
|
model::namespace_ get_tag_namespace(const clang::TagDecl &declaration);
|
||||||
|
|
||||||
|
model::namespace_ get_template_namespace(
|
||||||
|
const clang::TemplateDecl &declaration);
|
||||||
|
|
||||||
std::optional<clanguml::common::model::namespace_> get_enclosing_namespace(
|
std::optional<clanguml::common::model::namespace_> get_enclosing_namespace(
|
||||||
const clang::DeclContext *decl);
|
const clang::DeclContext *decl);
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ std::string template_parameter::to_string(
|
|||||||
{
|
{
|
||||||
using clanguml::common::model::namespace_;
|
using clanguml::common::model::namespace_;
|
||||||
|
|
||||||
|
assert(!(!type().empty() && concept_constraint().has_value()));
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
if (!type().empty()) {
|
if (!type().empty()) {
|
||||||
if (!relative)
|
if (!relative)
|
||||||
@@ -146,8 +148,17 @@ std::string template_parameter::to_string(
|
|||||||
res += namespace_{type()}.relative_to(using_namespace).to_string();
|
res += namespace_{type()}.relative_to(using_namespace).to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (concept_constraint()) {
|
||||||
|
if (!relative)
|
||||||
|
res += namespace_{concept_constraint().value()}.to_string();
|
||||||
|
else
|
||||||
|
res += namespace_{concept_constraint().value()}
|
||||||
|
.relative_to(using_namespace)
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
if (!name().empty()) {
|
if (!name().empty()) {
|
||||||
if (!type().empty())
|
if (!type().empty() || concept_constraint())
|
||||||
res += " ";
|
res += " ";
|
||||||
if (!relative)
|
if (!relative)
|
||||||
res += namespace_{name()}.to_string();
|
res += namespace_{name()}.to_string();
|
||||||
@@ -216,4 +227,14 @@ bool template_parameter::find_nested_relationships(
|
|||||||
return added_aggregation_relationship;
|
return added_aggregation_relationship;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void template_parameter::set_concept_constraint(std::string constraint)
|
||||||
|
{
|
||||||
|
concept_constraint_ = std::move(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<std::string> &template_parameter::concept_constraint() const
|
||||||
|
{
|
||||||
|
return concept_constraint_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace clanguml::common::model
|
} // namespace clanguml::common::model
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ public:
|
|||||||
const std::function<bool(const std::string &full_name)> &should_include)
|
const std::function<bool(const std::string &full_name)> &should_include)
|
||||||
const;
|
const;
|
||||||
|
|
||||||
|
void set_concept_constraint(std::string constraint);
|
||||||
|
const std::optional<std::string> &concept_constraint() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Represents the type of non-type template parameters
|
/// Represents the type of non-type template parameters
|
||||||
/// e.g. 'int'
|
/// e.g. 'int'
|
||||||
@@ -125,6 +128,10 @@ private:
|
|||||||
/// Whether the argument specializes argument pack from parent template
|
/// Whether the argument specializes argument pack from parent template
|
||||||
bool is_pack_{false};
|
bool is_pack_{false};
|
||||||
|
|
||||||
|
/// Stores optional fully qualified name of constraint for this template
|
||||||
|
/// parameter
|
||||||
|
std::optional<std::string> concept_constraint_;
|
||||||
|
|
||||||
// Nested template parameters
|
// Nested template parameters
|
||||||
std::vector<template_parameter> template_params_;
|
std::vector<template_parameter> template_params_;
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,39 @@ void for_each_if(const T &collection, C &&cond, F &&func)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F, typename FElse>
|
||||||
|
void apply_if_not_null(const T *pointer, F &&func, FElse &&func_else)
|
||||||
|
{
|
||||||
|
if (pointer != nullptr) {
|
||||||
|
std::forward<F>(func)(pointer);
|
||||||
|
}
|
||||||
|
else if (func_else) {
|
||||||
|
std::forward<FElse>(func_else)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
void apply_if_not_null(const T *pointer, F &&func)
|
||||||
|
{
|
||||||
|
apply_if_not_null(pointer, std::forward<F>(func), []() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename FElse>
|
||||||
|
void apply_if(const bool condition, F &&func, FElse &&func_else)
|
||||||
|
{
|
||||||
|
if (condition) {
|
||||||
|
std::forward<F>(func)();
|
||||||
|
}
|
||||||
|
else if (func_else) {
|
||||||
|
std::forward<FElse>(func_else)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> void apply_if(const bool condition, F &&func)
|
||||||
|
{
|
||||||
|
apply_if(condition, std::forward<F>(func), []() {});
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t hash_seed(std::size_t seed);
|
std::size_t hash_seed(std::size_t seed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,53 +2,71 @@
|
|||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace t00056 {
|
namespace t00056 {
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
concept greater_than = sizeof(T) > sizeof(L);
|
||||||
|
|
||||||
|
template <typename T, typename P>
|
||||||
|
concept greater_than_requires = requires(T l, P r)
|
||||||
|
{
|
||||||
|
sizeof(l) > sizeof(r);
|
||||||
|
};
|
||||||
|
|
||||||
// Constraint expression
|
// Constraint expression
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept MaxFourBytes = sizeof(T) <= 4;
|
concept max_four_bytes = sizeof(T) <= 4;
|
||||||
|
|
||||||
// Simple requirement
|
// Simple requirement
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept Iterable = requires(T container) {
|
concept iterable = requires(T container)
|
||||||
container.begin();
|
{
|
||||||
container.end();
|
container.begin();
|
||||||
};
|
container.end();
|
||||||
|
};
|
||||||
|
|
||||||
// Type requirement
|
// Type requirement
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept HasValueType = requires { typename T::value_type; };
|
concept has_value_type = requires
|
||||||
|
{
|
||||||
|
typename T::value_type;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept ConvertibleToString = requires(T s) { std::string{s}; };
|
concept convertible_to_string = requires(T s)
|
||||||
|
{
|
||||||
|
std::string{s};
|
||||||
|
};
|
||||||
|
|
||||||
// Compound requirement
|
// Compound requirement
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
// Combined concept
|
// Combined concept
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept IterableWithValueType = Iterable<T> && HasValueType<T>;
|
concept iterable_with_value_type = iterable<T> && has_value_type<T>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept IterableWithSmallValueType =
|
concept iterable_or_small_value_type =
|
||||||
IterableWithValueType<T> && MaxFourBytes<T>;
|
iterable_with_value_type<T> || max_four_bytes<T>;
|
||||||
|
|
||||||
// Simple type constraint
|
// Simple type constraint
|
||||||
template <MaxFourBytes T> struct A {
|
template <max_four_bytes T> struct A {
|
||||||
T a;
|
T a;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Requires constant expression
|
// Requires constant expression
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires MaxFourBytes<T>
|
requires iterable_or_small_value_type<T>
|
||||||
struct B {
|
struct B {
|
||||||
T b;
|
T b;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Anonymous concept requirement
|
// Anonymous concept requirement
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires requires(T t) {
|
requires requires(T t)
|
||||||
--t;
|
{
|
||||||
t--;
|
--t;
|
||||||
}
|
t--;
|
||||||
|
}
|
||||||
struct C {
|
struct C {
|
||||||
T c;
|
T c;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user