diff --git a/README.md b/README.md index 3c85a81b..8708f725 100644 --- a/README.md +++ b/README.md @@ -146,32 +146,65 @@ and checkout the SVG diagrams in `docs/diagrams` folder. Source code: ```cpp -#include -#include - -namespace clanguml { -namespace t00009 { - -template class A { -public: - T value; +template struct A { + T t; + P p; }; -class B { -public: - A aint; - A *astring; - A> &avector; +struct B { + std::string value; +}; + +template using AString = A; +template using AStringPtr = A>; + +template +using PairPairBA = std::pair>, long>; + +template using VectorPtr = std::unique_ptr>; +template using APtr = std::unique_ptr>; +template using ASharedPtr = std::shared_ptr>; +template +using AAPtr = std::unique_ptr, A>>; + +template using GeneralCallback = std::function; +using VoidCallback = GeneralCallback<>; + +using BVector = std::vector; +using BVector2 = BVector; + +using AIntString = AString; +using AStringString = AString; +using BStringString = AStringString; + +class R { + PairPairBA bapair; + + APtr abool; + AAPtr aboolfloat; + ASharedPtr afloat; + A boolstring; + AStringPtr floatstring; + AIntString intstring; + AStringString stringstring; + BStringString bstringstring; + +protected: + BVector bs; + +public: + BVector2 bs2; + GeneralCallback cb; + VoidCallback vcb; + VectorPtr vps; }; -} -} ``` generates the following diagram (via PlantUML): -![class_diagram_example](docs/test_cases/t00009_class.svg) +![class_diagram_example](docs/test_cases/t00014_class.svg) -> Open the raw image [here](https://raw.githubusercontent.com/bkryza/clang-uml/master/docs/test_cases/t00009_class.svg), +> Open the raw image [here](https://raw.githubusercontent.com/bkryza/clang-uml/master/docs/test_cases/t00014_class.svg), > and checkout the hover tooltips and hyperlinks to classes and methods. ### Sequence diagrams diff --git a/docs/configuration_file.md b/docs/configuration_file.md index 2ce9ee6e..9d216993 100644 --- a/docs/configuration_file.md +++ b/docs/configuration_file.md @@ -101,6 +101,17 @@ diagrams: ClassA: - up: ClassB - left: ClassC + # Specify customized relationship hints for types which are + # arguments in template instantiations + relationship_hints: + # All tuple arguments should be treated as aggregation + std::tuple: aggregation + # All some_template arguments should be treated as associations + # except for arguments with indexes 2 and 10 + ns1::n2::some_template: + default: association + 2: composition + 10: aggregation # Entities from this namespace will be shortened # (can only contain one element at the moment) using_namespace: diff --git a/docs/test_cases/t00002_class.svg b/docs/test_cases/t00002_class.svg index 215d62f1..f63fe482 100644 --- a/docs/test_cases/t00002_class.svg +++ b/docs/test_cases/t00002_class.svg @@ -1,6 +1,6 @@ - + @@ -9,123 +9,123 @@ - - + + A - + - + foo_a() = 0 : void - + - + foo_c() = 0 : void - - + + B - + - + foo_a() : void - - + + C - + - + foo_c() : void - - + + D - + - + as : std::vector<A*> - + - + foo_a() : void - + - + foo_c() : void - - + + E - + - + as : std::vector<A*> - + - + foo_a() : void - + - + foo_c() : void - + This is class A - + This is class B - + This is class D which is a little like B @@ -142,13 +142,13 @@ - + as - + - + diff --git a/docs/test_cases/t00003_class.svg b/docs/test_cases/t00003_class.svg index c9ac2a4c..f081ca90 100644 --- a/docs/test_cases/t00003_class.svg +++ b/docs/test_cases/t00003_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,195 +9,188 @@ - - - - - A - + + + + + A + - + - + public_member : int - + - + static_int : int - + - + static_const_int : int const - + - + auto_member : unsigned long const - + - + protected_member : int - + - + private_member : int - + - + a : int - + - + b : int - + - + c : int - - + + - + A() : void - + - + A(int i) : void - + - + A(A&& ) : void - + - + A(A const& ) : void - + - + ~A() : void - + - + basic_method() : void - + - + static_method() : int - + - + const_method() const : void - + - + auto_method() : int - + - + double_int(int const i) : int - + - + sum(double const a, double const b) : double - + - + default_int(int i = 12) : int - + - - default_string(int i, std::string s = "abc") : std::string + + create_from_int(int i) : A - + - + - - create_from_int(int i) : A + + protected_method() : void - + - + - - protected_method() : void + + private_method() : void - + - + - - private_method() : void - - - - - - - compare : std::function<bool(int const)> + + compare : std::function<bool(int const)> diff --git a/docs/test_cases/t00004_class.svg b/docs/test_cases/t00004_class.svg index 22b87327..0be9ec6d 100644 --- a/docs/test_cases/t00004_class.svg +++ b/docs/test_cases/t00004_class.svg @@ -1,6 +1,6 @@ - + @@ -9,38 +9,38 @@ - - + + A - + - + foo() const : void - + - + foo2() const : void - - + + AA - - + + Lights @@ -50,23 +50,23 @@ Red - - + + AAA - + - + - + diff --git a/docs/test_cases/t00005_class.svg b/docs/test_cases/t00005_class.svg index e2d23182..7a144b96 100644 --- a/docs/test_cases/t00005_class.svg +++ b/docs/test_cases/t00005_class.svg @@ -1,6 +1,6 @@ - + @@ -9,238 +9,238 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + R - + - + some_int : int - + - + some_int_pointer : int* - + - + some_int_pointer_pointer : int** - + - + some_int_reference : int& - + - + a : A - + - + b : B* - + - + c : C& - + - + d : D const* - + - + e : E const& - + - + f : F&& - + - + g : G** - + - + h : H*** - + - + i : I*& - + - + j : J volatile* - + - + k : K* - + +a - + +b - + +c - + +d - + +e - + +f - + +g - + +h - + +i - + +j - + +k diff --git a/docs/test_cases/t00006_class.svg b/docs/test_cases/t00006_class.svg index 1e3905dd..8533aef6 100644 --- a/docs/test_cases/t00006_class.svg +++ b/docs/test_cases/t00006_class.svg @@ -1,6 +1,6 @@ - + @@ -9,136 +9,136 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + L - - + + M - - + + N - - + + NN - - + + NNN - - + + custom_container @@ -146,15 +146,15 @@ T - + - + data : std::vector<T> - + custom_container @@ -162,159 +162,159 @@ E - - + + R - + - + a : std::vector<A> - + - + b : std::vector<B*> - + - + c : std::map<int,C> - + - + d : std::map<int,D*> - + - + e : custom_container<E> - + - + f : std::vector<std::vector<F>> - + - + g : std::map<int,std::vector<G*>> - + - + h : std::array<H,10> - + - + i : std::array<I*,5> - + - + j : J[10] - + - + k : K*[20] - + - + lm : std::vector<std::pair<L,M>> - + - + ns : std::tuple<N,NN,NNN> - + - + - + +a - + +b - + +c - + +d - + +e - + +f - + +g - + +h - + +i - + +j - + +k - + lm - + lm - + ns - + ns - + ns diff --git a/docs/test_cases/t00007_class.svg b/docs/test_cases/t00007_class.svg index a9dafb22..c16df699 100644 --- a/docs/test_cases/t00007_class.svg +++ b/docs/test_cases/t00007_class.svg @@ -1,6 +1,6 @@ - + @@ -9,66 +9,66 @@ - - + + A - - + + B - - + + C - - + + R - + - + a : std::unique_ptr<A> - + - + b : std::shared_ptr<B> - + - + c : std::weak_ptr<C> - + +a - + +b - + +c diff --git a/docs/test_cases/t00008_class.svg b/docs/test_cases/t00008_class.svg index f3de2788..ca0f90f6 100644 --- a/docs/test_cases/t00008_class.svg +++ b/docs/test_cases/t00008_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,51 +18,51 @@ T,P,CMP,int N - + - + value : T - + - + pointer : T* - + - + reference : T& - + - + values : std::vector<P> - + - + ints : std::array<int,N> - + - + comparator : CMP - - + + Vector @@ -70,16 +70,16 @@ T - + - + values : std::vector<T> - - + + B @@ -87,15 +87,15 @@ T,C<> - + - + template_template : C<T> - + B @@ -103,38 +103,38 @@ int,Vector - - + + D - + - + ints : B<int,Vector> - + - + D(std::tuple<Items...>* ) : void - + - + add(int i) : void - + - + ints diff --git a/docs/test_cases/t00009_class.svg b/docs/test_cases/t00009_class.svg index a061ff7d..718a3f69 100644 --- a/docs/test_cases/t00009_class.svg +++ b/docs/test_cases/t00009_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,15 +18,15 @@ T - + - + value : T - + A @@ -34,7 +34,7 @@ int - + A @@ -42,7 +42,7 @@ std::string - + A @@ -50,50 +50,50 @@ std::vector<std::string> - - + + B - + - + aint : A<int> - + - + astring : A<std::string>* - + - + avector : A<std::vector<std::string>>& - + - + - + - + aint - + astring - + avector diff --git a/docs/test_cases/t00010_class.svg b/docs/test_cases/t00010_class.svg index ca06e6e0..f9f273e7 100644 --- a/docs/test_cases/t00010_class.svg +++ b/docs/test_cases/t00010_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,22 +18,22 @@ T,P - + - + first : T - + - + second : P - + A @@ -41,8 +41,8 @@ T,std::string - - + + B @@ -50,15 +50,15 @@ T - + - + astring : A<T,std::string> - + B @@ -66,30 +66,30 @@ int - - + + C - + - + aintstring : B<int> - + - + astring - + - + aintstring diff --git a/docs/test_cases/t00011_class.svg b/docs/test_cases/t00011_class.svg index 67b545d2..50d8f399 100644 --- a/docs/test_cases/t00011_class.svg +++ b/docs/test_cases/t00011_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + D @@ -18,56 +18,56 @@ T - + - + value : T - - + + A - + - + foo() : void - - + + B - + - + m_a : A* - + - + foo() : void - + «friend» - + m_a diff --git a/docs/test_cases/t00012_class.svg b/docs/test_cases/t00012_class.svg index f428e13c..9a232fb7 100644 --- a/docs/test_cases/t00012_class.svg +++ b/docs/test_cases/t00012_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,23 +18,23 @@ T,Ts... - + - + value : T - + - + values : int - - + + B @@ -43,15 +43,15 @@ - + - + ints : std::array<int,sizeof...(Is)> - - + + C @@ -60,14 +60,14 @@ - + - + ints : std::array<T,sizeof...(Is)> - + A @@ -75,7 +75,7 @@ int,std::string,float - + A @@ -83,7 +83,7 @@ int,std::string,bool - + B @@ -91,7 +91,7 @@ 3,2,1 - + B @@ -99,7 +99,7 @@ 1,1,1,1 - + C @@ -107,79 +107,79 @@ std::map<int,std::vector<std::vector<std::vector<std::string>>>>,3,3,3 - - + + R - + - + a1 : A<int,std::string,float> - + - + a2 : A<int,std::string,bool> - + - + b1 : B<3,2,1> - + - + b2 : B<1,1,1,1> - + - + c1 : C<std::map<int,std::vector<std::vector<std::vector<std::string>>>>,3,3,3> - + Long template annotation - + - + - + - + - + - + a1 - + a2 - + b1 - + b2 - + c1 diff --git a/docs/test_cases/t00013.md b/docs/test_cases/t00013.md index bac2230a..b6da68aa 100644 --- a/docs/test_cases/t00013.md +++ b/docs/test_cases/t00013.md @@ -55,6 +55,11 @@ template struct E { T e; }; +template struct G { + T g; + std::tuple args; +}; + using namespace ABCD; class R { public: @@ -73,6 +78,8 @@ public: template T get_f(const F &f) { return f.f; } int get_int_f(const F &f) { return f.f; } + G gintstring; + private: mutable E estring; }; diff --git a/docs/test_cases/t00013_class.svg b/docs/test_cases/t00013_class.svg index 2c5437b5..8f24c3e3 100644 --- a/docs/test_cases/t00013_class.svg +++ b/docs/test_cases/t00013_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,250 +9,295 @@ - - - - - ABCD::F - - T - + + + + + ABCD::F + + T + - - - + + + - - f : T + + f : T - - - - - - A - + + + + + + A + - - - + + + - - a : int + + a : int - - - - - - B - + + + + + + B + - - - + + + - - b : int + + b : int - - - - - - C - + + + + + + C + - - - + + + - - c : int + + c : int - - - - - - D - + + + + + + D + - - - + + + - - d : int + + d : int - - - - + + + + - - print(R* r) : void + + print(R* r) : void - - - - - E - - T - + + + + + E + + T + - - - + + + - - e : T + + e : T - - - - - E - - int - - - - - - F - - int - - - - - - E - - std::string - - - - - - - R - + + + + + + G + + T,Args... + - - - + + + - - estring : E<std::string> + + g : T - - - - + + + - - get_a(A* a) : int + + args : std::tuple<Args...> - - - + + + + + E + + int + + + + + + F + + int + + + + + + G + + int,float,std::string + + + + + + E + + std::string + + + + + + + R + - - get_b(B& b) : int + + + - - - + + gintstring : G<int,float,std::string> - - get_const_b(B const& b) : int + + + - - - + + estring : E<std::string> - - get_c(C c) : int + + + + - - - + + get_a(A* a) : int - - get_d(D&& d) : int + + + - - - + + get_b(B& b) : int - - get_d2(D&& d) : int + + + - - - + + get_const_b(B const& b) : int - - get_e(E<T> e) : T + + + - - - + + get_c(C c) : int - - get_int_e(E<int> const& e) : int + + + - - - + + get_d(D&& d) : int - - get_int_e2(E<int>& e) : int + + + - - - + + get_d2(D&& d) : int - - get_f(F<T> const& f) : T + + + - - - + + get_e(E<T> e) : T - - get_int_f(F<int> const& f) : int + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - estring + + get_int_e(E<int> const& e) : int + + + + + + + get_int_e2(E<int>& e) : int + + + + + + + get_f(F<T> const& f) : T + + + + + + + get_int_f(F<int> const& f) : int + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gintstring + + + + estring diff --git a/docs/test_cases/t00014.md b/docs/test_cases/t00014.md index 4fb6d9be..6b2a366b 100644 --- a/docs/test_cases/t00014.md +++ b/docs/test_cases/t00014.md @@ -22,13 +22,10 @@ diagrams: ## Source code File t00014.cc ```cpp -#include #include -#include #include -#include +#include #include -#include #include #include @@ -50,15 +47,25 @@ template struct A { P p; }; -template using AString = A; - -template using GeneralCallback = std::function; -using VoidCallback = GeneralCallback<>; - struct B { std::string value; }; +template using AString = A; +template using AStringPtr = A>; + +template +using PairPairBA = std::pair>, long>; + +template using VectorPtr = std::unique_ptr>; +template using APtr = std::unique_ptr>; +template using ASharedPtr = std::shared_ptr>; +template +using AAPtr = std::unique_ptr, A>>; + +template using GeneralCallback = std::function; +using VoidCallback = GeneralCallback<>; + using BVector = std::vector; using BVector2 = BVector; @@ -67,11 +74,16 @@ using AStringString = AString; using BStringString = AStringString; class R { - // clang-uml: tinst A + PairPairBA bapair; + + APtr abool; + AAPtr aboolfloat; + ASharedPtr afloat; A boolstring; - AString floatstring; + AStringPtr floatstring; AIntString intstring; AStringString stringstring; + BStringString bstringstring; protected: BVector bs; @@ -80,6 +92,7 @@ public: BVector2 bs2; GeneralCallback cb; VoidCallback vcb; + VectorPtr vps; }; } } diff --git a/docs/test_cases/t00014_class.svg b/docs/test_cases/t00014_class.svg index 707b49f7..97e00b0d 100644 --- a/docs/test_cases/t00014_class.svg +++ b/docs/test_cases/t00014_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,204 +9,237 @@ - - - - - A - - T,P - + + + + + A + + T,P + - - - + + + - - t : T + + t : T - - - + + + - - p : P + + p : P - - - - - A - - T,std::string - - - - - - - B - + + + + + + B + + - - - + + + + A + + T,std::string + + + + + + A + + T,std::unique_ptr<std::string> + + + + + + A + + long,T + + + + + + A + + double,T + + + + + + A + + long,bool + + + + + + A + + double,bool + + + + + + A + + long,float + + + + + + A + + double,float + + + + + + A + + bool,std::string + + + + + + A + + float,std::unique_ptr<std::string> + + + + + + + R + - - value : std::string + + + - - - - - A - - bool,std::string - - - - - - AString - - float - - - - - - AString - - int - - - - - - AString - - std::string - - - - - - GeneralCallback - - AIntString - - - - - - GeneralCallback - - - - - - - R - + + bapair : PairPairBA<bool> - - - + + + - - boolstring : A<bool,std::string> + + abool : APtr<bool> - - - + + + - - floatstring : AString<float> + + aboolfloat : AAPtr<bool,float> - - - + + + - - intstring : AIntString + + afloat : ASharedPtr<float> - - - + + + - - stringstring : AStringString + + boolstring : A<bool,std::string> - - - + + + - - bs : BVector + + floatstring : AStringPtr<float> - - - + + + - - bs2 : BVector2 + + cb : GeneralCallback<AIntString> - - - + + + - - cb : GeneralCallback<AIntString> + + vps : VectorPtr<B> - - - - - - vcb : VoidCallback - - - - - - - - - - - - - - - - boolstring - - - - floatstring - - - - intstring - - - - stringstring - - - - bs - - - - bs2 - - - - cb - - - - vcb + + + + + + + + + + + + + + + + + + + + + + + + + bapair + + + + vps + + + + bapair + + + + abool + + + + aboolfloat + + + + aboolfloat + + + + afloat + + + + boolstring + + + + floatstring diff --git a/docs/test_cases/t00015_class.svg b/docs/test_cases/t00015_class.svg index 443cfe93..63f2416c 100644 --- a/docs/test_cases/t00015_class.svg +++ b/docs/test_cases/t00015_class.svg @@ -1,6 +1,6 @@ - + @@ -9,51 +9,51 @@ - - + + ns1::A - - + + ns1::ns2_v0_9_0::A - - + + ns1::Anon - - + + ns3::ns1::ns2::Anon - - + + ns3::B - + - + - + diff --git a/docs/test_cases/t00016_class.svg b/docs/test_cases/t00016_class.svg index 5c2bad26..0f880409 100644 --- a/docs/test_cases/t00016_class.svg +++ b/docs/test_cases/t00016_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + is_numeric<> @@ -19,8 +19,8 @@ value : enum - - + + is_numeric @@ -31,8 +31,8 @@ value : enum - - + + is_numeric @@ -43,8 +43,8 @@ value : enum - - + + is_numeric @@ -55,8 +55,8 @@ value : enum - - + + is_numeric @@ -67,13 +67,13 @@ value : enum - + - + - + - + diff --git a/docs/test_cases/t00017_class.svg b/docs/test_cases/t00017_class.svg index 7655f36a..6374f922 100644 --- a/docs/test_cases/t00017_class.svg +++ b/docs/test_cases/t00017_class.svg @@ -1,6 +1,6 @@ - + @@ -9,176 +9,176 @@ - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - - + + G - - + + H - - + + I - - + + J - - + + K - - + + R - + - + some_int : int - + - + some_int_pointer : int* - + - + some_int_pointer_pointer : int** - + - + some_int_reference : int& - + - + R(int& some_int, C& cc, E const& ee, F&& ff, I*& ii) : void - + - + -c - + - + -e - + - + -f - + - + -i - + -a - + -b - + -d - + -g - + -h - + -j - + -k diff --git a/docs/test_cases/t00018_class.svg b/docs/test_cases/t00018_class.svg index 195b0c04..d688393b 100644 --- a/docs/test_cases/t00018_class.svg +++ b/docs/test_cases/t00018_class.svg @@ -1,6 +1,6 @@ - + @@ -9,123 +9,123 @@ - - + + impl::widget - + - + n : int - + - + draw(widget const& w) const : void - + - + draw(widget const& w) : void - + - + widget(int n) : void - - + + widget - + - + pImpl : std::unique_ptr<impl::widget> - + - + draw() const : void - + - + draw() : void - + - + shown() const : bool - + - + widget(int ) : void - + - + ~widget() : void - + - + widget(widget&& ) : void - + - + widget(widget const& ) : void - + - + operator=(widget&& ) : widget& - + - + operator=(widget const& ) : widget& - + - + pImpl diff --git a/docs/test_cases/t00019_class.svg b/docs/test_cases/t00019_class.svg index fc950051..22ddaafe 100644 --- a/docs/test_cases/t00019_class.svg +++ b/docs/test_cases/t00019_class.svg @@ -1,6 +1,6 @@ - + - + @@ -9,195 +9,174 @@ - - - - - Layer2 - - LowerLayer - - + + + + + Layer2 + + LowerLayer + + - - - + + + - - all_calls_count() const : int + + all_calls_count() const : int - - - - - Base - - + + + + + Base + + - + - + Base() : void - + - + ~Base() : void - + - + m1() : int - - - + + + + + Layer1 + + LowerLayer + + - - m2() : std::string + + + - - - - - Layer1 - - LowerLayer - - + + m1() : int - - - + + + + + Layer3 + + LowerLayer + - - m1() : int + + + - - - + + m_m1_calls : int - - m2() : std::string + + + - - - - - Layer3 - - LowerLayer - + + m_m2_calls : int - - - + + + + - - m_m1_calls : int + + m1() : int - - - + + + - - m_m2_calls : int + + m1_calls() const : int - - - - + + + - - m1() : int + + m2_calls() const : int - - - + + + + Layer3 + + Base + + + + + + Layer2 + + Layer3<Base> + + + + + + Layer1 + + Layer2<Layer3<Base>> + + + + + + + A + - - m2() : std::string + + + - - - + + layers : std::unique_ptr<Layer1<Layer2<Layer3<Base>>>> - - m1_calls() const : int - - - - - - - m2_calls() const : int - - - - - Layer3 - - Base - - - - - - Layer2 - - Layer3<Base> - - - - - - Layer1 - - Layer2<Layer3<Base>> - - - - - - - A - - - - - - - - layers : std::unique_ptr<Layer1<Layer2<Layer3<Base>>>> - - - - - - - - - - - - - - - - - - - - - - - - layers + + + + + + + + + + + + + + + + + + + + + + + layers diff --git a/docs/test_cases/t00020_class.svg b/docs/test_cases/t00020_class.svg index 69db877a..6e184e84 100644 --- a/docs/test_cases/t00020_class.svg +++ b/docs/test_cases/t00020_class.svg @@ -1,6 +1,6 @@ - + @@ -9,195 +9,195 @@ - - + + ProductA - + - + ~ProductA() : void - + - + sell(int price) const = 0 : bool - - + + ProductA1 - + - + sell(int price) const : bool - - + + ProductA2 - + - + sell(int price) const : bool - - + + ProductB - + - + ~ProductB() : void - + - + buy(int price) const = 0 : bool - - + + ProductB1 - + - + buy(int price) const : bool - - + + ProductB2 - + - + buy(int price) const : bool - - + + AbstractFactory - + - + make_a() const = 0 : std::unique_ptr<ProductA> - + - + make_b() const = 0 : std::unique_ptr<ProductB> - - + + Factory1 - + - + make_a() const : std::unique_ptr<ProductA> - + - + make_b() const : std::unique_ptr<ProductB> - - + + Factory2 - + - + make_a() const : std::unique_ptr<ProductA> - + - + make_b() const : std::unique_ptr<ProductB> - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t00021_class.svg b/docs/test_cases/t00021_class.svg index cf41db7b..91c57152 100644 --- a/docs/test_cases/t00021_class.svg +++ b/docs/test_cases/t00021_class.svg @@ -1,6 +1,6 @@ - + @@ -9,184 +9,184 @@ - - + + Visitor - + - + ~Visitor() : void - + - + visit_A(A const& item) const = 0 : void - + - + visit_B(B const& item) const = 0 : void - - + + Visitor1 - + - + visit_A(A const& item) const : void - + - + visit_B(B const& item) const : void - - + + Visitor2 - + - + visit_A(A const& item) const : void - + - + visit_B(B const& item) const : void - - + + Visitor3 - + - + visit_A(A const& item) const : void - + - + visit_B(B const& item) const : void - - + + Item - + - + ~Item() : void - + - + accept(Visitor const& visitor) const = 0 : void - - + + A - + - + accept(Visitor const& visitor) const : void - - + + B - + - + accept(Visitor const& visitor) const : void - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t00022_class.svg b/docs/test_cases/t00022_class.svg index 086323f2..c7d8e8d2 100644 --- a/docs/test_cases/t00022_class.svg +++ b/docs/test_cases/t00022_class.svg @@ -1,6 +1,6 @@ - + @@ -9,82 +9,82 @@ - - + + A - + - + template_method() : void - + - + method1() = 0 : void - + - + method2() = 0 : void - - + + A1 - + - + method1() : void - + - + method2() : void - - + + A2 - + - + method1() : void - + - + method2() : void - + - + diff --git a/docs/test_cases/t00023_class.svg b/docs/test_cases/t00023_class.svg index bca7f3a9..3f18c1fa 100644 --- a/docs/test_cases/t00023_class.svg +++ b/docs/test_cases/t00023_class.svg @@ -1,6 +1,6 @@ - + @@ -9,111 +9,111 @@ - - + + Strategy - + - + ~Strategy() : void - + - + algorithm() = 0 : void - - + + StrategyA - + - + algorithm() : void - - + + StrategyB - + - + algorithm() : void - - + + StrategyC - + - + algorithm() : void - - + + Context - + - + m_strategy : std::unique_ptr<Strategy> - + - + Context(std::unique_ptr<Strategy> strategy) : void - + - + apply() : void - + - + - + - + - + m_strategy diff --git a/docs/test_cases/t00024_class.svg b/docs/test_cases/t00024_class.svg index 471a9d40..733347eb 100644 --- a/docs/test_cases/t00024_class.svg +++ b/docs/test_cases/t00024_class.svg @@ -1,6 +1,6 @@ - + @@ -9,126 +9,126 @@ - - + + Target - + - + ~Target() : void - + - + m1() = 0 : void - + - + m2() = 0 : void - - + + Target1 - + - + m1() : void - + - + m2() : void - - + + Target2 - + - + m1() : void - + - + m2() : void - - + + Proxy - + - + m_target : std::shared_ptr<Target> - + - + Proxy(std::shared_ptr<Target> target) : void - + - + m1() : void - + - + m2() : void - + - + - + - + m_target - + diff --git a/docs/test_cases/t00025_class.svg b/docs/test_cases/t00025_class.svg index c2de75c2..e757c68b 100644 --- a/docs/test_cases/t00025_class.svg +++ b/docs/test_cases/t00025_class.svg @@ -1,6 +1,6 @@ - + @@ -9,52 +9,52 @@ - - + + Target1 - + - + m1() : void - + - + m2() : void - - + + Target2 - + - + m1() : void - + - + m2() : void - - + + Proxy @@ -62,36 +62,36 @@ T - + - + m_target : std::shared_ptr<T> - + - + Proxy(std::shared_ptr<T> target) : void - + - + m1() : void - + - + m2() : void - + Proxy @@ -99,7 +99,7 @@ Target1 - + Proxy @@ -107,41 +107,41 @@ Target2 - - + + ProxyHolder - + - + proxy1 : Proxy<Target1> - + - + proxy2 : Proxy<Target2> - + - + - + - + - + proxy1 - + proxy2 diff --git a/docs/test_cases/t00026_class.svg b/docs/test_cases/t00026_class.svg index f9133186..ce37b633 100644 --- a/docs/test_cases/t00026_class.svg +++ b/docs/test_cases/t00026_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + Memento @@ -18,30 +18,30 @@ T - + - + m_value : T - + - + Memento(T&& v) : void - + - + value() const : T - - + + Originator @@ -49,51 +49,51 @@ T - + - + m_value : T - + - + Originator(T&& v) : void - + - + memoize_value() const : Memento<T> - + - + load(Memento<T> const& m) : void - + - + print() const : void - + - + set(T&& v) : void - - + + Caretaker @@ -101,29 +101,29 @@ T - + - + m_mementos : std::unordered_map<std::string,Memento<T>> - + - + state(std::string const& n) : Memento<T>& - + - + set_state(std::string const& s, Memento<T>&& m) : void - + Caretaker @@ -131,7 +131,7 @@ std::string - + Originator @@ -139,45 +139,45 @@ std::string - - + + StringMemento - + - + caretaker : Caretaker<std::string> - + - + originator : Originator<std::string> - + - + - + m_mementos - + - + - + caretaker - + originator diff --git a/docs/test_cases/t00027_class.svg b/docs/test_cases/t00027_class.svg index fd2ad0eb..e731c764 100644 --- a/docs/test_cases/t00027_class.svg +++ b/docs/test_cases/t00027_class.svg @@ -1,6 +1,6 @@ - + @@ -9,79 +9,79 @@ - - + + Shape - + - + display() = 0 : void - + - + ~Shape() : void - - - - - Line - - T<> + + + + + Line + + T<>... - + - + display() : void - - - - - Text - - T<> + + + + + Text + + T<>... - + - + display() : void - - + + ShapeDecorator - + - + display() = 0 : void - - + + Color @@ -90,15 +90,15 @@ - + - + display() : void - - + + Weight @@ -107,14 +107,14 @@ - + - + display() : void - + Line @@ -122,7 +122,7 @@ Color,Weight - + Line @@ -130,7 +130,7 @@ Color - + Text @@ -138,7 +138,7 @@ Color,Weight - + Text @@ -146,71 +146,71 @@ Color - - + + Window - + - + border : Line<Color,Weight> - + - + divider : Line<Color> - + - + title : Text<Color,Weight> - + - + description : Text<Color> - + - + - + - + - + - + - + - + - + border - + divider - + title - + description diff --git a/docs/test_cases/t00028_class.svg b/docs/test_cases/t00028_class.svg index 9048affb..cf74c801 100644 --- a/docs/test_cases/t00028_class.svg +++ b/docs/test_cases/t00028_class.svg @@ -1,6 +1,6 @@ - + @@ -9,68 +9,68 @@ - - + + A - + A class note. - + A class note. - - + + B - + B class note. - + B class note. - - + + C - + C class note. - + C class note. - - + + D - + D class note. - + D class note. - - + + E @@ -78,27 +78,27 @@ T - + - + param : T - + E template class note. - - + + G - - + + F @@ -108,13 +108,13 @@ three - + F enum note. - + F enum note. - + E @@ -122,97 +122,97 @@ int - - + + R - + - + aaa : A - + - + bbb : B* - + - + ccc : C& - + - + ddd : std::vector<std::shared_ptr<D>> - + - + eee : E<int> - + - + ggg : G** - + - + R(C& c) : void - + R class note. - + R class note. - - - - + + + + - + - + ccc - + aaa - + bbb - - + + ddd - + eee - + ggg diff --git a/docs/test_cases/t00029_class.svg b/docs/test_cases/t00029_class.svg index 2aa1ff03..1ec81141 100644 --- a/docs/test_cases/t00029_class.svg +++ b/docs/test_cases/t00029_class.svg @@ -1,6 +1,6 @@ - + @@ -9,16 +9,16 @@ - - + + A - - + + C @@ -26,16 +26,16 @@ T - + - + param : T - - + + E @@ -45,72 +45,72 @@ three - - + + G1 - - + + G2 - - + + G3 - - + + G4 - - + + R - + - + g1 : G1 - + - + g3 : G3& - + - + g4 : std::shared_ptr<G4> - + g1 - + g4 diff --git a/docs/test_cases/t00030_class.svg b/docs/test_cases/t00030_class.svg index d60e7ed1..682b0409 100644 --- a/docs/test_cases/t00030_class.svg +++ b/docs/test_cases/t00030_class.svg @@ -1,6 +1,6 @@ - + @@ -9,91 +9,91 @@ - - + + A - - + + B - - + + C - - + + D - - + + R - + - + aaa : A - + - + bbb : std::vector<B> - + - + ccc : std::vector<C> - + - + ddd : D - + aaa - + bbb 0..1 1..* - + ccc 0..1 1..5 - + ddd diff --git a/docs/test_cases/t00031_class.svg b/docs/test_cases/t00031_class.svg index 236a19c2..02833f40 100644 --- a/docs/test_cases/t00031_class.svg +++ b/docs/test_cases/t00031_class.svg @@ -1,33 +1,33 @@ - + - + - + - - - + + + A - - + + B @@ -37,8 +37,8 @@ three - - + + @@ -47,23 +47,23 @@ T - + - + ttt : T - - + + D - + C @@ -71,57 +71,57 @@ int - - + + R - + - + aaa : A* - + - + bbb : std::vector<B> - + - + ccc : C<int> - + - + ddd : D* - + - + aaa - + bbb - + ccc - + ddd diff --git a/docs/test_cases/t00032_class.svg b/docs/test_cases/t00032_class.svg index 8fe14280..9c53078c 100644 --- a/docs/test_cases/t00032_class.svg +++ b/docs/test_cases/t00032_class.svg @@ -1,6 +1,6 @@ - + @@ -9,69 +9,69 @@ - - + + Base - - + + TBase - - + + A - + - + operator()() : void - - + + B - + - + operator()() : void - - + + C - + - + operator()() : void - - + + Overload @@ -79,15 +79,15 @@ T,L,Ts... - + - + counter : L - + Overload @@ -95,42 +95,42 @@ TBase,int,A,B,C - - + + R - + - + overload : Overload<TBase,int,A,B,C> - + - + - + - + - + - + - + - + - + - + - + overload diff --git a/docs/test_cases/t00033_class.svg b/docs/test_cases/t00033_class.svg index 1eb69273..c41d34c6 100644 --- a/docs/test_cases/t00033_class.svg +++ b/docs/test_cases/t00033_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,16 +18,16 @@ T - + - + aaa : T - - + + B @@ -35,16 +35,16 @@ T - + - + bbb : T - - + + C @@ -52,30 +52,30 @@ T - + - + ccc : T - - + + D - + - + ddd : int - + C @@ -83,7 +83,7 @@ D - + B @@ -91,7 +91,7 @@ std::unique_ptr<C<D>> - + A @@ -99,34 +99,34 @@ B<std::unique_ptr<C<D>>> - - + + R - + - + abc : A<B<std::unique_ptr<C<D>>>> - + - + - + - + - + - + - + abc diff --git a/docs/test_cases/t00034_class.svg b/docs/test_cases/t00034_class.svg index 2e0bad96..a91d693b 100644 --- a/docs/test_cases/t00034_class.svg +++ b/docs/test_cases/t00034_class.svg @@ -1,6 +1,6 @@ - + @@ -9,30 +9,30 @@ - - + + Void - + - + operator==(Void const& ) const : bool - + - + operator!=(Void const& ) const : bool - - + + lift_void @@ -41,8 +41,8 @@ - - + + lift_void @@ -51,8 +51,8 @@ - - + + drop_void @@ -61,8 +61,8 @@ - - + + drop_void @@ -71,43 +71,43 @@ - - + + A - - + + R - + - + la : lift_void_t<A>* - + - + lv : lift_void_t<void>* - + - + - + - + la diff --git a/docs/test_cases/t00035_class.svg b/docs/test_cases/t00035_class.svg index 85165720..0292ca9f 100644 --- a/docs/test_cases/t00035_class.svg +++ b/docs/test_cases/t00035_class.svg @@ -1,6 +1,6 @@ - + @@ -9,40 +9,40 @@ - - + + Top - - + + Left - - + + Center - - + + Bottom - - + + Right diff --git a/docs/test_cases/t00036_class.svg b/docs/test_cases/t00036_class.svg index a98a51db..5ebb7364 100644 --- a/docs/test_cases/t00036_class.svg +++ b/docs/test_cases/t00036_class.svg @@ -1,6 +1,6 @@ - + @@ -9,23 +9,23 @@ - + ns1 - + ns11 - + ns111 - + ns2 - + ns22 - - + + E @@ -34,8 +34,8 @@ yellow - - + + A @@ -43,15 +43,15 @@ T - + - + a : T - + A @@ -59,32 +59,32 @@ int - - + + B - + - + a_int : A<int> - - + + C - + - + a_int diff --git a/docs/test_cases/t00037_class.svg b/docs/test_cases/t00037_class.svg index b43a6b40..c7f04e05 100644 --- a/docs/test_cases/t00037_class.svg +++ b/docs/test_cases/t00037_class.svg @@ -1,6 +1,6 @@ - + @@ -9,84 +9,84 @@ - - + + ST - + - + dimensions : «anonymous» - - + + <<anonymous>> - + - + t : double - + - + x : double - + - + y : double - + - + z : double - - + + A - + - + st : ST - + - + A() : void - + - + st diff --git a/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index 9b80a92b..c6b3e505 100644 --- a/docs/test_cases/t00038_class.svg +++ b/docs/test_cases/t00038_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + thirdparty::ns1::color_t @@ -20,16 +20,16 @@ blue - - + + thirdparty::ns1::E - - + + property_t @@ -39,47 +39,40 @@ property_c - - + + A - - + + B - - + + C - - - - - key_t - + + + + + key_t + + - - - - - - key : std::string - - - - + + map @@ -88,8 +81,8 @@ - - + + map @@ -98,8 +91,8 @@ - - + + map @@ -108,8 +101,8 @@ - - + + map @@ -118,8 +111,8 @@ - - + + map @@ -128,31 +121,31 @@ - + - + - + - + - + - + - + - + - + - - - + + + - + - + diff --git a/docs/test_cases/t00039_class.svg b/docs/test_cases/t00039_class.svg index eb8b5788..ecd56701 100644 --- a/docs/test_cases/t00039_class.svg +++ b/docs/test_cases/t00039_class.svg @@ -1,6 +1,6 @@ - + @@ -9,95 +9,95 @@ - - + + C - - + + D - - + + E - - + + CD - - + + DE - - + + CDE - - + + A - - + + AA - - + + AAA - + - + b : B* - - + + ns2::AAAA - - + + ns3::F @@ -105,16 +105,16 @@ T - + - + t : T* - - + + ns3::FF @@ -122,16 +122,16 @@ T,M - + - + m : M* - - + + ns3::FE @@ -139,16 +139,16 @@ T,M - + - + m : M* - - + + ns3::FFF @@ -156,39 +156,39 @@ T,M,N - + - + n : N* - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t00040_class.svg b/docs/test_cases/t00040_class.svg index 540c1433..354d12a3 100644 --- a/docs/test_cases/t00040_class.svg +++ b/docs/test_cases/t00040_class.svg @@ -1,6 +1,6 @@ - + @@ -9,76 +9,76 @@ - - + + A - + - + ii_ : int - + - + get_a() : int - - + + AA - - + + AAA - + - + b : B* - + - + get_aaa() : int - - + + R - + - + foo(A* a) : void - + - + diff --git a/docs/test_cases/t00041_class.svg b/docs/test_cases/t00041_class.svg index b9101d65..01dd0667 100644 --- a/docs/test_cases/t00041_class.svg +++ b/docs/test_cases/t00041_class.svg @@ -1,6 +1,6 @@ - + @@ -9,116 +9,116 @@ - - + + R - - + + D - + - + rr : RR* - - + + E - - + + F - - + + RR - + - + e : E* - + - + f : F* - - + + RRR - - + + ns1::N - - + + ns1::NN - - + + ns1::NM - + rr - + +e - + +f - + - + - + - + diff --git a/docs/test_cases/t00042_class.svg b/docs/test_cases/t00042_class.svg index ad1ae5f7..91cf9ad7 100644 --- a/docs/test_cases/t00042_class.svg +++ b/docs/test_cases/t00042_class.svg @@ -1,6 +1,6 @@ - + @@ -9,8 +9,8 @@ - - + + A @@ -18,16 +18,16 @@ T - + - + a : T - - + + B @@ -35,18 +35,18 @@ T,K - + - + b : T - + - + bb : K diff --git a/docs/test_cases/t00043_class.svg b/docs/test_cases/t00043_class.svg index 35edf881..50785359 100644 --- a/docs/test_cases/t00043_class.svg +++ b/docs/test_cases/t00043_class.svg @@ -1,6 +1,6 @@ - + @@ -9,189 +9,189 @@ - + dependants - + dependencies - - + + A - - + + B - + - + b(dependants::A* a) : void - - + + BB - + - + bb(dependants::A* a) : void - - + + C - + - + c(dependants::B* b) : void - - + + D - + - + d(dependants::C* c) : void - + - + dd(dependants::BB* bb) : void - - + + E - + - + e(dependants::D* d) : void - - + + G - - + + GG - - + + H - + - + h(dependencies::G* g) : void - + - + hh(dependencies::GG* gg) : void - - + + I - + - + i(dependencies::H* h) : void - - + + J - + - + i(dependencies::I* i) : void - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t30001_package.svg b/docs/test_cases/t30001_package.svg index 67e87435..1f5eea5f 100644 --- a/docs/test_cases/t30001_package.svg +++ b/docs/test_cases/t30001_package.svg @@ -1,6 +1,6 @@ - + @@ -9,67 +9,67 @@ - - + + A - - + + AA - - + + B - - + + AA - - + + AAA - - + + BBB - - + + BB - - + + AAA - - + + BBB - - + + BB - + A AAA note... - + This is namespace AA in namespace A - + This is namespace AA in namespace B - - - + + + diff --git a/docs/test_cases/t30002_package.svg b/docs/test_cases/t30002_package.svg index 62154bbc..bfae9442 100644 --- a/docs/test_cases/t30002_package.svg +++ b/docs/test_cases/t30002_package.svg @@ -1,6 +1,6 @@ - + @@ -9,135 +9,135 @@ - - + + A - - + + AA - - + + B - - + + BB - - + + A1 - - + + A2 - - + + A3 - - + + A4 - - + + A5 - - + + A6 - - + + A7 - - + + A8 - - + + A9 - - + + A10 - - + + A11 - - + + A12 - - + + A13 - - + + A14 - - + + A15 - - + + BBB - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t30003_package.svg b/docs/test_cases/t30003_package.svg index e7d1165d..13b1a2dc 100644 --- a/docs/test_cases/t30003_package.svg +++ b/docs/test_cases/t30003_package.svg @@ -1,6 +1,6 @@ - + @@ -9,39 +9,39 @@ - - + + ns1 - - + + ns3 «deprecated» - - + + ns1 - - + + ns2_v1_0_0 - - + + ns2_v0_9_0 «deprecated» - - + + ns2 - + diff --git a/docs/test_cases/t30004_package.svg b/docs/test_cases/t30004_package.svg index bc86fbe3..3b9914d5 100644 --- a/docs/test_cases/t30004_package.svg +++ b/docs/test_cases/t30004_package.svg @@ -1,6 +1,6 @@ - + @@ -9,50 +9,50 @@ - - + + A - + Package AAA. - + Package BBB. - + CCCC package note. - + Another CCC note. - + We skipped DDD. - - + + AAA - - + + BBB - - + + CCC - - + + EEE - - - - - + + + + + diff --git a/docs/test_cases/t30005_package.svg b/docs/test_cases/t30005_package.svg index a1133d0c..fd7a24b6 100644 --- a/docs/test_cases/t30005_package.svg +++ b/docs/test_cases/t30005_package.svg @@ -1,6 +1,6 @@ - + @@ -9,54 +9,54 @@ - - + + A - - + + AA - - + + B - - + + BB - - + + C - - + + CC - - + + AAA - - + + BBB - - + + CCC - + - + diff --git a/docs/test_cases/t30006_package.svg b/docs/test_cases/t30006_package.svg index 4a9906a1..e6e3c245 100644 --- a/docs/test_cases/t30006_package.svg +++ b/docs/test_cases/t30006_package.svg @@ -1,6 +1,6 @@ - + @@ -9,32 +9,32 @@ - - + + B - - + + A - - + + C - + Top A note. - + Bottom A note. - - - + + + - + diff --git a/docs/test_cases/t30007_package.svg b/docs/test_cases/t30007_package.svg index df7b60ac..1df74390 100644 --- a/docs/test_cases/t30007_package.svg +++ b/docs/test_cases/t30007_package.svg @@ -1,6 +1,6 @@ - + @@ -9,37 +9,37 @@ - - + + A - - + + B - - + + AA - - + + C - + Compare layout with t30006. - + Bottom A note. - - - + + + - + diff --git a/docs/test_cases/t30008_package.svg b/docs/test_cases/t30008_package.svg index 1d451b34..c91e6790 100644 --- a/docs/test_cases/t30008_package.svg +++ b/docs/test_cases/t30008_package.svg @@ -1,6 +1,6 @@ - + @@ -9,53 +9,53 @@ - - + + dependants - - + + dependencies - - + + A - - + + B - - + + C - - + + D - - + + E - - + + F - + - + - + - + diff --git a/docs/test_cases/t40001_include.svg b/docs/test_cases/t40001_include.svg index 217a9e1a..5e4bc0db 100644 --- a/docs/test_cases/t40001_include.svg +++ b/docs/test_cases/t40001_include.svg @@ -1,6 +1,6 @@ - + @@ -9,58 +9,58 @@ - + src - + include - + lib1 - - + + t40001.cc - - + + t40001_include1.h - - + + lib1.h - + string - + vector - + cppast/cpp_preprocessor.hpp - + This is a lib1 include dir - + This is a t40001_include1.h include file - + - + - + - + - + - + - - + + diff --git a/docs/test_cases/t40002_include.svg b/docs/test_cases/t40002_include.svg index 162bb854..24896067 100644 --- a/docs/test_cases/t40002_include.svg +++ b/docs/test_cases/t40002_include.svg @@ -1,6 +1,6 @@ - + @@ -9,58 +9,58 @@ - + src - + lib1 - + lib2 - + include - + lib1 - + lib2 - - + + t40002.cc - - + + lib1.cc - - + + lib2.cc - - + + lib1.h - - + + lib2.h - + - + - + - + - + diff --git a/docs/test_cases/t40003_include.svg b/docs/test_cases/t40003_include.svg index 991a1157..34531f84 100644 --- a/docs/test_cases/t40003_include.svg +++ b/docs/test_cases/t40003_include.svg @@ -1,6 +1,6 @@ - + @@ -9,84 +9,84 @@ - + include - + dependants - + dependencies - + src - + dependants - + dependencies - - + + t3.h - - + + t2.h - - + + t1.h - - + + t3.h - - + + t2.h - - + + t1.h - - + + t5.h - - + + t1.cc - - + + t2.cc - + - + - + - + - + - + - + - + diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index c1d068ca..c623359f 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -62,7 +62,7 @@ void class_::add_parent(class_parent &&parent) bases_.emplace_back(std::move(parent)); } -void class_::add_template(class_template tmplt) +void class_::add_template(template_parameter tmplt) { templates_.emplace_back(std::move(tmplt)); } @@ -73,7 +73,7 @@ const std::vector &class_::methods() const { return methods_; } const std::vector &class_::parents() const { return bases_; } -const std::vector &class_::templates() const +const std::vector &class_::templates() const { return templates_; } @@ -87,7 +87,8 @@ std::string class_::base_template() const { return base_template_full_name_; } bool operator==(const class_ &l, const class_ &r) { - return l.full_name() == r.full_name(); + return (l.name_and_ns() == r.name_and_ns()) && + (l.templates_ == r.templates_); } void class_::add_type_alias(type_alias &&ta) @@ -104,7 +105,7 @@ std::string class_::full_name_no_ns() const ostr << name(); - render_template_params(ostr); + render_template_params(ostr, false); return ostr.str(); } @@ -117,7 +118,7 @@ std::string class_::full_name(bool relative) const std::ostringstream ostr; ostr << name_and_ns(); - render_template_params(ostr); + render_template_params(ostr, relative); std::string res; @@ -133,18 +134,22 @@ std::string class_::full_name(bool relative) const } std::ostringstream &class_::render_template_params( - std::ostringstream &ostr) const + std::ostringstream &ostr, bool relative) const { using clanguml::common::model::namespace_; if (!templates_.empty()) { std::vector tnames; + std::vector tnames_simplified; + std::transform(templates_.cbegin(), templates_.cend(), - std::back_inserter(tnames), [this](const auto &tmplt) { - return tmplt.to_string(using_namespace()); - }); + std::back_inserter(tnames), + [ns = using_namespace(), relative]( + const auto &tmplt) { return tmplt.to_string(ns, relative); }); + ostr << fmt::format("<{}>", fmt::join(tnames, ",")); } + return ostr; } @@ -155,4 +160,36 @@ bool class_::is_abstract() const return std::any_of(methods_.begin(), methods_.end(), [](const auto &method) { return method.is_pure_virtual(); }); } + +int class_::calculate_template_specialization_match( + const class_ &other, const std::string &full_name) const +{ + int res{}; + + std::string left = name_and_ns(); + // TODO: handle variadic templates + if ((name_and_ns() != full_name) || + (templates().size() != other.templates().size())) { + return res; + } + + // Iterate over all template arguments + for (int i = 0; i < other.templates().size(); i++) { + const auto &template_arg = templates().at(i); + const auto &other_template_arg = other.templates().at(i); + + if (template_arg == other_template_arg) { + res++; + } + else if (other_template_arg.is_specialization_of(template_arg)) { + continue; + } + else { + res = 0; + break; + } + } + + return res; +} } diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index ff1f194e..757c5850 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -20,10 +20,10 @@ #include "class_member.h" #include "class_method.h" #include "class_parent.h" -#include "class_template.h" #include "common/model/element.h" #include "common/model/enums.h" #include "common/model/stylable_element.h" +#include "template_parameter.h" #include "type_alias.h" #include @@ -55,12 +55,12 @@ public: void add_member(class_member &&member); void add_method(class_method &&method); void add_parent(class_parent &&parent); - void add_template(class_template tmplt); + void add_template(template_parameter tmplt); const std::vector &members() const; const std::vector &methods() const; const std::vector &parents() const; - const std::vector &templates() const; + const std::vector &templates() const; void set_base_template(const std::string &full_name); std::string base_template() const; @@ -75,16 +75,29 @@ public: bool is_abstract() const; + bool is_alias() const { return is_alias_; } + + void is_alias(bool alias) { is_alias_ = alias; } + + void find_relationships( + std::vector> + &nested_relationships); + + int calculate_template_specialization_match( + const class_ &other, const std::string &full_name) const; + private: - std::ostringstream &render_template_params(std::ostringstream &ostr) const; + std::ostringstream &render_template_params( + std::ostringstream &ostr, bool relative) const; bool is_struct_{false}; bool is_template_{false}; bool is_template_instantiation_{false}; + bool is_alias_{false}; std::vector members_; std::vector methods_; std::vector bases_; - std::vector templates_; + std::vector templates_; std::string base_template_full_name_; std::map type_aliases_; diff --git a/src/class_diagram/model/class_template.cc b/src/class_diagram/model/class_template.cc deleted file mode 100644 index 589bdede..00000000 --- a/src/class_diagram/model/class_template.cc +++ /dev/null @@ -1,96 +0,0 @@ -/** - * src/class_diagram/model/class_template.cc - * - * Copyright (c) 2021-2022 Bartek Kryza - * - * 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 "class_template.h" -#include -#include - -namespace clanguml::class_diagram::model { - -class_template::class_template(const std::string &type, const std::string &name, - const std::string &default_value, bool is_variadic) - : type_{type} - , name_{name} - , default_value_{default_value} - , is_variadic_{is_variadic} -{ - if (is_variadic) - name_ = name_ + "..."; -} - -void class_template::set_type(const std::string &type) { type_ = type; } - -std::string class_template::type() const { return type_; } - -void class_template::set_name(const std::string &name) { name_ = name; } - -std::string class_template::name() const { return name_; } - -void class_template::set_default_value(const std::string &value) -{ - default_value_ = value; -} - -std::string class_template::default_value() const { return default_value_; } - -void class_template::is_variadic(bool is_variadic) noexcept -{ - is_variadic_ = is_variadic; -} - -bool class_template::is_variadic() const noexcept { return is_variadic_; } - -bool operator==(const class_template &l, const class_template &r) -{ - return (l.name() == r.name()) && (l.type() == r.type()); -} - -std::string class_template::to_string( - const clanguml::common::model::namespace_ &using_namespace) const -{ - using clanguml::common::model::namespace_; - - std::string res; - if (!type().empty()) - res += namespace_{type()}.relative_to(using_namespace).to_string(); - - // Render nested template params - if (!template_params_.empty()) { - std::vector params; - for (const auto &template_param : template_params_) { - params.push_back(template_param.to_string(using_namespace)); - } - - res += fmt::format("<{}>", fmt::join(params, ",")); - } - - if (!name().empty()) { - if (!type().empty()) - res += " "; - res += namespace_{name()}.relative_to(using_namespace).to_string(); - } - - if (!default_value().empty()) { - res += "="; - res += default_value(); - } - - return res; -} - -} diff --git a/src/class_diagram/model/class_template.h b/src/class_diagram/model/class_template.h deleted file mode 100644 index 603443e4..00000000 --- a/src/class_diagram/model/class_template.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * src/class_diagram/model/class_template.h - * - * Copyright (c) 2021-2022 Bartek Kryza - * - * 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/namespace.h" - -#include -#include - -namespace clanguml::class_diagram::model { - -class class_template { -public: - class_template(const std::string &type = "", const std::string &name = "", - const std::string &default_value = "", bool is_variadic = false); - - void set_type(const std::string &type); - std::string type() const; - - void set_name(const std::string &name); - std::string name() const; - - void set_default_value(const std::string &value); - std::string default_value() const; - - void is_variadic(bool is_variadic) noexcept; - bool is_variadic() const noexcept; - - friend bool operator==(const class_template &l, const class_template &r); - - std::vector template_params_; - - std::string to_string( - const clanguml::common::model::namespace_ &using_namespace) const; - -private: - std::string type_; - std::string name_; - std::string default_value_; - bool is_variadic_{false}; -}; -} diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 527f6bac..e964244c 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -58,7 +58,7 @@ diagram::get(const std::string &full_name) const bool diagram::has_class(const class_ &c) const { return std::any_of(classes_.cbegin(), classes_.cend(), - [&c](const auto &cc) { return cc.get().full_name() == c.full_name(); }); + [&c](const auto &cc) { return cc.get() == c; }); } bool diagram::has_enum(const enum_ &e) const @@ -109,30 +109,37 @@ void diagram::add_package(std::unique_ptr &&p) void diagram::add_class(std::unique_ptr &&c) { + const auto base_name = c->name(); + const auto full_name = c->full_name(false); + LOG_DBG("Adding class: {}::{}, {}", c->get_namespace().to_string(), - c->name(), c->full_name()); + base_name, full_name); - if (util::contains(c->name(), "::")) - throw std::runtime_error("Name cannot contain namespace: " + c->name()); + if (util::contains(base_name, "::")) + throw std::runtime_error("Name cannot contain namespace: " + base_name); - if (util::contains(c->name(), "<")) - throw std::runtime_error("Name cannot contain <: " + c->name()); + if (util::contains(base_name, "<")) + throw std::runtime_error("Name cannot contain <: " + base_name); - if (util::contains(c->name(), "*")) - throw std::runtime_error("Name cannot contain *: " + c->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; if (!has_class(*c)) { - classes_.emplace_back(*c); - auto ns = c->get_relative_namespace(); - auto name = c->name(); + classes_.push_back(type_safe::ref(*c)); + add_element(ns, std::move(c)); - ns |= name; - const auto ccc = get_element(ns); - assert(ccc.value().name() == name); + + const auto &el = get_element(name_and_ns).value(); + assert(el.name() == name); + assert(el.get_relative_namespace() == ns); } else - LOG_DBG( - "Class {} ({}) already in the model", c->name(), c->full_name()); + LOG_DBG("Class {} ({}) already in the model", base_name, full_name); } void diagram::add_enum(std::unique_ptr &&e) diff --git a/src/class_diagram/model/template_parameter.cc b/src/class_diagram/model/template_parameter.cc new file mode 100644 index 00000000..133bc364 --- /dev/null +++ b/src/class_diagram/model/template_parameter.cc @@ -0,0 +1,183 @@ +/** + * src/class_diagram/model/template_parameter.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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 "template_parameter.h" +#include "common/model/enums.h" +#include +#include + +namespace clanguml::class_diagram::model { + +template_parameter::template_parameter(const std::string &type, + const std::string &name, const std::string &default_value, bool is_variadic) + : default_value_{default_value} + , is_variadic_{is_variadic} +{ + set_name(name); + set_type(type); +} + +void template_parameter::set_type(const std::string &type) { type_ = type; } + +std::string template_parameter::type() const { return type_; } + +void template_parameter::set_name(const std::string &name) { name_ = name; } + +std::string template_parameter::name() const +{ + if (is_variadic_) + return name_ + "..."; + + return name_; +} + +void template_parameter::set_default_value(const std::string &value) +{ + default_value_ = value; +} + +std::string template_parameter::default_value() const { return default_value_; } + +void template_parameter::is_variadic(bool is_variadic) noexcept +{ + is_variadic_ = is_variadic; +} + +bool template_parameter::is_variadic() const noexcept { return is_variadic_; } + +bool template_parameter::is_specialization_of( + const template_parameter &ct) const +{ + if ((ct.is_template_parameter() || ct.is_template_template_parameter()) && + !is_template_parameter()) + return true; + + return false; +} + +void template_parameter::add_template_param(template_parameter &&ct) +{ + template_params_.emplace_back(std::move(ct)); +} + +void template_parameter::add_template_param(const template_parameter &ct) +{ + template_params_.push_back(ct); +} + +const std::vector & +template_parameter::template_params() const +{ + return template_params_; +} + +bool operator==(const template_parameter &l, const template_parameter &r) +{ + bool res{false}; + + if (l.is_template_parameter() != r.is_template_parameter()) + return res; + + if (l.is_template_parameter()) { + // If this is a template parameter (e.g. 'typename T' or 'typename U' + // we don't actually care what it is called + res = (l.is_variadic() == r.is_variadic()) && + (l.default_value() == r.default_value()); + } + else + res = (l.name() == r.name()) && (l.type() == r.type()) && + (l.default_value() == r.default_value()); + + return res && (l.template_params_ == r.template_params_); +} + +bool operator!=(const template_parameter &l, const template_parameter &r) +{ + return !(l == r); +} + +std::string template_parameter::to_string( + const clanguml::common::model::namespace_ &using_namespace, + bool relative) const +{ + using clanguml::common::model::namespace_; + + std::string res; + if (!type().empty()) { + if (!relative) + res += namespace_{type()}.to_string(); + else + res += namespace_{type()}.relative_to(using_namespace).to_string(); + } + + if (!name().empty()) { + if (!type().empty()) + res += " "; + if (!relative) + res += namespace_{name()}.to_string(); + else + res += namespace_{name()}.relative_to(using_namespace).to_string(); + } + + // Render nested template params + if (!template_params_.empty()) { + std::vector params; + for (const auto &template_param : template_params_) { + params.push_back( + template_param.to_string(using_namespace, relative)); + } + + res += fmt::format("<{}>", fmt::join(params, ",")); + } + + if (!default_value().empty()) { + res += "="; + res += default_value(); + } + + return res; +} + +void template_parameter::find_nested_relationships( + std::vector> + &nested_relationships, + common::model::relationship_t hint, + std::function condition) const +{ + // If this type argument should be included in the relationship + // just add it and skip recursion (e.g. this is a user defined type) + if (condition(name())) { + nested_relationships.push_back({to_string({}, false), hint}); + } + // Otherwise (e.g. this is a std::shared_ptr) and we're actually + // interested what is stored inside it + else { + for (const auto &template_argument : template_params()) { + if (condition(template_argument.name())) { + nested_relationships.push_back( + {template_argument.to_string({}, false), hint}); + } + else { + template_argument.find_nested_relationships( + nested_relationships, hint, condition); + } + } + } +} + +} diff --git a/src/class_diagram/model/template_parameter.h b/src/class_diagram/model/template_parameter.h new file mode 100644 index 00000000..0599000d --- /dev/null +++ b/src/class_diagram/model/template_parameter.h @@ -0,0 +1,118 @@ +/** + * src/class_diagram/model/template_parameter.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * 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/enums.h" +#include "common/model/namespace.h" + +#include +#include + +namespace clanguml::class_diagram::model { + +/// @brief Represents template parameter or template argument +/// +/// This class can represent both template parameter and template arguments, +/// including variadic parameters and instantiations with +/// nested templates +class template_parameter { +public: + template_parameter(const std::string &type = "", + const std::string &name = "", const std::string &default_value = "", + bool is_variadic = false); + + template_parameter(const template_parameter &right) = default; + + void set_type(const std::string &type); + std::string type() const; + + void set_name(const std::string &name); + std::string name() const; + + void set_default_value(const std::string &value); + std::string default_value() const; + + void is_variadic(bool is_variadic) noexcept; + bool is_variadic() const noexcept; + + bool is_specialization_of(const template_parameter &ct) const; + + friend bool operator==( + const template_parameter &l, const template_parameter &r); + friend bool operator!=( + const template_parameter &l, const template_parameter &r); + + bool is_template_parameter() const { return is_template_parameter_; } + + void is_template_parameter(bool is_template_parameter) + { + is_template_parameter_ = is_template_parameter; + } + + bool is_template_template_parameter() const + { + return is_template_template_parameter_; + } + + void is_template_template_parameter(bool is_template_template_parameter) + { + is_template_template_parameter_ = is_template_template_parameter; + } + + std::string to_string( + const clanguml::common::model::namespace_ &using_namespace, + bool relative) const; + + void add_template_param(template_parameter &&ct); + + void add_template_param(const template_parameter &ct); + + const std::vector &template_params() const; + + void find_nested_relationships( + std::vector> + &nested_relationships, + common::model::relationship_t hint, + std::function condition) const; + +private: + /// Represents the type of non-type template parameters + /// e.g. 'int' + std::string type_; + + /// The name of the parameter (e.g. 'T' or 'N') + std::string name_; + + /// Default value of the template parameter + std::string default_value_; + + /// Whether the template parameter is a regular template parameter + /// When false, it is a non-type template parameter + bool is_template_parameter_{false}; + + /// Whether the template parameter is a template template parameter + /// Can only be true when is_template_parameter_ is true + bool is_template_template_parameter_{false}; + + /// Whether the template parameter is variadic + bool is_variadic_{false}; + + // Nested template parameters + std::vector template_params_; +}; +} diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index bba81ad8..97259946 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -40,10 +40,10 @@ using clanguml::class_diagram::model::class_; using clanguml::class_diagram::model::class_member; using clanguml::class_diagram::model::class_method; using clanguml::class_diagram::model::class_parent; -using clanguml::class_diagram::model::class_template; using clanguml::class_diagram::model::diagram; using clanguml::class_diagram::model::enum_; using clanguml::class_diagram::model::method_parameter; +using clanguml::class_diagram::model::template_parameter; using clanguml::class_diagram::model::type_alias; using clanguml::common::model::access_t; using clanguml::common::model::relationship; @@ -200,8 +200,9 @@ void translation_unit_visitor::process_type_alias_template( const cppast::cpp_alias_template &at) { auto alias_kind = at.type_alias().underlying_type().kind(); + if (alias_kind == cppast::cpp_type_kind::unexposed_t) { - LOG_DBG("Template alias has unexposed underlying type: {}", + LOG_DBG("Template alias has unexposed underlying type - ignoring: {}", static_cast( at.type_alias().underlying_type()) .name()); @@ -213,6 +214,13 @@ void translation_unit_visitor::process_type_alias_template( static_cast( resolve_alias(at.type_alias().underlying_type()))); + assert(tinst); + + tinst->is_alias(true); + + if (tinst->get_namespace().is_empty()) + tinst->set_namespace(ctx.get_namespace()); + ctx.add_type_alias_template( cx::util::full_name(ctx.get_namespace(), at), type_safe::ref(at.type_alias().underlying_type())); @@ -688,18 +696,18 @@ void translation_unit_visitor::process_class_children( } bool translation_unit_visitor::process_field_with_template_instantiation( - const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr, - class_ &c, class_member &m, cppast::cpp_access_specifier_kind as) + const cppast::cpp_member_variable &mv, const cppast::cpp_type &type, + class_ &c, class_member &member, cppast::cpp_access_specifier_kind as) { LOG_DBG("Processing field with template instantiation type {}", - cppast::to_string(tr)); + cppast::to_string(type)); bool res = false; - auto tr_declaration = cppast::to_string(tr); + auto tr_declaration = cppast::to_string(type); const auto &template_instantiation_type = - static_cast(tr); + static_cast(type); const auto &unaliased = static_cast( @@ -708,28 +716,43 @@ bool translation_unit_visitor::process_field_with_template_instantiation( auto tr_unaliased_declaration = cppast::to_string(unaliased); std::unique_ptr tinst_ptr; - if (util::contains(tr_declaration, "<")) - tinst_ptr = build_template_instantiation(template_instantiation_type); - else + + found_relationships_t nested_relationships; + if (tr_declaration == tr_unaliased_declaration) tinst_ptr = build_template_instantiation(unaliased); + else + tinst_ptr = build_template_instantiation( + static_cast( + type.canonical())); auto &tinst = *tinst_ptr; + // // Infer the relationship of this field to the template // instantiation + // TODO: Refactor this to a configurable mapping + relationship_t nested_relationship_hint = relationship_t::kAggregation; + + if (tr_unaliased_declaration.find("std::shared_ptr") == 0) { + nested_relationship_hint = relationship_t::kAssociation; + } + else if (tr_unaliased_declaration.find("std::weak_ptr") == 0) { + nested_relationship_hint = relationship_t::kAssociation; + } + relationship_t relationship_type{}; if (mv.type().kind() == cppast::cpp_type_kind::pointer_t || mv.type().kind() == cppast::cpp_type_kind::reference_t) relationship_type = relationship_t::kAssociation; else - relationship_type = relationship_t::kAggregation; + relationship_type = nested_relationship_hint; relationship rr{relationship_type, tinst.full_name(), detail::cpp_access_specifier_to_access(as), mv.name()}; - rr.set_style(m.style_spec()); + rr.set_style(member.style_spec()); // Process field decorators - auto [decorator_rtype, decorator_rmult] = m.get_relationship(); + auto [decorator_rtype, decorator_rmult] = member.get_relationship(); if (decorator_rtype != relationship_t::kNone) { rr.set_type(decorator_rtype); auto mult = util::split(decorator_rmult, ":"); @@ -739,6 +762,8 @@ bool translation_unit_visitor::process_field_with_template_instantiation( } } + // Add instantiation relationship from the generated template instantiation + // of the field type to its primary template if (ctx.diagram().should_include(tinst.get_namespace(), tinst.name())) { LOG_DBG("Adding field instantiation relationship {} {} {} : {}", rr.destination(), clanguml::common::model::to_string(rr.type()), @@ -746,19 +771,64 @@ bool translation_unit_visitor::process_field_with_template_instantiation( c.add_relationship(std::move(rr)); - if (tr_declaration != tr_unaliased_declaration) { - // Add template instantiation/specialization relationship; - tinst.add_relationship( - {relationship_t::kInstantiation, tr_unaliased_declaration}); - } - res = true; LOG_DBG("Created template instantiation: {}", tinst.full_name()); + assert(tinst_ptr); + ctx.diagram().add_class(std::move(tinst_ptr)); } + // + // Only add nested template relationships to this class if the top level + // template is not in the diagram (e.g. it is a std::shared_ptr<>) + // + if (!ctx.diagram().should_include(tinst.get_namespace(), tinst.name())) { + res = add_nested_template_relationships(mv, c, member, as, tinst, + relationship_type, decorator_rtype, decorator_rmult); + } + + return res; +} + +bool translation_unit_visitor::add_nested_template_relationships( + const cppast::cpp_member_variable &mv, class_ &c, class_member &m, + cppast::cpp_access_specifier_kind &as, const class_ &tinst, + relationship_t &relationship_type, relationship_t &decorator_rtype, + std::string &decorator_rmult) +{ + bool res{false}; + found_relationships_t nested_relationships; + + for (const auto &template_argument : tinst.templates()) { + template_argument.find_nested_relationships(nested_relationships, + relationship_type, + [&d = ctx.diagram()](const std::string &full_name) { + auto [ns, name] = cx::util::split_ns(full_name); + return d.should_include(ns, name); + }); + } + + if (!nested_relationships.empty()) { + for (const auto &rel : nested_relationships) { + relationship nested_relationship{std::get<1>(rel), std::get<0>(rel), + detail::cpp_access_specifier_to_access(as), mv.name()}; + nested_relationship.set_style(m.style_spec()); + if (decorator_rtype != relationship_t::kNone) { + nested_relationship.set_type(decorator_rtype); + auto mult = util::split(decorator_rmult, ":"); + if (mult.size() == 2) { + nested_relationship.set_multiplicity_source(mult[0]); + nested_relationship.set_multiplicity_destination(mult[1]); + } + } + c.add_relationship(std::move(nested_relationship)); + } + + res = true; + } + return res; } @@ -805,6 +875,8 @@ void translation_unit_visitor::process_field( process_field_with_template_instantiation(mv, tr, c, m, as); } else if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) { + // This can be either template instantiation or an alias template + // instantiation template_instantiation_added_as_aggregation = process_field_with_template_instantiation(mv, tr, c, m, as); } @@ -814,12 +886,18 @@ void translation_unit_visitor::process_field( // TODO } + // + // Try to find relationships in the type of the member, unless it has + // been already added as part of template processing or it is marked + // to be skipped in the comment + // if (!m.skip_relationship() && !template_instantiation_added_as_aggregation && (tr.kind() != cppast::cpp_type_kind::builtin_t) && (tr.kind() != cppast::cpp_type_kind::template_parameter_t)) { const auto &ttt = resolve_alias(mv.type()); - std::vector> relationships; + + found_relationships_t relationships; auto found = find_relationships(ttt, relationships); for (const auto &[type, relationship_type] : relationships) { @@ -1206,7 +1284,7 @@ void translation_unit_visitor:: // First check if tinst already exists auto tinst_ptr = build_template_instantiation(template_instantiation_type); - auto &tinst = *tinst_ptr; + const auto &tinst = *tinst_ptr; relationship rr{relationship_t::kDependency, tinst.full_name()}; LOG_DBG("Adding field dependency relationship {} {} {} " @@ -1216,7 +1294,7 @@ void translation_unit_visitor:: c.add_relationship(std::move(rr)); - if (ctx.diagram().should_include(c)) + if (ctx.diagram().should_include(tinst)) ctx.diagram().add_class(std::move(tinst_ptr)); } } @@ -1225,20 +1303,40 @@ void translation_unit_visitor:: void translation_unit_visitor::process_template_type_parameter( const cppast::cpp_template_type_parameter &t, class_ &parent) { - parent.add_template({"", t.name(), "", t.is_variadic()}); + template_parameter ct; + ct.set_type(""); + ct.is_template_parameter(true); + ct.set_name(t.name()); + ct.set_default_value(""); + ct.is_variadic(t.is_variadic()); + + parent.add_template(std::move(ct)); } void translation_unit_visitor::process_template_nontype_parameter( const cppast::cpp_non_type_template_parameter &t, class_ &parent) { - parent.add_template( - {cppast::to_string(t.type()), t.name(), "", t.is_variadic()}); + template_parameter ct; + ct.set_type(cppast::to_string(t.type())); + ct.is_template_parameter(false); + ct.set_name(t.name()); + ct.set_default_value(""); + ct.is_variadic(t.is_variadic()); + + parent.add_template(std::move(ct)); } void translation_unit_visitor::process_template_template_parameter( const cppast::cpp_template_template_parameter &t, class_ &parent) { - parent.add_template({"", t.name() + "<>"}); + template_parameter ct; + ct.set_type(""); + ct.is_template_template_parameter(true); + ct.set_name(t.name() + "<>"); + ct.set_default_value(""); + ct.is_variadic(t.is_variadic()); + + parent.add_template(std::move(ct)); } void translation_unit_visitor::process_friend(const cppast::cpp_friend &f, @@ -1389,30 +1487,9 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( auto full_name = fmt::format("{}", fmt::join(ns_and_name, "::")); - // Try to match common containers - // TODO: Refactor to a separate class with configurable - // container list - if (full_name.find("std::unique_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAggregation); - } - else if (full_name.find("std::shared_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAssociation); - } - else if (full_name.find("std::weak_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAssociation); - } - else if (full_name.find("std::vector") == 0) { - if (args[0u].type().has_value()) - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAggregation); - else - LOG_DBG("Failed to process template argument of std::vector at: {}", - fn); - } - else if (ctx.diagram().should_include(ns, name)) { + auto full_base_name = full_name.substr(0, full_name.find('<')); + + if (ctx.diagram().should_include(ns, name)) { LOG_DBG("User defined template instantiation: {} | {}", cppast::to_string(t_), cppast::to_string(t_.canonical())); @@ -1431,11 +1508,23 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( } } else { + int argument_index = 0; + auto relationship_hint = relationship_type; for (const auto &arg : args) { - if (arg.type().has_value()) { - found = find_relationships( - arg.type().value(), relationships, relationship_type); + if (ctx.config().relationship_hints().count(full_base_name) > 0) { + relationship_hint = ctx.config() + .relationship_hints() + .at(full_base_name) + .get(argument_index); } + + if (arg.type().has_value()) { + found = found || + find_relationships( + arg.type().value(), relationships, relationship_hint); + } + + argument_index++; } } @@ -1510,11 +1599,11 @@ bool translation_unit_visitor::find_relationships_in_array( } bool translation_unit_visitor::find_relationships_in_unexposed_template_params( - const class_template &ct, found_relationships_t &relationships) const + const template_parameter &ct, found_relationships_t &relationships) const { bool found{false}; LOG_DBG("Finding relationships in user defined type: {}", - ct.to_string(ctx.config().using_namespace())); + ct.to_string(ctx.config().using_namespace(), false)); auto type_with_namespace = ctx.get_name_with_namespace(ct.type()); @@ -1529,7 +1618,7 @@ bool translation_unit_visitor::find_relationships_in_unexposed_template_params( found = true; } - for (const auto &nested_template_params : ct.template_params_) { + for (const auto &nested_template_params : ct.template_params()) { found = find_relationships_in_unexposed_template_params( nested_template_params, relationships) || found; @@ -1541,23 +1630,48 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( const cppast::cpp_template_instantiation_type &t, std::optional parent) { + // // Create class_ instance to hold the template instantiation + // auto tinst_ptr = std::make_unique(ctx.config().using_namespace()); auto &tinst = *tinst_ptr; std::string full_template_name; + auto tr_declaration = cppast::to_string(t); + + // + // If this is an alias - resolve the alias target + // + const auto &unaliased = + static_cast( + resolve_alias(t)); + auto t_unaliased_declaration = cppast::to_string(unaliased); + + bool t_is_alias = t_unaliased_declaration != tr_declaration; + + // + // Here we'll hold the template base params to replace with the + // instantiated values + // std::deque> template_base_params{}; tinst.set_namespace(ctx.get_namespace()); auto tinst_full_name = cppast::to_string(t); + // + // Typically, every template instantiation should have a + // primary_template() which should also be generated here if it doesn't + // exist yet in the model + // if (t.primary_template().get(ctx.entity_index()).size()) { + auto size = t.primary_template().get(ctx.entity_index()).size(); + (void)size; build_template_instantiation_primary_template( t, tinst, template_base_params, parent, full_template_name); } else { - LOG_DBG("Template instantiation {} has no primary template", + LOG_DBG("Template instantiation {} has no primary template?", cppast::to_string(t)); full_template_name = cppast::to_string(t); @@ -1565,7 +1679,9 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( LOG_DBG("Building template instantiation for {}", full_template_name); + // // Extract namespace from base template name + // const auto [ns, name] = cx::util::split_ns(tinst_full_name); tinst.set_name(name); if (ns.is_empty()) @@ -1573,17 +1689,12 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( else tinst.set_namespace(ns); - if (tinst_full_name.find('<') != std::string::npos) { - tinst.set_name(tinst_full_name.substr(0, tinst_full_name.find('<'))); - } - tinst.is_template_instantiation(true); + tinst.is_alias(t_is_alias); - if (tinst.full_name().substr(0, tinst.full_name().find('<')).find("::") == - std::string::npos) { - tinst.set_name(name); - } - + // + // Process exposed template arguments - if any + // if (t.arguments_exposed()) { auto arg_index = 0U; // We can figure this only when we encounter variadic param in @@ -1593,7 +1704,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( auto has_variadic_params = false; for (const auto &targ : t.arguments().value()) { - class_template ct; + template_parameter ct; if (targ.type()) { build_template_instantiation_process_type_argument( parent, tinst, targ, ct); @@ -1612,7 +1723,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( ct); } - LOG_DBG("Adding template argument '{}'", ct.type()); + LOG_DBG("Adding template argument '{}'", ct.to_string({}, false)); tinst.add_template(std::move(ct)); } @@ -1631,9 +1742,31 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( destination = cppast::to_string(ctx.get_type_alias(fn).get()); } else { - // Otherwise point to the base template - destination = tinst.base_template(); + std::string best_match_full_name{}; + + int best_match = 0; + // First try to find the best match for this template in partially + // specialized templates + for (const auto c : ctx.diagram().classes()) { + if (c == tinst) + continue; + + auto match = c->calculate_template_specialization_match( + tinst, full_template_name); + + if (match > best_match) { + best_match = match; + best_match_full_name = c->full_name(false); + } + } + + if (!best_match_full_name.empty()) + destination = best_match_full_name; + else + // Otherwise point to the base template + destination = tinst.base_template(); } + relationship r{relationship_t::kInstantiation, destination}; tinst.add_relationship(std::move(r)); @@ -1643,7 +1776,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( bool translation_unit_visitor::build_template_instantiation_add_base_classes( class_ &tinst, std::deque> &template_base_params, - int arg_index, bool variadic_params, const class_template &ct) const + int arg_index, bool variadic_params, const template_parameter &ct) const { bool add_template_argument_as_base_class = false; @@ -1659,11 +1792,12 @@ bool translation_unit_visitor::build_template_instantiation_add_base_classes( } if (add_template_argument_as_base_class) { - LOG_DBG("Adding template argument as base class '{}'", ct.type()); + LOG_DBG("Adding template argument as base class '{}'", + ct.to_string({}, false)); class_parent cp; cp.set_access(access_t::kPublic); - cp.set_name(ct.type()); + cp.set_name(ct.to_string({}, false)); tinst.add_parent(std::move(cp)); } @@ -1673,7 +1807,7 @@ bool translation_unit_visitor::build_template_instantiation_add_base_classes( void translation_unit_visitor:: build_template_instantiation_process_expression_argument( - const cppast::cpp_template_argument &targ, class_template &ct) const + const cppast::cpp_template_argument &targ, template_parameter &ct) const { const auto &exp = targ.expression().value(); if (exp.kind() == cppast::cpp_expression_kind::literal_t) @@ -1684,27 +1818,51 @@ void translation_unit_visitor:: .expression() .as_string()); - LOG_DBG("Template argument is an expression {}", ct.type()); + LOG_DBG("Template argument is an expression {}", ct.name()); } void translation_unit_visitor:: build_template_instantiation_process_type_argument( const std::optional &parent, class_ &tinst, const cppast::cpp_template_argument &targ, - class_template &ct) + template_parameter &ct) { - ct.set_type(cppast::to_string(targ.type().value())); - - LOG_DBG("Template argument is a type {}", ct.type()); - auto fn = cx::util::full_name( + auto full_name = cx::util::full_name( cppast::remove_cv(cx::util::unreferenced(targ.type().value())), ctx.entity_index(), false); - auto [fn_ns, fn_name] = cx::util::split_ns(fn); + auto [fn_ns, fn_name] = cx::util::split_ns(full_name); + auto template_argument_kind = targ.type().value().kind(); - if (targ.type().value().kind() == + if (template_argument_kind == cppast::cpp_type_kind::unexposed_t) { + // Here we're on our own - just make a best guess + if (!full_name.empty() && !util::contains(full_name, "<") && + !util::contains(full_name, ":") && std::isupper(full_name.at(0))) + ct.is_template_parameter(true); + else + ct.is_template_parameter(false); + + ct.set_name(full_name); + } + else if (template_argument_kind == + cppast::cpp_type_kind::template_parameter_t) { + ct.is_template_parameter(true); + ct.set_name(full_name); + } + else if (template_argument_kind == cppast::cpp_type_kind::builtin_t) { + ct.is_template_parameter(false); + ct.set_type(full_name); + } + else if (template_argument_kind == cppast::cpp_type_kind::template_instantiation_t) { + // Check if this template should be simplified (e.g. system + // template aliases such as std:basic_string should be simply + // std::string + if (simplify_system_template(ct, full_name)) { + return; + } + const auto &nested_template_parameter = static_cast( targ.type().value()); @@ -1716,18 +1874,31 @@ void translation_unit_visitor:: auto [tinst_ns, tinst_name] = cx::util::split_ns(tinst.full_name(false)); + ct.set_name(full_name.substr(0, full_name.find('<'))); + + assert(!ct.name().empty()); + auto nested_tinst = build_template_instantiation(nested_template_parameter, ctx.diagram().should_include(tinst_ns, tinst_name) ? std::make_optional(&tinst) : parent); + assert(nested_tinst); + + for (const auto &t : nested_tinst->templates()) + ct.add_template_param(t); + relationship tinst_dependency{ relationship_t::kDependency, nested_tinst->full_name()}; - auto nested_tinst_full_name = nested_tinst->full_name(); + auto nested_tinst_full_name = nested_tinst->full_name(false); + + auto [nested_tinst_ns, nested_tinst_name] = + cx::util::split_ns(nested_tinst_full_name); + + if (ctx.diagram().should_include(nested_tinst_ns, nested_tinst_name)) { - if (ctx.diagram().should_include(fn_ns, fn_name)) { ctx.diagram().add_class(std::move(nested_tinst)); } @@ -1738,7 +1909,7 @@ void translation_unit_visitor:: ) { LOG_DBG("Creating nested template dependency to template " "instantiation {}, {} -> {}", - fn, tinst.full_name(), tinst_dependency.destination()); + full_name, tinst.full_name(), tinst_dependency.destination()); tinst.add_relationship(std::move(tinst_dependency)); } @@ -1746,18 +1917,18 @@ void translation_unit_visitor:: LOG_DBG("Creating nested template dependency to parent " "template " "instantiation {}, {} -> {}", - fn, (*parent)->full_name(), tinst_dependency.destination()); + full_name, (*parent)->full_name(), + tinst_dependency.destination()); (*parent)->add_relationship(std::move(tinst_dependency)); } else { LOG_DBG("No nested template dependency to template " "instantiation: {}, {} -> {}", - fn, tinst.full_name(), tinst_dependency.destination()); + full_name, tinst.full_name(), tinst_dependency.destination()); } } - else if (targ.type().value().kind() == - cppast::cpp_type_kind::user_defined_t) { + else if (template_argument_kind == cppast::cpp_type_kind::user_defined_t) { relationship tinst_dependency{relationship_t::kDependency, cx::util::full_name( cppast::remove_cv(cx::util::unreferenced(targ.type().value())), @@ -1767,6 +1938,8 @@ void translation_unit_visitor:: "type {} -> {}", tinst.full_name(), tinst_dependency.destination()); + ct.set_name(full_name); + if (ctx.diagram().should_include(fn_ns, fn_name)) { tinst.add_relationship(std::move(tinst_dependency)); } @@ -1870,9 +2043,17 @@ const cppast::cpp_type &translation_unit_visitor::resolve_alias( if (util::contains(type_full_name, "<")) type_full_name = util::split(type_full_name, "<")[0]; + auto type_full_name_in_current_ns = ctx.get_namespace(); + type_full_name_in_current_ns |= common::model::namespace_{type_full_name}; + if (ctx.has_type_alias_template(type_full_name)) { return ctx.get_type_alias(type_full_name).get(); } + else if (ctx.has_type_alias_template( + type_full_name_in_current_ns.to_string())) { + return ctx.get_type_alias(type_full_name_in_current_ns.to_string()) + .get(); + } else if (ctx.has_type_alias(type_full_name)) { return ctx.get_type_alias_final(raw_type).get(); } @@ -1892,4 +2073,15 @@ const cppast::cpp_type &translation_unit_visitor::resolve_alias_template( return type; } + +bool translation_unit_visitor::simplify_system_template( + template_parameter &ct, const std::string &full_name) +{ + if (ctx.config().template_aliases().count(full_name) > 0) { + ct.set_name(ctx.config().template_aliases().at(full_name)); + return true; + } + else + return false; +} } diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 1a7fcf6d..00286c19 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -19,6 +19,7 @@ #include "class_diagram/model/diagram.h" #include "class_diagram/visitor/translation_unit_context.h" +#include "common/model/enums.h" #include "config/config.h" #include @@ -48,6 +49,11 @@ namespace clanguml::class_diagram::visitor { using found_relationships_t = std::vector>; +// class nested_template_relationships { +// +// std::vector> children; +//}; + class translation_unit_visitor { public: translation_unit_visitor(cppast::cpp_entity_index &idx, @@ -71,9 +77,9 @@ public: cppast::cpp_access_specifier_kind as); bool 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 &type, clanguml::class_diagram::model::class_ &c, - clanguml::class_diagram::model::class_member &m, + clanguml::class_diagram::model::class_member &member, cppast::cpp_access_specifier_kind as); void process_static_field(const cppast::cpp_variable &mv, @@ -195,7 +201,7 @@ private: common::model::relationship_t relationship_type) const; bool find_relationships_in_unexposed_template_params( - const model::class_template &ct, + const model::template_parameter &ct, found_relationships_t &relationships) const; void build_template_instantiation_primary_template( @@ -208,16 +214,16 @@ private: void build_template_instantiation_process_type_argument( const std::optional &parent, model::class_ &tinst, const cppast::cpp_template_argument &targ, - model::class_template &ct); + class_diagram::model::template_parameter &ct); void build_template_instantiation_process_expression_argument( const cppast::cpp_template_argument &targ, - model::class_template &ct) const; + model::template_parameter &ct) const; bool build_template_instantiation_add_base_classes(model::class_ &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, - const model::class_template &ct) const; + const model::template_parameter &ct) const; void process_function_parameter_find_relationships_in_template( model::class_ &c, const std::set &template_parameter_names, @@ -225,5 +231,15 @@ private: // ctx allows to track current visitor context, e.g. current namespace translation_unit_context ctx; + bool simplify_system_template( + model::template_parameter ¶meter, const std::string &basicString); + + bool add_nested_template_relationships( + const cppast::cpp_member_variable &mv, model::class_ &c, + model::class_member &m, cppast::cpp_access_specifier_kind &as, + const model::class_ &tinst, + common::model::relationship_t &relationship_type, + common::model::relationship_t &decorator_rtype, + std::string &decorator_rmult); }; } diff --git a/src/common/generators/plantuml/generator.cc b/src/common/generators/plantuml/generator.cc index eea26a9c..b10fd4c4 100644 --- a/src/common/generators/plantuml/generator.cc +++ b/src/common/generators/plantuml/generator.cc @@ -37,6 +37,8 @@ std::string to_plantuml(relationship_t r, std::string style) return style.empty() ? "<.." : fmt::format("<.[{}].", style); case relationship_t::kDependency: return style.empty() ? "..>" : fmt::format(".[{}].>", style); + case relationship_t::kAlias: + return style.empty() ? ".." : fmt::format(".[{}].", style); default: return ""; } diff --git a/src/common/model/enums.cc b/src/common/model/enums.cc index 218844b9..d6fdce79 100644 --- a/src/common/model/enums.cc +++ b/src/common/model/enums.cc @@ -44,6 +44,8 @@ std::string to_string(relationship_t r) return "friendship"; case relationship_t::kDependency: return "dependency"; + case relationship_t::kAlias: + return "alias"; default: assert(false); } diff --git a/src/common/model/enums.h b/src/common/model/enums.h index 7c2501c2..e9c3cd2e 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -35,6 +35,7 @@ enum class relationship_t { kAssociation, kInstantiation, kFriendship, + kAlias, kDependency }; diff --git a/src/common/model/nested_trait.h b/src/common/model/nested_trait.h index 8894d4e1..b4e79cfe 100644 --- a/src/common/model/nested_trait.h +++ b/src/common/model/nested_trait.h @@ -56,8 +56,8 @@ public: { assert(p); - LOG_DBG( - "Adding nested element {} at path {}", p->name(), path.to_string()); + LOG_DBG("Adding nested element {} at path '{}'", p->name(), + path.to_string()); if (path.is_empty()) { add_element(std::move(p)); diff --git a/src/common/model/path.h b/src/common/model/path.h index 555b5085..44571bb3 100644 --- a/src/common/model/path.h +++ b/src/common/model/path.h @@ -39,7 +39,7 @@ public: std::copy(begin, end, std::back_inserter(path_)); } - path(const path &right) noexcept = default; + path(const path &right) { path_ = right.path_; } path &operator=(const path &right) noexcept = default; diff --git a/src/config/config.cc b/src/config/config.cc index ba1f2b30..f327ab0d 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -120,6 +120,53 @@ common::model::diagram_t include_diagram::type() const return common::model::diagram_t::kInclude; } +void class_diagram::initialize_relationship_hints() +{ + using common::model::relationship_t; + + if (!relationship_hints().count("std::vector")) { + relationship_hints().insert({"std::vector", {}}); + } + if (!relationship_hints().count("std::unique_ptr")) { + relationship_hints().insert({"std::unique_ptr", {}}); + } + if (!relationship_hints().count("std::shared_ptr")) { + relationship_hints().insert( + {"std::shared_ptr", {relationship_t::kAssociation}}); + } + if (!relationship_hints().count("std::weak_ptr")) { + relationship_hints().insert( + {"std::weak_ptr", {relationship_t::kAssociation}}); + } + if (!relationship_hints().count("std::tuple")) { + relationship_hints().insert({"std::tuple", {}}); + } + if (!relationship_hints().count("std::map")) { + relationship_hint_t hint{relationship_t::kNone}; + hint.argument_hints.insert({1, relationship_t::kAggregation}); + relationship_hints().insert({"std::tuple", std::move(hint)}); + } +} + +void class_diagram::initialize_template_aliases() +{ + if (!template_aliases().count("std::basic_string")) { + template_aliases().insert({"std::basic_string", "std::string"}); + } + if (!template_aliases().count("std::basic_string")) { + template_aliases().insert( + {"std::basic_string", "std::wstring"}); + } + if (!template_aliases().count("std::basic_string")) { + template_aliases().insert( + {"std::basic_string", "std::u16string"}); + } + if (!template_aliases().count("std::basic_string")) { + template_aliases().insert( + {"std::basic_string", "std::u32string"}); + } +} + template <> void append_value(plantuml &l, const plantuml &r) { l.append(r); @@ -141,6 +188,7 @@ using clanguml::config::layout_hint; using clanguml::config::method_arguments; using clanguml::config::package_diagram; using clanguml::config::plantuml; +using clanguml::config::relationship_hint_t; using clanguml::config::sequence_diagram; using clanguml::config::source_location; @@ -459,6 +507,11 @@ template <> struct convert { get_option(node, rhs.include_relations_also_as_members); get_option(node, rhs.generate_method_arguments); get_option(node, rhs.generate_packages); + get_option(node, rhs.relationship_hints); + get_option(node, rhs.template_aliases); + + rhs.initialize_relationship_hints(); + rhs.initialize_template_aliases(); return true; } @@ -551,6 +604,42 @@ template <> struct convert { } }; +// +// relationship_hint_t Yaml decoder +// +template <> struct convert { + static bool decode(const Node &node, relationship_hint_t &rhs) + { + assert(node.Type() == NodeType::Map || node.Type() == NodeType::Scalar); + + if (node.Type() == NodeType::Scalar) { + // This will be default relationship hint for all arguments + // of this template (useful for instance for tuples) + rhs.default_hint = node.as(); + } + else { + for (const auto &it : node) { + auto key = it.first.as(); + if (key == "default") { + rhs.default_hint = node["default"].as(); + } + else { + try { + auto index = stoul(key); + rhs.argument_hints[index] = + it.second.as(); + } + catch (std::exception &e) { + return false; + } + } + } + } + + return true; + } +}; + // // config Yaml decoder // diff --git a/src/config/config.h b/src/config/config.h index c663f88d..0c2ad6e9 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -95,6 +95,29 @@ struct git_config { std::string toplevel; }; +struct relationship_hint_t { + std::map argument_hints; + common::model::relationship_t default_hint; + + relationship_hint_t(common::model::relationship_t def = + common::model::relationship_t::kAggregation) + : default_hint{def} + { + } + + common::model::relationship_t get(unsigned int argument_index) const + { + if (argument_hints.count(argument_index) > 0) + return argument_hints.at(argument_index); + + return default_hint; + } +}; + +using relationship_hints_t = std::map; + +using template_aliases_t = std::map; + std::string to_string(const hint_t t); struct inheritable_diagram_options { @@ -113,6 +136,8 @@ struct inheritable_diagram_options { option base_directory{"__parent_path"}; option relative_to{"relative_to"}; option generate_system_headers{"generate_system_headers", false}; + option relationship_hints{"relationship_hints"}; + option template_aliases{"template_aliases"}; void inherit(const inheritable_diagram_options &parent); }; @@ -139,7 +164,9 @@ struct class_diagram : public diagram { option> classes{"classes"}; option layout{"layout"}; - bool has_class(std::string clazz); + void initialize_relationship_hints(); + + void initialize_template_aliases(); }; struct sequence_diagram : public diagram { diff --git a/src/cx/util.cc b/src/cx/util.cc index 40f0ad71..4de3512e 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -25,7 +25,7 @@ #include #include -#include +#include #include namespace clanguml { @@ -162,8 +162,9 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx) .get(idx)[0] .get()); } - else - return ""; + else { + return {}; + } } else { auto canon = cppast::to_string(t.canonical()); @@ -180,6 +181,9 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx) return fmt::format( "{}", fmt::join(ns_toks.begin(), ns_toks.end(), "::")); } + else if (canon.find("type-parameter-") == 0) { + return ""; + } else { // This is a bug/feature in libclang, where canonical representation // of a template type with incomplete specialization doesn't have a @@ -250,19 +254,19 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t) return t; } -std::vector +std::vector parse_unexposed_template_params(const std::string ¶ms, std::function ns_resolve) { - using class_diagram::model::class_template; + using class_diagram::model::template_parameter; - std::vector res; + std::vector res; int nested_template_level{0}; auto it = params.begin(); std::string type{}; - std::vector nested_params; + std::vector nested_params; bool complete_class_template{false}; while (it != params.end()) { @@ -289,7 +293,8 @@ parse_unexposed_template_params(const std::string ¶ms, nested_params = parse_unexposed_template_params(nested_params_str, ns_resolve); if (nested_params.empty()) - nested_params.emplace_back(class_template{nested_params_str}); + nested_params.emplace_back( + template_parameter{nested_params_str}); it = bracket_match_end - 1; } else if (*it == '>') { @@ -302,10 +307,11 @@ parse_unexposed_template_params(const std::string ¶ms, type += *it; } if (complete_class_template) { - class_template t; + template_parameter t; t.set_type(ns_resolve(clanguml::util::trim(type))); type = ""; - t.template_params_ = std::move(nested_params); + for (auto &¶m : nested_params) + t.add_template_param(std::move(param)); res.emplace_back(std::move(t)); complete_class_template = false; @@ -314,10 +320,11 @@ parse_unexposed_template_params(const std::string ¶ms, } if (!type.empty()) { - class_template t; + template_parameter t; t.set_type(ns_resolve(clanguml::util::trim(type))); type = ""; - t.template_params_ = std::move(nested_params); + for (auto &¶m : nested_params) + t.add_template_param(std::move(param)); res.emplace_back(std::move(t)); complete_class_template = false; diff --git a/src/cx/util.h b/src/cx/util.h index cd8e1564..6a052095 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include namespace clanguml { @@ -54,7 +54,7 @@ std::pair split_ns( bool is_inside_class(const cppast::cpp_entity &e); -std::vector +std::vector parse_unexposed_template_params(const std::string ¶ms, std::function ns_resolve); diff --git a/tests/t00013/t00013.cc b/tests/t00013/t00013.cc index d96d0e84..d132b142 100644 --- a/tests/t00013/t00013.cc +++ b/tests/t00013/t00013.cc @@ -34,6 +34,11 @@ template struct E { T e; }; +template struct G { + T g; + std::tuple args; +}; + using namespace ABCD; class R { public: @@ -52,6 +57,8 @@ public: template T get_f(const F &f) { return f.f; } int get_int_f(const F &f) { return f.f; } + G gintstring; + private: mutable E estring; }; diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h index 5bcf89c4..28f672b1 100644 --- a/tests/t00013/test_case.h +++ b/tests/t00013/test_case.h @@ -40,6 +40,9 @@ TEST_CASE("t00013", "[test-case][class]") REQUIRE_THAT(puml, IsClass(_A("B"))); REQUIRE_THAT(puml, IsClass(_A("C"))); REQUIRE_THAT(puml, IsClass(_A("D"))); + REQUIRE_THAT(puml, IsClassTemplate("E", "T")); + REQUIRE_THAT(puml, IsClassTemplate("G", "T,Args...")); + REQUIRE_THAT(puml, !IsDependency(_A("R"), _A("R"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("A"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); @@ -56,6 +59,9 @@ TEST_CASE("t00013", "[test-case][class]") REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("F"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("F"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("G"), _A("G"))); + save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); } diff --git a/tests/t00014/t00014.cc b/tests/t00014/t00014.cc index a1e3824a..dca89263 100644 --- a/tests/t00014/t00014.cc +++ b/tests/t00014/t00014.cc @@ -1,10 +1,7 @@ -#include #include -#include #include -#include +#include #include -#include #include #include @@ -26,15 +23,25 @@ template struct A { P p; }; -template using AString = A; - -template using GeneralCallback = std::function; -using VoidCallback = GeneralCallback<>; - struct B { std::string value; }; +template using AString = A; +template using AStringPtr = A>; + +template +using PairPairBA = std::pair>, long>; + +template using VectorPtr = std::unique_ptr>; +template using APtr = std::unique_ptr>; +template using ASharedPtr = std::shared_ptr>; +template +using AAPtr = std::unique_ptr, A>>; + +template using GeneralCallback = std::function; +using VoidCallback = GeneralCallback<>; + using BVector = std::vector; using BVector2 = BVector; @@ -43,11 +50,16 @@ using AStringString = AString; using BStringString = AStringString; class R { - // clang-uml: tinst A + PairPairBA bapair; + + APtr abool; + AAPtr aboolfloat; + ASharedPtr afloat; A boolstring; - AString floatstring; + AStringPtr floatstring; AIntString intstring; AStringString stringstring; + BStringString bstringstring; protected: BVector bs; @@ -56,6 +68,7 @@ public: BVector2 bs2; GeneralCallback cb; VoidCallback vcb; + VectorPtr vps; }; } } diff --git a/tests/t00014/test_case.h b/tests/t00014/test_case.h index 70a414af..e7dc0533 100644 --- a/tests/t00014/test_case.h +++ b/tests/t00014/test_case.h @@ -36,31 +36,55 @@ TEST_CASE("t00014", "[test-case][class]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); REQUIRE_THAT(puml, IsClassTemplate("A", "T,P")); REQUIRE_THAT(puml, IsClassTemplate("A", "T,std::string")); + REQUIRE_THAT(puml, IsClassTemplate("A", "T,std::unique_ptr")); + REQUIRE_THAT(puml, IsClassTemplate("A", "double,T")); + REQUIRE_THAT(puml, !IsClassTemplate("A", "long,U")); + REQUIRE_THAT(puml, IsClassTemplate("A", "long,T")); + REQUIRE_THAT(puml, IsClassTemplate("A", "long,bool")); + REQUIRE_THAT(puml, IsClassTemplate("A", "double,bool")); + REQUIRE_THAT(puml, IsClassTemplate("A", "long,float")); + REQUIRE_THAT(puml, IsClassTemplate("A", "double,float")); REQUIRE_THAT(puml, IsClassTemplate("A", "bool,std::string")); - REQUIRE_THAT(puml, IsClassTemplate("AString", "float")); - REQUIRE_THAT(puml, IsClassTemplate("AString", "int")); - REQUIRE_THAT(puml, IsClassTemplate("AString", "std::string")); + REQUIRE_THAT(puml, IsClass(_A("B"))); + REQUIRE_THAT( puml, !IsClassTemplate("std::std::function", "void(T...,int),int)")); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, !IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT( - puml, IsInstantiation(_A("A"), _A("AString"))); + puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT( - puml, IsInstantiation(_A("A"), _A("AString"))); - REQUIRE_THAT( - puml, !IsInstantiation(_A("AString"), _A("AString"))); + puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT(puml, - IsInstantiation(_A("A"), _A("AString"))); + IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, - !IsInstantiation( - _A("AString"), _A("AString"))); + IsInstantiation(_A("A>"), + _A("A>"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("A"), _A("A>"))); + + REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("B"), "+vps")); + REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("B"), "-bapair")); + REQUIRE_THAT( + puml, IsAggregation(_A("R"), _A("A"), "-aboolfloat")); + REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("A"), "-bapair")); + REQUIRE_THAT( + puml, IsAggregation(_A("R"), _A("A"), "-aboolfloat")); + REQUIRE_THAT( + puml, IsAssociation(_A("R"), _A("A"), "-afloat")); REQUIRE_THAT( puml, IsAggregation(_A("R"), _A("A"), "-boolstring")); - REQUIRE_THAT( - puml, IsAggregation(_A("R"), _A("AString"), "-floatstring")); - REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("B"), "#bs")); - REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("B"), "+bs2")); + REQUIRE_THAT(puml, + IsAggregation(_A("R"), _A("A>"), + "-floatstring")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t00019/test_case.h b/tests/t00019/test_case.h index cddbdb28..31654680 100644 --- a/tests/t00019/test_case.h +++ b/tests/t00019/test_case.h @@ -44,6 +44,16 @@ TEST_CASE("t00019", "[test-case][class]") IsBaseClass( _A("Layer2>"), _A("Layer1>>"))); + REQUIRE_THAT(puml, + IsAggregation(_A("A"), _A("Layer1>>"), "+layers")); + + REQUIRE_THAT( + puml, !IsAggregation(_A("A"), _A("Layer2>"), "+layers")); + + REQUIRE_THAT(puml, !IsAggregation(_A("A"), _A("Layer3"), "+layers")); + + REQUIRE_THAT(puml, !IsAggregation(_A("A"), _A("Base"), "+layers")); + save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); } diff --git a/tests/t00027/test_case.h b/tests/t00027/test_case.h index f073b50b..0fd2e9ff 100644 --- a/tests/t00027/test_case.h +++ b/tests/t00027/test_case.h @@ -36,14 +36,14 @@ TEST_CASE("t00027", "[test-case][class]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); REQUIRE_THAT(puml, IsAbstractClass(_A("Shape"))); REQUIRE_THAT(puml, IsAbstractClass(_A("ShapeDecorator"))); - REQUIRE_THAT(puml, IsClassTemplate("Line", "T<>")); - REQUIRE_THAT(puml, IsClassTemplate("Text", "T<>")); - REQUIRE_THAT(puml, IsInstantiation(_A("Line>"), _A("Line"))); + REQUIRE_THAT(puml, IsClassTemplate("Line", "T<>...")); + REQUIRE_THAT(puml, IsClassTemplate("Text", "T<>...")); + REQUIRE_THAT(puml, IsInstantiation(_A("Line...>"), _A("Line"))); REQUIRE_THAT( - puml, IsInstantiation(_A("Line>"), _A("Line"))); - REQUIRE_THAT(puml, IsInstantiation(_A("Text>"), _A("Text"))); + puml, IsInstantiation(_A("Line...>"), _A("Line"))); + REQUIRE_THAT(puml, IsInstantiation(_A("Text...>"), _A("Text"))); REQUIRE_THAT( - puml, IsInstantiation(_A("Text>"), _A("Text"))); + puml, IsInstantiation(_A("Text...>"), _A("Text"))); REQUIRE_THAT( puml, IsAggregation(_A("Window"), _A("Line"), "+border")); diff --git a/tests/test_config.cc b/tests/test_config.cc index d4316568..297ce04d 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -68,6 +68,27 @@ TEST_CASE("Test config simple", "[unit-test]") contains(diagram.include().relationships, relationship_t::kOwnership)); CHECK(contains(diagram.exclude().relationships, relationship_t::kNone)); + + CHECK(diagram.relationship_hints().at("std::vector").get(0) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("std::tuple").get(10) == + relationship_t::kAggregation); + CHECK(diagram.relationship_hints().at("std::map").get(0) == + relationship_t::kNone); + CHECK(diagram.relationship_hints().at("std::map").get(1) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("std::shared_ptr").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("std::weak_ptr").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("std::unique_ptr").get(0) == + relationship_t::kAggregation); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(2) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(10) == + relationship_t::kAggregation); } TEST_CASE("Test config inherited", "[unit-test]") diff --git a/tests/test_config_data/simple.yml b/tests/test_config_data/simple.yml index d6a44955..332fb1fa 100644 --- a/tests/test_config_data/simple.yml +++ b/tests/test_config_data/simple.yml @@ -33,4 +33,14 @@ diagrams: - dependency exclude: relationships: - - none \ No newline at end of file + - none + relationship_hints: + std::vector: composition + std::map: + default: none + 1: composition + std::tuple: aggregation + ns1::n2::some_template: + default: association + 2: composition + 10: aggregation diff --git a/tests/test_util.cc b/tests/test_util.cc index 0901000a..01bede11 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -99,9 +99,9 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") int_template_str, [](const auto &n) { return n; }); CHECK(int_template.size() == 1); - CHECK(int_template[0].template_params_.size() == 1); + CHECK(int_template[0].template_params().size() == 1); CHECK(int_template[0].type() == "ns1::ns2::class1"); - CHECK(int_template[0].template_params_[0].type() == "int"); + CHECK(int_template[0].template_params()[0].type() == "int"); const std::string int_int_template_str{"ns1::ns2::class1"}; @@ -109,10 +109,10 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") int_int_template_str, [](const auto &n) { return n; }); CHECK(int_int_template.size() == 1); - CHECK(int_int_template[0].template_params_.size() == 2); + CHECK(int_int_template[0].template_params().size() == 2); CHECK(int_int_template[0].type() == "ns1::ns2::class1"); - CHECK(int_int_template[0].template_params_[0].type() == "int"); - CHECK(int_int_template[0].template_params_[1].type() == "int"); + CHECK(int_int_template[0].template_params()[0].type() == "int"); + CHECK(int_int_template[0].template_params()[1].type() == "int"); const std::string nested_template_str{ "class1>>"}; @@ -121,13 +121,13 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") nested_template_str, [](const auto &n) { return n; }); CHECK(nested_template.size() == 1); - CHECK(nested_template[0].template_params_.size() == 2); + CHECK(nested_template[0].template_params().size() == 2); CHECK(nested_template[0].type() == "class1"); - CHECK(nested_template[0].template_params_[0].type() == "int"); - const auto &class2 = nested_template[0].template_params_[1]; + CHECK(nested_template[0].template_params()[0].type() == "int"); + const auto &class2 = nested_template[0].template_params()[1]; CHECK(class2.type() == "ns1::class2"); - CHECK(class2.template_params_[0].type() == "int"); - CHECK(class2.template_params_[1].type() == "std::vector"); - CHECK( - class2.template_params_[1].template_params_[0].type() == "std::string"); + CHECK(class2.template_params()[0].type() == "int"); + CHECK(class2.template_params()[1].type() == "std::vector"); + CHECK(class2.template_params()[1].template_params()[0].type() == + "std::string"); } diff --git a/thirdparty/cppast b/thirdparty/cppast index 27328473..6db9daf5 160000 --- a/thirdparty/cppast +++ b/thirdparty/cppast @@ -1 +1 @@ -Subproject commit 273284739d8cb5c49e3c3a5dc13b8dcc01a3ffdc +Subproject commit 6db9daf5d2fa161855f70ad3deea0873f53e195f