Reputation: 158
I want to improve the readability of the code. So I commented the parameter's direction in the code like this:
#define IN
#define OUT
void Add(IN int Para1, IN int Para2, OUT int& Result);
But I think the compiler will replace every instance of IN and OUT with blank and it could be quite a problem some times.
So is there a better way? Thanks.
(I use C++.)
Upvotes: 4
Views: 5494
Reputation: 13
You can use Microsoft's SAL notation for C++:
Taken from the docs:
The function will only read from the buffer. The caller must provide the buffer and initialize it.
The function will only write to the buffer. The caller must provide the buffer, and the function will initialize it.
The function may freely read from and write to the buffer. The caller must provide the buffer and initialize it.
Applies to output parameters that are de-referenced. Given a parameter p, *p is the buffer pointer. p must not be NULL.
The function will only write to the buffer. The function will provide the buffer and initialize it.
Upvotes: 1
Reputation: 13384
I can think of two easy ways for this..
1.
/*Description : Function for adding the two variables.
* Returns : Nothing
* Parameters : Para1 and Para2 are **IN** parameter and
* Result is an **OUT** parameter
* @author : <put ur name here>
*/
void Add(IN int Para1, IN int Para2, OUT int& Result);
apart from this basic information you can also store information such as version number, creation date etc.
2. You can also embed parameter type information with the variable name i.e inPara1, inPara2 and outResult. for example
void Add(int inPara1,int inPara2,int& outResult);
one more thing I would recommend to use the camel case letters for variable and function name i.e. Para1 can be as para1 etc this will help you in future.
Upvotes: 3
Reputation: 6797
Given that this is a stylistic question, it's going to provoke some subjective responses, including mine. :-)
There are procedural languages out there that do require that function parameters are defined as being input or output. In C++, that's largely unnecessary.
The general mindset that a modern C++ developer should have is one that focuses more on mutable vs. immutable, and interfaces over raw types and data.
When we look at a function like this:
void f(int x);
... a C++ developer can tell you at a glance that 'x' is used for input. Why? It's being passed by value. There is no way to modify 'x' in a way that has any impact on the caller and thus any argument passed to this function is not going to be modified.
That's also true of any const reference:
void f(const Foo& read_only_foo);
The above is definitely a strict input parameter. When we look at a function like this:
void f(int& x);
We can generally assume that f
is going to modify x
(it's not guaranteed, but knowing what f
does should clear any doubt).
With user-defined types, it becomes a bit more hazy.
ostream& operator<<(ostream& os, const Foo& foo);
Here we know for sure that 'foo' is an input parameter since it is immutable. But what about 'os'? Is it an output parameter, input, both? In strict procedural languages, an output parameter would generally imply that the parameter will be changed, but we're also reading from it here, so it would be both. While we will be invoking methods in 'os' that have an effect on its state, it's not exactly the way we think of an output parameter in the procedural languages that support in/out parameters natively.
The point is that this strict I/O way of thinking about parameters can get downright confusing with these kinds of high-level interfaces and object-oriented design. A more useful way to look at things here tends to be whether the object implementing the interface is mutable or immutable. Here, 'os' is mutable. The function is generally saying that it's going to be invoking some functions that modify its state.
How about this?
// fills the specified list with stuff
void some_list(list<int>& out_list);
Here it tends to go along, perhaps more naturally, with the sort of semantics we expect of an output parameter. With something like filling a list, we tend to think of it more intuitively as the function outputting a result through the list. I even prefixed the name with 'out' to emphasize that. But actually, and especially with C++11, we shouldn't be writing such things as Stroustrup emphasizes:
// returns a new list filled with stuff
list<int> some_list();
That actually leaves very few places left where the in/out distinction could be arguably very useful at all (and not redundant with the means already provided to mark parameters as mutable/immutable, by value, reference, pointer, or r-value reference).
Combined with clear documentation about what the function is doing, there is generally no ambiguity about how it works with its parameters, so in/out conventions tend to do very little but add a lot of extra code, and may promote a more data-oriented mindset which you should be trying to get away from.
All in all, I'd suggest trying to avoid this convention all together. Even if there are good arguments in favor of it, it's just not what people tend to do in C++. And if you want people to enjoy working with your code and not getting frustrated, you have to learn to go along with what the common masses tend to understand and like. Anything too exotic will frighten people away.
If you're absolutely, fanatically attached to designating everything as being in or out, I recommend a minimally intrusive solution like a documentation style or naming convention. Definitely avoid macros that do nothing. That will require your readers to check every time that these macros do, indeed, do nothing. A naming convention or documentation style requires no such checking.
Finally, a little quote from the creator of C++ himself:
The first rule about macros is: Don't use them unless you have to. Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer. -- Bjarne Stroustrup
Upvotes: 1
Reputation: 2590
You could make the 'IN' variables const
, implying that they are never going to be modified, so must be input-only variables. The reference without a const
could also signify that it will have its contents modified, so must be an 'OUT' variable. But really, just following a good variable naming convention should be enough. Calling a parameter 'Result' implies that it will be an 'OUT' variable in itself.
EDIT: As has been rightly mentioned by others, passing values like ints as const vars is probably not a good idea. Nonetheless, you should be able to infer whether a variable is an output one or not by the sheer fact that they always need to be references (or pointers). By making references that are input values const, that means all references that aren't const must be output values.
Upvotes: 1
Reputation: 20739
Use proper language constructs, not artifacts.
Using IN and OUT doesn't grant they are really IN and OUT. It's like the Hungarian notation, that calls WPARAM
something that was a word
and today is a long
.
int
) is an IN that you can modifyconst int&
) is an IN that behave as a constant in the functionint&
) is an OUT that must be thereconst int*
) is a IN that can also be not given (null pointer)int*
) is an OUT that can also be not given (null pointer)The advantage of using proper language construct is that improper usage will result in compilation error (so that you're forced to correct the problem) while IN and OUT will never produce any sort of error in code, and you risk to introduce a formal convention that -after a certain number of maintenance releases- can be even lie to itself.
Upvotes: 0
Reputation: 106166
There are two sides to this: the function signature, and the call site. You're focused on the signature, so I'll address that first. Notes:
const
ness communicates and enforces potential modifications by the functionAt the client call site, when passing a non-const argument it's unclear what might happen to it.
loadXXX
, group outputs first/last, and prefer-to-return
-output(s) - getXXX
- conventions mentioned above help the client too.const
parameters to const
to communicate at the call site that they won't be modified, but that's verbose and tedious, and the practice can't be enforced by the compiler (the compiler will prevent the called function modifying those parameters, but not force a caller providing a parameter the function accepts as const
to explicitly cast it to const
).Some of these things can be "forced" with proxy objects, but it would make your code virtually unreadable and unmaintainable.
Upvotes: 0
Reputation: 94890
I use something like this
void Add(
/* input parameters */
int Para1,
int Para2,
/* output parameters */
int& Result
);
This makes adding new parameters to the function easy since you do not have to mark each of them with input or output, they can just go to the corresponding section.
Upvotes: 1
Reputation: 3510
Yes: Forget those things and use constness. That will work as long as you don't have a "in" and "out" parameter, which is and should be rarely used.
void foo(int i, const std::string& s, std::vector<char>& out_buf);
// i and s are obviously "in" variables, while out_buf could be both,
// but you can easily show that by giving the parameter a proper name.
Edit: And const correctness does not mean to make value parameters const! This doesn't give the caller any additional information at all, since you can't change his variables either way.
Upvotes: 6
Reputation: 2704
You can try to put it in comments. That is much better and readable.
void Add(/*IN*/ int Para1, /*IN*/ int Para2, /*OUT*/ int& Result);
Upvotes: 10