Sign in to follow this  

Trying doxygen comments, need tips [SOLVED]

This topic is 3459 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to use doxygen-style comments in my C++ source code. So far, I've just been trying to emulate the style found in other people's comments that I've read. Can anyone who has worked with doxygen offer some advice, and possible improvements to my comments? Thanks.
 
/**
 * @file 
 *
 * Contains the Vector class template.
 *
 * (license)
 */


#ifndef SMOOTHE_VECTOR_TEMPLATE_HPP
#define SMOOTHE_VECTOR_TEMPLATE_HPP


#include <iostream>

#include "smoothe.hpp"


namespace smoothe
{

/**
 * @class Vector
 * 
 * An n-dimensional vector, for representing 2D, 3D, and 4D data. 
 * 
 * Though it can work with any integral type, this class is intended for floating
 * point types (e.g. fixed, half, float, double)
 */
template< typename T, size_t Size >
class Vector
{
public:
    typedef T value_type;

    // --------------------------------
    // Constructors
    // --------------------------------
    /**
     * @brief Default constructor. 
     *
     * @note Doesn't perform any initialization, for sake of efficiency.
     */
    Vector() {}

    /**
     * @brief Copy constructor.
     */
    Vector( const Vector& other );

    /**
     * Copies data from an array.
     *
     * @note Doesn't perform any size checking, so an array of fewer elements
     *       won't cause an error, but will cause strange results.
     *
     * @param dataPtr Array of same type, and greater or equal size, as Vector.
     */
    Vector( const T* dataPtr );

    /**
     * Initializes all entries in Vector's member array to a single value.
     *
     * @note Passing integer zero causes ambiguity with Vector( const T* dataPtr ).
     *       Safest to cast all constant values to the same type as the Vector.
     *
     * @param value Any valid number value for the type of the Vector.
     */
    Vector( T value );

    // --------------------------------
    // Accessors/Mutators
    // --------------------------------
    /**
     * @return The size of the Vector specified by its template parameter.
     */
    size_t getSize() const;


    /**
     * Sets all entries in Vector's member array to a single value.
     *
     * @note Passing integer zero causes ambiguity with setValue( const T* dataPtr ).
     *       Safest to cast all constant values to the same type as the Vector.
     *
     * @param value Any valid number value for the type of the Vector.
     *
     * @return Reference to this Vector, after setting the new values.
     */
    Vector& setValue( T value );

    /**
     * Copies data from an array.
     *
     * @note Doesn't perform any size checking, so an array of fewer elements
     *       won't cause an error, but will cause strange results.
     *
     * @param dataPtr Array of same type, and greater or equal size, as Vector.
     *
     * @return Reference to this Vector, after setting the new values.
     */
    Vector& setValue( const T* dataPtr );


    // --------------------------------
    // Casting operators
    // --------------------------------
    /**
     * @brief Casting operator
     *
     * Causes casts of the same type as the vector to gain access to the private
     * array data for sake of copying and editing.
     *
     * @return Pointer to the array member of the Vector
     */
    operator T*();

    /**
     * @brief Casting operator
     *
     * Causes casts of the same type as the vector to gain access to the private
     * array data for sake of copying and editing.
     *
     * @return Const pointer to the array member of the Vector
     */
    operator const T*() const;


    // --------------------------------
    // Accessor operators
    // --------------------------------
    /**
     * @brief Accessor operator
     *
     * Used for accessing private array entries, without exposing them.
     *
     * @note This operator was chosen for consistency with Matrix class two index
     *       accessor. Also chosen since [] operator caused ambiguity errors.
     *
     * @return Reference to an entry in the member array.
     *
     */
    T& operator ()( size_t index );

    /**
     * @brief Accessor operator
     *
     * Used for accessing private array entries, without exposing them.
     *
     * @note This operator was chosen for consistency with Matrix class two index
     *       accessor. Also chosen since [] operator caused ambiguity errors.
     *
     * @return Const reference to an entry in the member array.
     *
     */
    const T& operator ()( size_t index ) const;


    // --------------------------------
    // Swizzling/Smearing operators
    // --------------------------------
    /**
     * @brief Swizzling/Smearing operator
     *
     * Takes indices into the Vector's member array, and builds a new temporary
     * Vector that is initialized with the data as its entries.
     *
     * @note Doesn't perform any size checking, so indices that are out of range
     *       will cause an error.
     *
     * @param i0 Index value, in the range 0 to (Vector size - 1)
     * @param i1 Index value, in the range 0 to (Vector size - 1)
     *
     * @return Vector whose values are copies of those corresponding to the indices
     *         of the original vector.
     *
     *  \code
     *  // Removing the fourth component for assignment
     *  Vec3 rgb = rgba(0, 1, 2); 
     *  
     *  // Smearing
     *  Vec4 xxzz = xyzw(0, 0, 2, 2); 
     *
     *  // Swizzling
     *  Vec2 yx = xy(1, 0); 
     *  \endcode
     */
    Vector<T, 2> operator ()( size_t i0, size_t i1 ) const;

    /**
     * @brief Swizzling/Smearing operator
     *
     * Takes indices into the Vector's member array, and builds a new temporary
     * Vector that is initialized with the data as its entries.
     *
     * @note Doesn't perform any size checking, so indices that are out of range
     *       will cause an error.
     *
     * @param i0 Index value, in the range 0 to (Vector size - 1)
     * @param i1 Index value, in the range 0 to (Vector size - 1)
     * @param i2 Index value, in the range 0 to (Vector size - 1)
     *
     * @return Vector whose values are copies of those corresponding to the indices
     *         of the original vector.
     *
     *  \code
     *  // Removing the fourth component for assignment
     *  Vec3 rgb = rgba(0, 1, 2); 
     *  
     *  // Smearing
     *  Vec4 xxzz = xyzw(0, 0, 2, 2); 
     *
     *  // Swizzling
     *  Vec2 yx = xy(1, 0); 
     *  \endcode
     */
    Vector<T, 3> operator ()( size_t i0, size_t i1, size_t i2 ) const;

    /**
     * @brief Swizzling/Smearing operator
     *
     * Takes indices into the Vector's member array, and builds a new temporary
     * Vector that is initialized with the data as its entries.
     *
     * @note Doesn't perform any size checking, so indices that are out of range
     *       will cause an error.
     *
     * @param i0 Index value, in the range 0 to (Vector size - 1)
     * @param i1 Index value, in the range 0 to (Vector size - 1)
     * @param i2 Index value, in the range 0 to (Vector size - 1)
     * @param i3 Index value, in the range 0 to (Vector size - 1)
     *
     * @return Vector whose values are copies of those corresponding to the indices
     *         of the original vector.
     *
     *  \code
     *  // Removing the fourth component for assignment
     *  Vec3 hPos = (Vec4(pos, 1.0) * mvp)(0, 1, 2);
     *  
     *  // Smearing
     *  Vec4 xxzz = xyzw(0, 0, 2, 2); 
     *
     *  // Swizzling
     *  Vec3 bgr = rgb(2, 1, 0); 
     *  \endcode
     */
    Vector<T, 4> operator ()( size_t i0, size_t i1, size_t i2, size_t i3 ) const;


    // --------------------------------
    // Unary operators
    // --------------------------------
    /**
     * @brief Negation operator
     *
     * Creates and returns a Vector whose elements are the negated version of
     * the corresponding entries in the source Vector.
     *
     * @return Vector with negated values
     */
    Vector operator -() const;


    // --------------------------------
    // Binary operators
    // --------------------------------
    /**
     * @brief Vector-Vector multiplication operator
     *
     * Multiplies each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of a new 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Vector of multiplied values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_VECTOR(*)

    /**
     * @brief Vector-Vector division operator
     *
     * Divides each of the entries of this Vector by the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of a new 
     * Vector, which it then returns.
     *
     * @note Doesn't check for zero values in rhs.
     *
     * @param rhs Vector of other values
     *
     * @return Vector of divided values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_VECTOR(/)

    /**
     * @brief Vector-Vector addition operator
     *
     * Adds each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of a new 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Vector of added values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_VECTOR(+)

    /**
     * @brief Vector-Vector subtraction operator
     *
     * Divides each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of a new 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Vector of divided values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_VECTOR(-)


    /**
     * @brief Vector-Scalar multiplication operator
     *
     * Multiplies each entry of this Vector with Scalar rhs, and assigns the 
     * results to the appropriate entries of the returned Vector.
     *
     * @param rhs Scalar value
     *
     * @return Vector of multiplied values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_SCALAR(*)

    /**
     * @brief Vector-Scalar division operator
     *
     * Divides each entry of this Vector with Scalar rhs, and assigns the 
     * results to the appropriate entries of the returned Vector.
     *
     * @note Doesn't check if rhs is zero.
     *
     * @param rhs Scalar value
     *
     * @return Vector of divided values
     */
    SMOOTHE_VECTOR_BINARY_OPERATOR_SCALAR(/)

    /**
     * @brief Scalar-Vector multiplication operator
     *
     * Multiplies each entry of Vector rhs with Scalar lhs, and assigns the 
     * results to the appropriate entries of the returned Vector.
     *
     * @param lhs Scalar to multiply by
     * @param rhs Vector to be multiplied
     *
     * @return Vector of multiplied values
     */
    friend Vector operator *( T lhs, const Vector& rhs );


    // --------------------------------
    // Assignment operators
    // --------------------------------
    /**
     * @brief Vector-Vector assignment operator
     *
     * Assigns the values of Vector rhs to this Vector, and returns a refecence to
     * this Vector.
     *
     * @param rhs Vector of other values
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_VECTOR(=)

    /**
     * @brief Vector-Vector multiplication assignment operator
     *
     * Multiplies each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of this 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_VECTOR(*=)

    /**
     * @brief Vector-Vector division assignment operator
     *
     * Divides each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of this 
     * Vector, which it then returns.
     *
     * @note Doesn't check for zero values in rhs.
     *
     * @param rhs Vector of other values
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_VECTOR(/=)

    /**
     * @brief Vector-Vector addition assignment operator
     *
     * Adds each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of this 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_VECTOR(+=)

    /**
     * @brief Vector-Vector subtraction assignment operator
     *
     * Subtracts each of the entries of this Vector with the corresponding entries
     * of Vector rhs. Assigns the results to the appropriate entries of this 
     * Vector, which it then returns.
     *
     * @param rhs Vector of other values
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_VECTOR(-=)

    
    /**
     * @brief Vector-Scalar multiplication assignment operator
     *
     * Multiplies each entry of this Vector with Scalar rhs, and assigns the 
     * results to the appropriate entries of the this Vector. Then returns 
     * reference to this Vector.
     *
     * @param rhs Scalar to multiply by
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_SCALAR(*=)

    /**
     * @brief Vector-Scalar division assignment operator
     *
     * Divides each entry of this Vector with Scalar rhs, and assigns the 
     * results to the appropriate entries of the this Vector. Then returns 
     * reference to this Vector.
     *
     * @note Doesn't check for zero values in rhs.
     *
     * @param rhs Scalar to divide by
     *
     * @return Reference to this Vector
     */
    SMOOTHE_VECTOR_ASSIGN_OPERATOR_SCALAR(/=)


    // --------------------------------
    // Comparison operators
    // --------------------------------
    /**
     * @brief Equality operator
     *
     * Compares this Vector against Vector rhs for equality, returning true
     * when each entry of this Vector equals each entry of rhs.
     *
     * @param rhs Vector to compare against
     *
     * @return true when equal, false otherwise
     */
    bool operator ==( const Vector& rhs ) const;

    /**
     * @brief Inequality operator
     *
     * Compares this Vector against Vector rhs for inequality, returning true
     * if even one entry of this Vector doesn't equal rhs its counterpart in rhs.
     *
     * @param rhs Vector to compare against
     *
     * @return true when equal, false otherwise
     */
    bool operator !=( const Vector& rhs ) const;


    // --------------------------------
    // Stream operators
    // --------------------------------
    /**
     * @brief Stream output operator
     *
     * For debugging purposes.
     */
    friend std::ostream& operator<<( std::ostream &os, const Vector& rhs );

private:
    T _a[Size];

}; // class Vector


[Edited by - n00body on June 24, 2008 11:50:09 AM]

Share this post


Link to post
Share on other sites
A lot of software tries to go the route you are heading down, but IMHO it is a bad idea to 'over document' your code. Code is meant to be at least partially self-documenting. Do you really need to document the copy-constructor? It looks like it does exactly what a copy-constructor is meant to - ditto for the arithmetic operations. You really only need to document functions (and even classes), whose use is unclear from their name and context.

Share this post


Link to post
Share on other sites
Some people will over-document their code.

If you won't ever read the documentation, don't bother writing it.

It is possible that you might want the documentation for a vector class, but for most classes inside a game it isn't worth the time and bother to write and maintain. Well written code needs very few comments, since the names should be descriptive and the functions should be doing only a single thing.

Share this post


Link to post
Share on other sites
Personally, I think it's folly to clutter up the class declaration. IMO, a class declaration should be devoid of per-function comments and any function body which requires a line break to be readable. I think of the class declaration as a summary of what you can do with the class, and so it shouldn't be cluttered with how it does it. I put all such comments where I define my functions outside the class itself, whether that be in the header (templates, inlines) or in the source (everything else).

As the others have said, there's a certain draw to over-document, which should be avoided and I also agree strongly that, by writing clear code with clear function and variable names, not only can most comments be avoided with clear coding practices, but its actually easier for many programmers to understand since there's less room for ambiguity. Comments should enhance understandability, rather than be a required for it.

That said, I don't share the opinion that documenting code on a high level is wasted effort, even if you're the only one that will ever see the documentation. Reason being, things change and interruptions always come up. I've got many projects where I was developing some component I intend to use in a full project, but had to step away from for some period of time. I find that if I've coded clearly and documented for 3rd-party consumption then I can get back in the thick of it without ramping up again. The time invested may actually be a wash if I only have to drop it and pick it up again only once, but if I've documented it then I've got the docs ready for next time, or when I decide to share the code.

[Edited by - Ravyne on June 23, 2008 4:24:07 PM]

Share this post


Link to post
Share on other sites

I completely agree with some of the comments made above.

Simply put, when I am working on patching an existing system I'd want to know what methods are available for me to work with in achieving my goal. I hardly ever look at textual description, I expect the functions/methods to be well named and their parameters to be intuitive.

I think languages like C# and C++ have a lot of tools already available to developers to be able to write easy to navigate code. Tools like namespaces, consts and enums are just great and with intellisense it just gets better.

Also be careful with classes that affect other classes. If anything, I think using textual documentation to warn people or even redirect people to a better class/method can't hurt. Usually that sort of information would be hard to get if the class/method wasn't private!

Share this post


Link to post
Share on other sites
Well, thanks for the comments. I'll try to put them to good use.

Having looked around the boards, I happened upon NaturalDocs as an alternative, and will give that a try instead. It looks like it might be more appropriate for my needs.

Share this post


Link to post
Share on other sites
Quote:
Original post by n00body
Having looked around the boards, I happened upon NaturalDocs as an alternative, and will give that a try instead. It looks like it might be more appropriate for my needs.

Sorry to beat a dead horse, but it really doesn't make any difference - and doxygen is the product with wider support. Take a look at their example page though - it showcases everything I feel is wrong with documenting code in this way:
/*
Function: Multiply

Multiplies two integers.

Parameters:

x - The first integer.
y - The second integer.

Returns:

The two integers multiplied together.

See Also:

<Divide>
*/

int Multiply (int x, int y)
{ return x * y; };

I mean, golly, if I can't figure out that int Multiply(int x, int y) multiplies 2 integers and returns the results - I should be in kindergarten, not grad school.

Share this post


Link to post
Share on other sites
For any larger project, a complete documentation of everything has a simple reason: Consistency. Not only for the reader, but also for the writer. If I had to ponder every time "Is this function simple and self explaining enough?", I would force unneccessary work upon my brain, distracting it from the main work: Hammering out quality code (and with quality code I mean, documented code). And complete documentation (of the interface) produces confidence in the code for users of your code.

In essence, writing documentation for these simple cases happens automatically (finger memory ^_^'), whereas omitting it takes brain power. Additionally it is rather unnerving to have "Undocumented piece of code" popping up in your docs.

In your example documentation, I see a few problems:

1. "Indirect" writing style:


T& operator ()( size_t index );

/**
* @brief Accessor operator
*
* Used for accessing private array entries, without exposing them.


Should be reworded more directly to something like:

Access(es) private array entries


The "without exposing them" part is more a moral explanation (OOP morals ^_°).

Also, you document the interface explaining implementation internals, which is bad.


Takes indices into the Vector's member array, and builds a new temporary
* Vector that is initialized with the data as its entries.


Just write "Takes indices into the Vector's member array and returns a Vector containing the indexed data as its entries" or something like that.


@swiftcoder: Documentation is not meant to test your comprehension, it is there to explain things to you, preferably not requiring you to read the code.
In these simple cases, it might look like not worth it, but when the code is part of a large class library, you might be happy that it is there just to put things into place. (Also, once you have to document the overflow behaviour of that code, you'll be writing the docs anyway, wondering while there were none in the first place - distracting you a bit. ;))

Share this post


Link to post
Share on other sites
Quote:
Original post by Konfusius
but when the code is part of a large class library, you might be happy that it is there just to put things into place.
This is exactly it.

Libraries -- which other people are going to use -- should have documentation.

Code that is going to be maintained for many years only really needs documentation when it isn't clear to you. Maintenance programmers should add additional comments as they see the need. These people are generally making superficial changes, and they won't read the documentation -- they'll assume the names are good enough.

Your game code itself generally should not have any fancy extracted comments. The added time and overhead required to generate and maintain comments is generally not worth it.


Who is the documentation for?

Are you trying to show complete strangers how to use your libraries? Are you trying show people who are already familiar with the system (including yourself) some necessary details and flaws with the code? Does the documentation clearly provide the information they need?

I've seen people waste a huge amount of time on useless documentation, and it looks like this code block is doing the same. This vector class doesn't need anywhere near this much documentation. Anybody who is using the class ought to know what most of those function do. Operator-, operator*, operator==, operator<< and a few others really don't need documentation, especially when used by just you. Look at operator T*(); and operator T*() const;, which really have about 50% wasted keystrokes in documentation.

You've got some interesting usage notes for your operator() overloads, and those details should probably stay, but most of the documentation in there is just a waste of typing.


Share this post


Link to post
Share on other sites
Quote:
Original post by frob
Quote:
Original post by Konfusius
but when the code is part of a large class library, you might be happy that it is there just to put things into place.
This is exactly it.

Libraries -- which other people are going to use -- should have documentation.

Code that is going to be maintained for many years only really needs documentation when it isn't clear to you. Maintenance programmers should add additional comments as they see the need. These people are generally making superficial changes, and they won't read the documentation -- they'll assume the names are good enough.

Your game code itself generally should not have any fancy extracted comments. The added time and overhead required to generate and maintain comments is generally not worth it.


Who is the documentation for?

Are you trying to show complete strangers how to use your libraries? Are you trying show people who are already familiar with the system (including yourself) some necessary details and flaws with the code? Does the documentation clearly provide the information they need?

I've seen people waste a huge amount of time on useless documentation, and it looks like this code block is doing the same. This vector class doesn't need anywhere near this much documentation. Anybody who is using the class ought to know what most of those function do. Operator-, operator*, operator==, operator<< and a few others really don't need documentation, especially when used by just you. Look at operator T*(); and operator T*() const;, which really have about 50% wasted keystrokes in documentation.

You've got some interesting usage notes for your operator() overloads, and those details should probably stay, but most of the documentation in there is just a waste of typing.


But most of us don't code in a vacuum either. Odds are that we have co-workers consuming the code, or technical reviewers, auditors or, perhaps most importantly, new hires.

I agree that you have to know your audience and, as a professional Technical Writer, I would assert that this is the single most important question you should ask yourself before documenting anything. If you're reasonably sure that your audience is fully knowledgeable of vector math, then you can probably get away with a much more terse explanation of things, but something so fundamental is definitely the outlier. Most often we create custom code from a spec which doesn't mirror concepts from math or science or other widely understood concepts. Honestly, I probably wouldn't document a vector class if it were not meant for third-party consumption, save consistency and completeness.

I also don't buy the argument that documenting code distracts you greatly from writing that code, or that it has a significant cost to time or productivity. In the long run, if it saves you having to explain your class for an hour or two over the course of the project, then it saves you time. Then there's the time you save your co-workers tracking you down, asking others for help, wasting time trying to digest your source code or twiddling their thumbs waiting for someone who can answer their question. In short, there are many small benefits to pervasive documentation that you may not even perceive of, and they all add up.

Share this post


Link to post
Share on other sites

This topic is 3459 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this