This paper details the best practices for using ccdoc based on feedback from users over the years. They are listed below:
In the following sections, each practice is presented a description and one or more examples.
1. General Guidelines |
Description
- Document 'HOW' not 'WHAT'.
Program documentation is most useful for users when they are told how it is meant to be used. When writing documentation think about what the user would be likely to ask you. For example, a user rarely cares about the design decisions that lead to the development of a class whereas they are very concerned with when and how to use it, which constructor should they call and which operations make sense. Here are some additional rules of thumb:
- Provide working code examples when possible. This is especially important for global functions and classes.
- Provide links to related classes and functions using the @see and {@link ...} directives.
- Provide links to related external documentation using the HTML anchor directive.
- Always provide the author (@author) for classes, global functions, global variables, global enumerations and global typedefs so that users know who to contact to get additional information.
When there are multiple authors, list them chronologically from most recent to earliest. That is the first @author statement should be the most recent author and the last @author statement should be the least recent.
- Always provide the version (@version) for classes, global functions, global variables, global enumerations and global typedefs so that users know who to contact to get additional information.
Use a revision control tag that is recognized by your revision control system for the @version when possible. This allows the automation of revision control information. For example, I generally specify my versions as use "@version $Id: best_practices.htm,v 1.3 2003/02/19 14:52:27 jlinoff Exp $" because I use the CVS revision control system.
- The user should not have to scan other source code to figure out how to do things. This leads to bad practices being propagated.
- Document return values (@returns). Be especially careful to document the conditions under which a NULL value is returned for functions/methods that return pointers.
- Document boundary conditions for function/method arguments (@param).
- Document exceptions that can be thrown by a function/method (@exception).
- Document how a user is supposed to alter their code when specifying a deprecated (@deprecated) entity.
- Document namespaces in a single place to ease maintenance. After r27, ccdoc will correctly recognize which namespace entities are documented and only use them (you can override this behavior using the -rptcfuns switch).
- Use the ///< or //!< form for single line comments.
Poor Practice Example
This example shows an example of a documented class that really doesn't help the user much:
/** * This is a directed, a cyclic graph class. * For more information on directed, acyclic graphs see the * text book "Graph Theory" by Professor S Obtuse. * This was developed to allow us to experiment with different * graph algorithms. * @author author1 * @version $Id: best_practices.htm,v 1.6 2003/02/27 05:02:51 jlinoff Exp $ * @see Node * @see Arc */ class GraphBest Practice Example
As you can see, the previous documentation clearly describes what the class is but it really doesn't do much to help the user. I think that the following is much better:
/** * This is a directed, a cyclic graph class. * For more information on directed, acyclic graphs see the * text book "Graph Theory" by Professor S Obtuse. * This was developed to allow us to experiment with different * graph algorithms. * * The example below shows how to create a simple graph and * then use the node and arc counting operations. The graph has * three nodes that are fully connected. *<blockquote> *<pre> *@@ #include "Graph/export/Graph.h" *@@ int main() *@@ { *@@ Graph* graph = new Graph("g1"); *@@ *@@ // Create the top nodes. *@@ graph->AddNode("N1"); *@@ graph->AddNode("N2"); *@@ graph->AddNode("N3"); *@@ *@@ // Fully connect them. *@@ graph->Connect("N1","N2"); // N1 -> N2 *@@ graph->Connect("N1","N3"); // N1 -> N3 *@@ graph->Connect("N2","N1"); // N2 -> N1 *@@ graph->Connect("N2","N3"); // N2 -> N3 *@@ graph->Connect("N3","N1"); // N3 -> N1 *@@ graph->Connect("N3","N2"); // N3 -> N2 *@@ *@@ // Count the nodes and arcs. *@@ assert( graph->GetNumNodes() == 3 ); *@@ assert( graph->GetNumArcs() == 6 ); // 3! *@@ *@@ // Clean up *@@ delete graph; *@@ *@@ return 0; *@@ } *</pre> *</blockquote> * The graph class also provides more complex algorithms and * heuristics for finding shortest and longest paths, partitions * etc. * * The graph class does not support hierarchy. * @author author1 * @version $Id: best_practices.htm,v 1.6 2003/02/27 05:02:51 jlinoff Exp $ * @see Node * @see Arc */As you can see, it provides a wealth of information about how to use this object. If a programmer sees this they will immediately have an idea of how to use this class. With the previous description they would have to call the author or look at code examples.
2. Code Examples |
Description
When documenting simple source code use one of the following paradigms to ensure maximum readability:
/** * Code example. *<blockquote> *<pre> *@@ ... *</pre> *</blockquote> */or
/** * Code example. *<blockquote> *<code> *@@ ... *</code> *</blockquote> */The <blockquote> tag is used to indent the region and the <pre> or <code> tag defines a fixed width font so that code looks more like it does in an editor. The ccdoc '@@' directive tells ccdoc to automatically convert < to < > to > and & to & to avoid confusing the HTML parser.
Do not use the <dir> tag. It does not produce readable results on most browsers.
When documenting source code that you want to embed to HTML directives in make sure that you manually convert the <, > and & characters.
Poor Practice Example
In this example, the user want to document source code with extra formatting but forgot to manually convert the <, > and & characters. This fragment will produce very strange results in HTML.
/** * Code example with highlights that doesn't work at all: *<blockquote> *<pre> * for( ; i<10 && i>0; ++i ) * cout << <b>fct(i)</b> << endl; *</pre> *</blockquote> */Best Practice Example
The above example produces unexpected results because the HTML parser tries to interpret '<10 && i>' as a tag with unexpected results. Changing this to the following would help but it would not fix the problem because "fct" would not show up in bold:
/** * Code example with highlights that doesn't works better: *<blockquote> *<pre> *@@ for( ; i<10 && i>0; ++i ) *@@ cout << <b>fct(i)</b> << endl; *</pre> *</blockquote> */In fact, "fct" shows up as "<b>fct</b>" in the HTML. This is very useful for documenting HTML tags but not helpful for code examples. To fix this, change it to the following:
/** * Code example with highlights that works: *<blockquote> *<pre> *@@ for( ; i<10 && i>0; ++i ) * cout << <b>fct(i)</b> >> endl; *</pre> *</blockquote> */Note that I kept the '@@' on the first line because there were no HTML tags embedded there and the '@@' directive is more convenient than substituting for the <, > and & characters.
3. Ordered Lists |
Description
When documenting ordered lists always use the HTML <ol> tag for maximum readability as follows:
/** * There are 4 things in the list: *<ol> *<li>One fish *<li>Two fish *<li>Red fish *<li>Blue fish *</ol> */This recommendation is made because large list entries that are entered use the <pre> tag wrap in strange ways when users resize their windows.
Poor Practice Example
In this example, the user documents a list with using the HTML <ol> tag.
/** * Here is a list with a long entry that will be hard to read under * certain conditions: *<pre> * 1. This really looks easy to read when you type it in but * for users it is a real disaster if their browser window * is too small. *</pre> */Best Practice Example
The above example is very hard to read when the users browser window is too small. Using the <ol> tag makes it much easier as shown below:
/** * Here is a list with a long entry that will be hard to read under * certain conditions: *<ol> *<li>This is easy to read when you type it in and it is easy * for users read. *</ol> */
4. Unordered Lists |
Description
When documenting unordered lists always use the HTML <ul> tag for maximum readability as follows:
/** * There are 4 things in the list: *<ul> *<li>One fish *<li>Two fish *<li>Red fish *<li>Blue fish *</ul> */This recommendation is made because large list entries that are entered use the <pre> tag wrap in strange ways when users resize their windows.
Poor Practice Example
In this example, the user documents a list with using the HTML <ol> tag.
/** * Here is a list with a long entry that will be hard to read under * certain conditions: *<pre> * 1. This really looks easy to read when you type it in but * for users it is a real disaster if their browser window * is too small. *</pre> */Best Practice Example
The above example is very hard to read when the users browser window is too small. Using the <ol> tag makes it much easier as shown below:
/** * Here is a list with a long entry that will be hard to read under * certain conditions: *<ul> *<li>This is easy to read when you type it in and it is easy * for users read. *</ul> */
5. Tables |
Description
There are two recommendations for tabular data: use the <pre> tag and format the data or use the <table> tag. The <pre> tag should be used for small, simple tables that aren't likely to have fields that wrap. The <table> tag should be used for complex tables.
The first example below shows how to use the <pre> paradigm.
/** * This enumerated type has three values as shown in * the table below: *<pre> * Enumeration Description * =========== ========================= * RED This is a red hue value. * GREEN This is a green hue value. * BLUE This is a blue hue value. *</pre>As you can see, the entries are very small and are not likely to wrap. They are also easy to read in the code.
The following example is more complex because it uses anchors and extra text.
/** * This enumerated type describes the currently active * algorithm. *<table border=1> *<tr> * <th>Algorithm</th> * <th>Author</th> * <th>Brief Description</th> *</tr> *<tr> * <td>Annealing Placement</td> * <td><a href="mailto:foobar@xilinx.com">F. Bar</a> * <td>Huston annealing algorithm.</td> *</tr> *<tr> * <td>Constructive Placement 1</td> * <td><a href="mailto:smart1@xilinx.com">A. Smart1</a> * <td>An older constructive placement algorithm that * is faster than annealing placement with slightly * poorer results.</td> *</tr> *<tr> * <td>Constructive Placement 2</td> * <td><a href="mailto:smart1@xilinx.com">A. Smart1</a> * <td>The latest and greatest constructive placement algorithm.</td> *</tr> *</table> */It would be difficult to produce readable output using the <pre> paradigm.
6. Reuse comments for derived virtual methods |
Description
Many times derived virtual methods have exactly the same function as their base counterparts. When this is the case it is recommended that you reuse the prior comment by specifying the @link directive to save typing and to improve maintainability.
Poor Practice Example
In this example, the user re-documents an existing virtual function: is_in_db().
struct A { /** * This method reports whether the specified name is * in the database. * @param name The name to search for. It can * any of the following formats: * <ul> * <li>Last, First MI * <li>First MI Last * <li>First Last * </ul> * @returns True if the name was found or false otherwise. * If the name is NULL, false is returned. */ virtual bool is_in_db(const char* name) const; };struct B : public A { /** * This method reports whether the specified name is * in the database. * @param name The name to search for. It can * any of the following formats: * <ul> * <li>Last, First MI * <li>First MI Last * <li>First Last * </ul> * @returns True if the name was found or false otherwise. * If the name is NULL, false is returned. */ virtual bool is_in_db(const char* name) const; };Best Practice Example
The problem with the previous example is maintenance. Anytime the behavior changes, both methods have to be updated. It is better to simply reference the original function unless you are really doing something different. The following example illustrates this:
struct A { /** * This method reports whether the specified name is * in the database. * @param name The name to search for. It can * any of the following formats: * <ul> * <li>Last, First MI * <li>First MI Last * <li>First Last * </ul> * @returns True if the name was found or false otherwise. * If the name is NULL, false is returned. */ virtual bool is_in_db(const char* name) const; }; struct B : public A { /** See {@link A::is_in_db}. */ virtual bool is_in_db(const char* name) const; };
7. Verify the output format |
Description
Before publishing ccdoc comments test them locally as shown below. Note that the first step is to manually verify that you are using the correct version of ccdoc.
% ccdoc -version % rm -rf ccdoc % mkdir ccdoc % ccdoc -db ccdoc/ccdoc.db -pkg test export/*.h % ccdoc -db ccdoc/ccdoc.db -index % ccdoc -db ccdoc/ccdoc.db -norptim -rptmac -rptfwcf -html ccdoc/ % <view in the browser of your choice>
8. Create internal documentation for developers |
Description
Ccdoc can be useful in training new subsystem developers and for reminding existing developers of whats there. I recommend creating a ccdoc target in your development makefile that generates documentation about all of the external and internal classes.
This is generally different than the documentation generated for users because it can include headers files that are not exported.
9. Hide include guards |
Description
When documenting header files, always hide the include guards unless there is a compelling reason to leave them. They are an unnecessary distraction for most users.
Another option for hiding include guards is to use the -rptmac1 switch. This will automatically ignore macros with the following name characteristics:
- Prefixes
- dll_,DLL_
- include_,INCLUDE_
- included_,INCLUDED_
- Suffixes
- _dll,_DLL
- _h,_H
- _hh,_HH,
- _include,_INCLUDE
- _included,_INCLUDED
- _included_,_INCLUDED_
This is especially useful for systems where you cannot change some of the header files.
Poor Practice Example
In this example, the user does not hide the include guard and doesn't document it leading to documentation clutter.
// Header file for stuff. #ifndef my_stuff_h #define my_stuff_h ... #endifBetter Practice Example
In this example, the user does not hide the include guard but documents it. Although this may still lead to documentation clutter, the user will be able to understand what it is.
// Header file for stuff. #ifndef my_stuff_h /** * Include guard for my_stuff.h. * @author Ima Programmer * @version $Id: best_practices.htm,v 1.6 2003/02/27 05:02:51 jlinoff Exp $ */ #define my_stuff_h ... #endifBest Practice Example
In this example, the user hides the include guard to avoid documentation clutter.
// Header file for stuff. #ifndef my_stuff_h #ifndef __ccdoc__ #define my_stuff_h #endif ... #endifAnother best practice is to not hide the include guard but use the -rptmac1 switch.
// Header file for stuff. #ifndef my_stuff_h // No need for a ccdoc guard because the // -rptmac1 switch is used. #define my_stuff_h ... #endif
10. Namespaces |
Description
When documenting the same namespace distributed over different files, choose one as the primary source and only document that one.
Poor Practice Example
In this example, the user does not document any of the Stuff namespace instances.
// file1.h namespace Stuff { int a; } // file2.h namespace Stuff { int b; }Better Practice Example
In this example, the user documents all of the Stuff namespace instances. This is better than the previous example because the user can at least determine who the author is.
// file1.h /** * This namespace does stuff. * @author A. Programmer. * @version 1.0 */ namespace Stuff { int a; } // file2.h /** * This namespace does stuff. * @author A. Programmer. * @version 1.0 */ namespace Stuff { int b; }Best Practice Example
In this example, the user documents one of the Stuff namespace instances. This is better than the previous example because it reduces documentation clutter in the ccdoc output.
// file1.h /** * This namespace does stuff. * @author A. Programmer. * @version 1.0 */ namespace Stuff { int a; } // file2.h // This is documented in file1.h namespace Stuff { int b; }
11. Templates |
Description
When documenting templates, always specify the parameter information.
Poor Practice Example
In this example, the user does not document the template at all.
template<class T1,class T2=StdAllocator> class MyArray { public: MyArray(); ~MyArray(); };Better Practice Example
In this example, the user documents the template like a class.
/** * A simple vector like collection template. * @author Joe Linoff * @version $Id: best_practices.htm,v 1.6 2003/02/27 05:02:51 jlinoff Exp $ */ template<class T1,class T2=StdAllocator> class MyArray { public: /** * Default constructor. */ MyArray(); /** * Destructor. */ ~MyArray(); };Best Practice Example
In this example, the user documents the template and its parameters.
/** * A simple vector like collection template. * @param T1 The collection entry type. * @param T2 The allocator. The default is the standard allocator. * @author Joe Linoff * @version $Id: best_practices.htm,v 1.6 2003/02/27 05:02:51 jlinoff Exp $ */ template<class T1,class T2=StdAllocator> class MyArray { public: /** * Default constructor. */ MyArray(); /** * Destructor. */ ~MyArray(); };