Jump to content
  • Advertisement
Bob Marl

Is that an in or in/out parameter? Doxygen

Recommended Posts

I started adopting the doxygen style of documenting my code and I'm bugged with a specific case of function parameter direction argument ([in], [out] or [in,out] for @param) .

If a pointer is passed to a function for read only, then this pointer is an [in] parameter.

If a pointer is passed to a function for read only, but this function makes a copy of the pointer to have access to it in module related functions for read only operations, this pointer is still [in].

If the function uses the pointer for read only, but the other module related functions use the pointer for write operations, what does that make the parameter direction in the function that takes it? An [in] parameter without const or an [in,out] parameter?

Example of what I mean in C++:

class SteeringWheel {
        public: float rotation;
        public: SteeringWheel(void) {
                this->rotation = 0.f;
        }
};

class Car {
        private: SteeringWheel *steeringWheel;
        public:

        /**
         * @param[in or in,out?] steeringWheel The steering wheel of the car.
         */
        Car (SteeringWheel *steeringWheel) {
          	/* 1. Pointer is done read operations only in the scope of this function. */
                this->steeringWheel = steeringWheel; 
        }

        /**
         * @param[in] degree Steering amount in degrees.
         */
        void steer(float degree) 
        {
          	/* 2. Pointer is written to in a module related function called by the user. */
                this->steeringWheel->rotation += degree;
        }
};

int main(int argc, char **argv)
{
        SteeringWheel steeringWheel();

        /* car() uses steeringWheel as read only. */
        Car car(&steeringWheel);

        /* steer() uses steeringWheel from car() to write. */
        car.steer(50.f);

        return 0;
}

Share this post


Link to post
Share on other sites
Advertisement

If you pass the pointer by value, it's [in].  It doesn't matter if a transitive relation uses the pointer to change something else somewhere else at some other time.

If you pass the pointer by pointer or by reference, it's [out] or [inout] if you change the pointer, otherwise it's [in] and probably bad code.

Make your rule based on what you do to the pointer, not what you do to the value the pointer addresses.  If you're passing a pointer to some object into a function, you can assume that object may be changed (otherwise you would pass it by value or by const reference), but the pointer itself is not changed, so it's an [in] parameter.  Think of it this way:  the value of the pointer is only used as input, but the fact you're passing a pointer is documentation that the object pointed to is going to get changed.

I mean, you need some kind of rule, and at least this one is consistent.  Automated tool to generate documentation based on syntax is never going to capture the semantics of your ideas, no matter what.

Share this post


Link to post
Share on other sites

I've never really seen the appeal of doxygen honestly.  I prefer to use descriptive function names and parameters and proper use of const to indicate the functions intentions.  I find it faster to write code this way and more maintainable.  Obviously YMMV.

Share this post


Link to post
Share on other sites
7 hours ago, Mike2343 said:

I've never really seen the appeal of doxygen honestly.

For personal projects you are correct.  As projects grow, as they are used by teams of 50 or 100, as they are spread to the masses where thousands of external users use the system, the value increases tremendously.

The comments can be embedded directly in the code explaining the reasons why decisions were made.  Many IDEs will pick up the comments and show them as a tooltip.

The names themselves should always indicate their intent, but the inline comments explain the reasoning, the caveats, and the alternatives. People who use the tools daily will understand them, but a quick inline comment by the original developer can serve as a tutorial for everyone who comes after.  As a trivial example in the C standard libraries, a quick single-line comment in the memcpy() and memmove() prototypes can be ignored by those familiar with the libraries, but consider someone just learning how to use the library the when automatic tool-tip exposed by an IDE that says something like "Caution: If the memory buffers potentially overlap use memmove() instead.", and "If the memory buffers do not overlap memcpy() is more efficient."  A few extra seconds provides a tutorial for many thousand users.

 

13 hours ago, Bregma said:

Think of it this way:  the value of the pointer is only used as input, but the fact you're passing a pointer is documentation that the object pointed to is going to get changed.

There are several such indicators, and that is a good one out of many.

Const pointer values should never be changed and are an [in], and should never be an [inout]. They may also become [unused] but that is often best to change the parameter name explicitly, such as void* unused or void* depricated if you cannot remove them directly from the code.

Non-const references to pointers are practically guaranteed to be changed, so they're an [out].  The bigger question is if you read from the value first, making it an [inout] instead of just [out].

Share this post


Link to post
Share on other sites

A simple set of rules:

If it's passed by value, it's [in].

If it's passed by const reference, it's [in].

If it's passed by rvalue reference, it's still [in].

If it's passed by non-const non-rvalue reference, it's either [out] or [inout].

If it's a pointer, you have a choice: either treat it as a reference (so pointers to const are [in], pointers to non-const are [out] or [inout]), or treat it as a value (so all pointers are [in] unless the pointer itself is passed by reference).  Both conventions work.  The latter is more formally correct, but the former is probably more useful.  Pick one and stick with it, and above all stay consistent.

Share this post


Link to post
Share on other sites
On 12/10/2017 at 11:34 AM, Bob Marl said:

I started adopting the doxygen style of documenting my code and I'm bugged with a specific case of function parameter direction argument ([in], [out] or [in,out] for @param) .

I treat the in/out not of the pointer but the data it points to. If the function copies the data into itself, it's 'in'. If it modifies the data, it's 'inout', and if it only writes to the data it is 'out'. That helps in deciding whether I need to supply an address to a valid value, prepare for the data being modified (eg make a copy if it might be shared before the call), or just supply empty space where I can find the result after the call.

Doxygen itself doesn't attach meaning to the annotation, it's mostly a matter of deciding what in/out means for you or your project, and sticking with that.

 

On 12/10/2017 at 10:41 PM, Mike2343 said:

I prefer to use descriptive function names and parameters and proper use of const to indicate the functions intentions.

That leaves you no room to explain edge cases (length is in [m] if use_nautical is false, else it is [nm]), allowed input ranges (speed is in feet/micro-second, and should be less than 500), output meaning and ranges to expect (returns number of entries, non-zero positive number upto but excluding 100), and special values (-1 means "no thing", -3 means "something else", positive numbers lower than 100 is good, above 200 is dangerous if param1 is below -20).

Share this post


Link to post
Share on other sites
1 hour ago, Alberth said:

That leaves you no room to explain edge cases (length is in [m] if use_nautical is false, else it is [nm]), allowed input ranges (speed is in feet/micro-second, and should be less than 500), output meaning and ranges to expect (returns number of entries, non-zero positive number upto but excluding 100), and special values (-1 means "no thing", -3 means "something else", positive numbers lower than 100 is good, above 200 is dangerous if param1 is below -20).

Hence my usage of the word "prefer".  There are exceptions to every rule but for the most part I try and use descriptive names.  Edge cases are edge cases and I will put // Usage: notes.  I've just experienced enough incidents where the internal code was changed (header not touched) and the documentation was not updated and apparent code review didn't catch it either.  It can still happen with how I do things too, but if your incoming parameter is "const float meters_per_sec" I just find it's a bit less likely to be turned into something else as easily.

Share this post


Link to post
Share on other sites
9 hours ago, Mike2343 said:

const float meters_per_sec

I fully agree you should have descriptive names for functions and variables, but space is limited. Your example doesn't express whose speed it is, that of the ship, the bullet, the torpedo, or the water itself?

To avoid accidents, you should treat documentation much like code, or even as leading before code. (It's a higher level description of intention, while the code does the implementation with its load of irrelevant details (in the overall context).)

Note that in your case,some people did jump to the documentation, and used its information, rather than digging through all the code details. I do this too in my code. Reading the few lines of text is an order of magnitude faster in understanding how to call a function, especially if you didn't write it.

As Frob said, documentation becomes more useful for code written by others and code that you have to maintain and modify over a long period.

Share this post


Link to post
Share on other sites

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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!