Thread: RE: [Doxygen-users] Class collaboration
Brought to you by:
dimitri
From: Prikryl,Petr <PRI...@sk...> - 2001-09-14 06:06:53
|
Dimitri wrote: > Anthony Yuen wrote: > > > > [...] In the class collaboration diagram that is generated, > > I think something is not quite right. Let me give an example: > > > > class A { /* some methods */ }; > > class B { std::vector<A *> vec; }; > > > > [...] I'm interested to know if this is the intended behaviour. If > > so, what is the reason behind? If not, maybe this is a bug? > > Currently this is intended behaviour, but I would like > to hear what other people think about this. Should > template arguments, such as in the example above, be > visualised in collaboration diagrams? and if so, how? > [...] Shortly, I think that this behaviour is correct. No so shortly, I feel that it is more philosophic problem than the technical one (I may be wrong). So I would reformulate the question: "Should only direct relations be considered in the collaboration diagram?" If the answer is no, then main() (for example) is a function that is indirectly related to almost everything in the program -- except the part hidden in implementation parts (.c, .cpp). If I use here some object of the class that hides inside the whole functionality of the application, then the collaboration graph of that class would be very complex and it would contain almost every class of the application. (Well, this is rather simplified, but I hope you understand the point.) If the above were accepted, then the only reasonable solution would be to define the maximum level of indirect relations. Say, 1 (one) is the level when we think only about direct relations. Now, std::vector<A> is a vector of instances of class A. The mentioned example of std::vector<A *> is vector of pointers to such instances. Even when you know how std::vector<whatever> may look like inside---from the abstract point of view---you should agree that the "whatever" is hidden inside something lager. If "vec" is the object of that class, then "whatever" is somehow hidden inside. So, I would mark the level of collaboration between B and "whatever" using at least value 2. Moreover, if the "whatever" is a pointer to "something-else", I would mark the level of collaboration between B and "something-else" using at least the value 3. Even more serious problem is that the argument of any template is used textually inside the body of the template. This also means that you do not exactly know how it is used. To conclude, I guess that generally it would be very difficult to build the collaboration diagram correctly if the template arguments should be followed. On the other hand, the life is not only that much scientific. It could probably be useful to include some internal knowledge about standard containers into doxygen. But then, you have to know whether "vector<something>" means _some_ vector or std::vector<>, etc. In other words, the parser would be probably more complicated. So, the final question is: "Is the result of a solution of the problem worth of the complexity of the solution?" With regards, Petr -- Petr Prikryl, SKIL, spol. s r.o., pri...@sk... |
From: Schreib, D. VTC-B. <dir...@vo...> - 2001-09-14 06:38:21
|
> > In the > > class collaboration diagram that is generated, I think something is > > not quite right. Let me give an example: > > > > class A { /* some methods */ }; > > > > class B { std::vector<A *> vec; }; > > > > I'm just using the std::vector as an example. It can > be any kind of > > (STL) container. Even though this strongly suggests that class B > > somehow uses class A for "something" (in my case, B *does* > make use of > > A, which is why B stores a vector of pointers to A), > Doxygen does not > > conclude that there is collaboration between A and B. This can be > > extended to include the use of smart pointers stored in a container. > > > > I'm interested to know if this is the intended > behaviour. If so, > > what is the reason behind? If not, maybe this is a bug? > > Currently this is intended behaviour, but I would like to > hear what other > people think about this. Should template arguments, such as > in the example > above, be visualised in collaboration diagrams? and if so, > how? Does UML cover these kind of relations? Take a look at Together C++ / Control Center. This tool visualizes it as an one-to-many relationship from B to A. (and an aggregation, if you remove the pointer). Dirk --------------------------------------------------------- This Mail has been checked for Viruses Attention: Encrypted mails can NOT be checked! ** Diese Mail wurde auf Viren geprueft Hinweis: Verschluesselte mails koennen NICHT auf Viren geprueft werden! --------------------------------------------------------- |
From: Prikryl,Petr <PRI...@sk...> - 2001-09-14 12:35:05
|
Hi Kris, I agree that adding some extra command to manually mark the relation between the classes would be fine. Majority probably do not need some general solution of that kind. Those who need to capture the relationship of that special kind would probably agree to insert it manually. I cannot argue here. I belong to the majority ;-) Petr -- Petr Prikryl, SKIL, spol. s r.o., pri...@sk... > -----Original Message----- > From: Kris Thielemans [SMTP:kri...@ic...] > Sent: Friday, September 14, 2001 12:26 PM > To: dox...@li... > Subject: RE: [Doxygen-users] Class collaboration > > Dear Petr, > > I agree with you. It seems impossible to do this without knowing what > std::vector is. One could argue this is easy to do, but what about by own > class Array. Can doxygen know it's also a container class? Too hard. > > > > > [...] In the class collaboration diagram that is generated, > > > > I think something is not quite right. Let me give an example: > > > > > > > > class A { /* some methods */ }; > > > > class B { std::vector<A *> vec; }; > > > > "Is the result of a solution of the problem worth of the > > complexity of the solution?" > > So, I don't think anyone should try to build this in in the parser. > However, > it might be useful to have an extra doxygen command (one more!) to force a > class to be put in the collaboration diagram. That would solve this > conundrum I guess. [...] |
From: Anthony Y. <an...@ac...> - 2001-09-14 14:33:20
|
I'm a rather lazy person and that's the whole reason I'm using a tool such as Doxygen to automatically generate some nice reference manuals from my messy source code. :) But I agree that it would be nice to have the option when you REALLY need it! But most of the time, I would avoid doing anything "manually" when writing "manuals". :) Happy Doxygening! I'm quite impressed by the quality of the generated materials by Doxygen. I'm a happy users (with some minor complaints/whines/rants). :) -----Original Message----- From: dox...@li... [mailto:dox...@li...] On Behalf Of Prikryl,Petr Sent: Friday, 14 September, 2001 09:32 To: dox...@li... Subject: RE: [Doxygen-users] Class collaboration Hi Kris, I agree that adding some extra command to manually mark the relation between the classes would be fine. Majority probably do not need some general solution of that kind. Those who need to capture the relationship of that special kind would probably agree to insert it manually. I cannot argue here. I belong to the majority ;-) Petr -- |
From: Kris T. <kri...@ic...> - 2001-09-14 10:29:16
|
Dear Petr, I agree with you. It seems impossible to do this without knowing what std::vector is. One could argue this is easy to do, but what about by own class Array. Can doxygen know it's also a container class? Too hard. > > > [...] In the class collaboration diagram that is generated, > > > I think something is not quite right. Let me give an example: > > > > > > class A { /* some methods */ }; > > > class B { std::vector<A *> vec; }; > > > > > "Is the result of a solution of the problem worth of the > complexity of the solution?" > So, I don't think anyone should try to build this in in the parser. However, it might be useful to have an extra doxygen command (one more!) to force a class to be put in the collaboration diagram. That would solve this conundrum I guess. All the best, Kris Thielemans Imaging Research Solutions Ltd Cyclotron Building Hammersmith Hospital Du Cane Road London W12 ONN, United Kingdom web site address: http://www.irsl.org/~kris |
From: Anthony Y. <an...@ac...> - 2001-09-14 14:26:09
|
Let me take the discussion a little further since I started it... A fair warning, it is a bit long... [snip] > "Should only direct relations be considered in the > collaboration diagram?" My answer to this question is YES. But it would be helpful to define exactly what "direct relation" is. I'll quote from the "graph legend": "A purple dashed arrow is used if a class is contained or used by another class. The arrow is labeled with the variable(s) through which the pointed class or struct is accessible." So, in fact, keep in mind that Doxygen already considers (plain) pointers to another class as a collaboration. This means the "level of indirect relation" is 2 (see below). Then why does Doxygen put a "purple dashed arrow" if there is a "pointed class" inside another class? Let's examine a possible reason for that. It is clear what "a class is contained" means. It means that: if class B has an instance member variable to class A, then we say "class A is contained in B". Should a pointer to class A count as "contained in" too? No, I don't think so. Even if sizeof(A) changes, sizeof(B) doesn't, because B only contains a pointer (or reference). This is a proof that class A is *not* contained in class B. DEFINITION: Class A is CONTAINED in class B if and only if class B has an instance (not pointer or reference) member variable to class A. This leaves us the second way to get a "purple dashed arrow", which is when class A is "used" by class B. But what is "used"? How do you define "used"? Here is a reasonable definition: DEFINITION: Class A is USED-BY class B if and only if in some member functions of class B invokes a member function of class A. (I say member function, because we make all our member variables private, right?) Obviously, this includes (public, protected, private) inheritance relationships, since a derived class, at the minimum, must invoke the base class constructor(s) and destructor. Doxygen handles these separately. Exploring further, we see that if class B invokes a public member function of class A, then class A is USED-BY class B. How is this possible? There are 3 ways in C++: instance, pointer, reference. struct A { void Foo(void){} }; CASE I (Instance) 1) class B { A a; void Bar(void) {a.Foo();} }; 2) class B { A a[10]; void Bar(void) {a[0].Foo();} }; // ?? 3) class B { void Bar(A a) {a.Foo();} }; CASE II (Pointer) 1) class B { A *a; void Bar(void) {a->Foo();} }; 2) class B { void Bar(A *a) {a->Foo();} }; CASE III (Reference) 1) class B { A &a; void Bar(void) {a.Foo();} }; 2) class B { void Bar(A &a) {a.Foo();} }; We need to be careful in handling cases II and III, because B does not necessarily invoke any public member function of A... However, in reality, Doxygen simply concludes that class A is USED-BY Class B in cases II.1 and III.1 It does not consider II.2 or III.2 Perhaps it should? Should a friend class be considered a collaboration? Sure. Otherwise, the two classes shouldn't be friends in the first place. The reason you made class B a FRIEND to class A is so that B can invoke protected or private member functions of A... Doxygen does not handle this case either. In conclusion, what is a "direct relation"? DEFINITION: Class A is DIRECTLY-RELATED to class B if and only if class A is CONTAINED in class B, or class A is USED-BY class B, or class B is a FRIEND to class A. The above is my humble opinion on what a "direct relation" is. It is by no means exhaustive or scientifically rigorous. >If the answer is no, then main() (for example) is a >function that is indirectly related to almost everything >in the program -- except the part hidden in >implementation parts (.c, .cpp). If I use here some >object of the class that hides inside the whole >functionality of the application, then the collaboration graph >of that class would be very complex and it would contain almost >every class of the application. (Well, this is rather. >simplified, but I hope you understand the point.) Yes, I see a problem here, if the "level of indirect Relation" is allowed to be "infinity". >If the above were accepted, then the only reasonable >solution would be to define the maximum level of indirect >relations. Say, 1 (one) is the level when we think only >about direct relations. Agreed, if you consider my definition of a direct relation. >Now, std::vector<A> is a vector of instances of class A. >The mentioned example of std::vector<A *> is vector of >pointers to such instances. Even when you know how >std::vector<whatever> may look like inside---from the abstract >point of view---you should agree that the "whatever" is hidden >inside something lager. If "vec" is the object of that class, then >"whatever" is somehow hidden inside. So, I would mark >the level of collaboration between B and "whatever" using >at least value 2. Moreover, if the "whatever" is a >pointer to "something-else", I would mark the level of >collaboration between B and "something-else" using at >least the value 3. I agree that "whatever" is hidden. But if class B invokes a public member function of class A, through the vector or some other kind of container, then class A is USED-BY class B. Another way of "justifying" a collaboration between A and B is that, std::vector<A> (or std::vector<A *>) is a type that depends on A. So A is USED-BY B to create a new type. Hence, we have a CASE IV: CASE IV (Template) 1) template <typename T> class B {}; B<A> b; 2) class B { template <typename T> void Bar(void){} }; B b; b.Bar<A>(); 3) class B { some_template<A> t_a; }; Of course, there are many variations on this theme, but I hope you get the idea. This CASE IV is what complicates things by many orders of magnitudes. And I wouldn't want to be the person to try to solve it. :) >Even more serious problem is that the argument of any >template is used textually inside the body of the >template. This also means that you do not exactly know >how it is used. We know how it is used -- to create a new type using the template! > To conclude, I guess that generally it would be very >difficult to build the collaboration diagram correctly if >the template arguments should be followed. Agreed. May be this is an NP-Hard problem. :) >On the other hand, the life is not only that much >scientific. It could probably be useful to include some >internal knowledge about standard containers into doxygen. >But then, you have to know whether "vector<something>" >means _some_ vector or std::vector<>, etc. In other words, >the parser would be probably more complicated. Agreed, so I "forgive" Doxygen for not handling the CASE IV. But, still, what about CASE II.2 and III.2? >So, the final question is: > > "Is the result of a solution of the problem worth of the > complexity of the solution?" Probably not... Oh well, life is full of tradeoff's. :) It has been fun typing this message. Thank you for your time if you made it this far. |
From: Kris T. <kri...@ic...> - 2001-09-14 15:07:39
|
This is fun indeed. Maybe not practical, but ok... > DEFINITION: > Class A is CONTAINED in class B if and only if class B has > an instance (not pointer or reference) member variable to class A. > fine for me > DEFINITION: > Class A is USED-BY class B if and only if in some member > functions of class B invokes a member function of class A. > > (I say member function, because we make all our member variables > private, right?) > fine for me as well. Except that I would include member variables in the definition. That way, you wouldn't have to add anything specific for friends below (if it's a friend, but doesn't use any of its members, there wasn't a point in having it a friend anyway). Here's one practical problem for doxygen: to decide on this, it would have to parse the actual C++ code, and in particular the implementations of the member functions. I don't think doxygen does this, and I don't think it would be easy. Sounds much more like real compilation to me. In particular, for nearly all purposes, you can get away with doxygening only the .h files (except of you put doxygen comments in .cxx). > struct A { void Foo(void){} }; > > CASE I (Instance) > 1) class B { A a; void Bar(void) {a.Foo();} }; > 2) class B { A a[10]; void Bar(void) {a[0].Foo();} }; // ?? > 3) class B { void Bar(A a) {a.Foo();} }; > CASE II (Pointer) > 1) class B { A *a; void Bar(void) {a->Foo();} }; > 2) class B { void Bar(A *a) {a->Foo();} }; > CASE III (Reference) > 1) class B { A &a; void Bar(void) {a.Foo();} }; > 2) class B { void Bar(A &a) {a.Foo();} }; > > We need to be careful in handling cases II and III, because > B does not necessarily invoke any public member function of A... > However, in reality, Doxygen simply concludes that class A is USED-BY > Class B in cases II.1 and III.1 It does not consider II.2 or III.2 > Perhaps it should? > I.2 seems an obvious candidate as well > > DEFINITION: > Class A is DIRECTLY-RELATED to class B if and only if class > A is CONTAINED in class B, or class A is USED-BY class B, or class > B is a FRIEND to class A. > fine for me. > CASE IV (Template) tough and so on. But I'm afraid that if you don't handle it, a lot of my code will not see any benefit if all this. > > "Is the result of a solution of the problem worth of the > > complexity of the solution?" > > Probably not... Oh well, life is full of tradeoff's. :) > if you ask me, it means that 'collaboration diagrams' are very diffiuclt to make sensible. In such a case, it is better to disable them in my opinion. Incomplete doc is more confusing than no doc. In any case, I can get so graphs in my doc from doxygen nowadays that it all looks stunning enough already! > It has been fun typing this message. Thank you for > your time if you made it this far. you're welcome. Kris Thielemans Imaging Research Solutions Ltd Cyclotron Building Hammersmith Hospital Du Cane Road London W12 ONN, United Kingdom web site address: http://www.irsl.org/~kris |