views:

435

answers:

10

DISCLAIMER: I haven't done C++ for some time...

Is it common nowadays to decorate C/C++ function/method declarations in order to improve readability?

Crude Example:

void some_function(IN int param1, OUT char **param2);

with the macros IN and OUT defined with an empty body (i.e. lightweight documentation if you will in this example). Of course I understand this goes somewhat in parallel with the "doc comment block" associated with the method/function.

Could you provide some other examples... assuming this topic is useful to the community. Please bear in mind that the example above is just what it is.

A: 

I have not seen this before. I would think it would be better to put information like this in comments.

Nali4Freedom
+15  A: 

I wouldn't appreciate such decoration.

Much better to use const and references and constant references, like in

void some_function(AClass const &param1, AnotherClass &param2)

Usually int are passed by value and not by reference, so I used AClass and AnotherClass for the example. It seems to me that adding empy IN and OUT would be distracting.

Francesco
+1  A: 

I try to use:

  • Values for input parameters or references if they are big
  • References for out parameters
  • Pointers to give ownership to the called function

Most of the time is really easy to see which are IN or OUT parameters, of course proper names in the declaration are a good documentation.

I find those IN, OUT addons annoying.

Arkaitz Jimenez
+2  A: 

Not in C++, I have not done C programming professionally but at least in C++ the type of the parameters is self-explanatory:

void f( std::string const & ); // input parameter
void f( std::string );         // input parameter again (by value)
void f( std::string& );        // in/out parameter
std::string f();               // output

That together with in-code documenting tools (doxygen) where you add some context to the parameters (what values are expected or unacceptable by the function, how the function does change the passed in objects...

About pointers: We tend to limit raw pointers in our method interfaces. When need be, they can be used, but in general smart pointers should be preferred. Then again, ownership semantics come from the choice of smart pointer: shared_ptr<> for diluted shared responsibility (or when needed), auto_ptr<>/unique_ptr<> for single ownership (usually as return value from factories, locals or member attributes)...

David Rodríguez - dribeas
Pavel Minaev
Yes, but it must be a fully constructed object, so it is not 'purely' out. I tend to see them (this is just me, not a common ground) as in/out parameters regardless of whether they are processed/read inside. While acceptable, I would require an explanation for an argument received by reference whose value is not used inside the function in a code review. Places where it is acceptable: a function returning a raw buffer (pointer to a block of memory) that also updates the size passed as parameter (only *after* they have *really* justified not using a safer construct) or as optimization if needed
David Rodríguez - dribeas
+1  A: 

I have seen this, but I don't think I would say it's "common."

The Win32 API (C not C++) uses something similar:

WINADVAPI
BOOL
WINAPI
CreateProcessWithLogonW(
    __in        LPCWSTR lpUsername,
    __in_opt    LPCWSTR lpDomain,
    __in        LPCWSTR lpPassword,
    __in        DWORD dwLogonFlags,
    __in_opt    LPCWSTR lpApplicationName,
    __inout_opt LPWSTR lpCommandLine,
    __in        DWORD dwCreationFlags,
    __in_opt    LPVOID lpEnvironment,
    __in_opt    LPCWSTR lpCurrentDirectory,
    __in        LPSTARTUPINFOW lpStartupInfo,
    __out       LPPROCESS_INFORMATION lpProcessInformation
      );

In the case of the Visual C++ 2005 and later compilers, these actually map to declarations like __$allowed_on_parameter and are checked at compile time.

Tim Sylvester
+5  A: 

Windows headers actually do exactly this. See Header Annotations for the full list of annotations used. For example"

DWORD
WINAPI
GetModuleFileName(
    __in_opt HMODULE hModule,
    __out_ecount_part(nSize, return + 1) LPTSTR lpFilename,
    __in DWORD nSize
    );

For this function, hModule is an optional input parameter, lpFilename is an output parameter which store a maximum of nSize character elements and which will contain (the return value of the function)+1 character elements in it upon return, and nSize is an input parameter.

Adam Rosenfield
+1. This blog entry has background and more examples: http://blogs.msdn.com/michael_howard/archive/2006/05/19/602077.aspx
Josh Kelley
But those aren't empty macros - in the end, they expand to SAL attributes such as `[SA_Pre(...)]` and `[SA_Post(...)]`, which are actually handled by the compiler.
Pavel Minaev
+4  A: 

For documentation purposes, a well-written comment block is sufficient, so these don't serve any purpose. Furthermore, some documentation comment parsers have special syntax for just such a thing; for example, given Doxygen, you could write:

/**
 * @param[in]  param1 ...
 * @param[out] param2 ...
 **/
void some_function(int param1, char **param2);
Pavel Minaev
... but what about for things outside of the documentation concern?
jldupont
What do you mean? You can and should document all methods, not just those in public API. For purpose of documentation generation, there's usually a way to signify which methods are "for private use", so that they don't get into public docs - e.g. `\internal` in Doxygen.
Pavel Minaev
If you're not looking at the docs, you're looking at the source code. If it isn't at least fairly obvious, you need to refactor and/or rename elements in the source instead of decorating.
David Thornley
+4  A: 

I think this is a bad idea. Especially since anybody can come along and define the macros IN/OUT and leave you in heap big trouble.

If you really want to document it put comments in there.

void some_function(/* IN */ int param1, /* OUT */ char **param2);

Also why use an out when a return value will work fine.
Also I would prefer to use pass by ref and const ref to indicate my intentions. Also the compiler now does relatively good optimsing for intent when your code is const correct.

void some_function(/* IN */ int const& param1, /* OUT */ char*& param2);
// OK for int const& is kind of silly but other types may be usefull.
Martin York
good point... thanks!
jldupont
@Martin: as far as the "char *" comment you made, I had my disclaimer at the top of the question ;-)
jldupont
/* +1 */ (as opposed to PLUSONE)
David Thornley
+1  A: 

The only thing worse then this was seen long ago in a C program written by Pascal dev:


#define begin {
#define end   }

int main( int argc, char* argv[] )
begin
  ...
end
Nikolai N Fetissov
A: 

I saw usage of prefixes i_, o_, io_ in addition to information in parameter types:

void some_function(int i_param1, char** o_param2, int& io_param3);
Konstantin