When IN, OUT and IN/OUT is not enough!Filed Under: Weekly Tuesday Dose of goodness
Hi all,
Just came back from a considerable drive from Genting Highlands… (yeah I know it’s just a short distance for some…)… so, sorry for the late post. ;p
This article talks about the basic code documentations that are missing in most of the codes I’ve seen so far. In my C++ syllabus for the Intermediate course, I did mention that the minimum code documentations for the parameters of a function/method should be decorated with a IN, OUT or IN/OUT. Now, I’m not the one who invented this kind of notation.
And moreover, this notation doesn’t apply to all types of parameters. It’ll only apply to parameters passed by references or passed by pointers. This will indicate how the data will be used and handled.
However, it’s still not enough. Why?
Read on…
Let’s take a look at the following method:
//--------------------------------------------------------------------------------
// Checks the string to see if it's a numerical floating point number
// Parm :
// cString& - The string to check against (IN)
// Return :
// bool - True if the string is a floating number
// False if the following characters are encountered:
// 1) Alphabets
// 2) More than 1 decimal point
// 3) Spaces, commas, special character etc
//--------------------------------------------------------------------------------
bool cStringManipulator::isStringDecimal(cString& theString) {...}
Sources: StridesLib, Utility namespace, String Manipulator class
This is very straightforward, however, it doesn’t indicate if the method will keep the reference into a member attribute (via a pointer).
So, even if (IN), it’s insufficient to indicate if the method will keep a copy of that reference/pointer somewhere else. As a result, there’ll still be weird application behavior despite proper usage.
In this case, how should it have looked like if the method should keep a copy of that reference?
Check it out:
//--------------------------------------------------------------------------------
// Checks the string to see if it's a numerical floating point number
// Parm :
// cString& - The string to check against (IN/KEPT)
// Return :
// bool - True if the string is a floating number
// False if the following characters are encountered:
// 1) Alphabets
// 2) More than 1 decimal point
// 3) Spaces, commas, special character etc
//--------------------------------------------------------------------------------
bool cStringManipulator::isStringDecimal(cString& theString) {...}
So, a simple KEPT appended behind, making it a (IN/KEPT) will clearly indicate that the parameter is used as an input and it’ll be kept by the class somewhere. This will also apply to (OUT) and (IN/OUT).
By doing so, it’ll be very straightforward since you’ll now know that you can’t delete certain pointers or pass by temporary object as a reference since it’ll be kept.
It’ll not eliminate all the possible errors but it’ll certainly help fix some. It’ll also help highlight any possible design flaws as early as possible while coding.
However, this is only possible if the programmer actually READS the code documentations first before using that particular method.
On top of that, I’m considering adding other keywords into the parameter documentations as well. Such as, OWNER.
Therefore, if a method requires a pointer to be passed in and it states clearly as (IN/KEPT/OWNER), it means that the pointer will be kept as an attribute in the class and will be deleted by the same instance when all is done.
If there’s a notation like (IN/OWNER), then it simply means that the pointer will be deleted in the method there and then. These little keywords can help clarify the directional flow and ownership of pointers as well.
That’s all for this week! Signing off,
Jeremy
- Permalink
- Admin
- 29 Dec 2009 9:03 PM
- Comments (3)
December 30th, 2009 at 1:37 pm
func(IN, OUT, IN/OUT);
or
func(IN, IN/OUT, OUT);?
in certain cases, i couldn’t get a nice
func(int* in1, int* out1, long* in2, long* out2)
where (int*,int*,long*,long*) but (IN,OUT,IN,OUT)
but had to change it to
func(int* in1, long* in2, int* out1, long* out2)
where (int*,long*,int*,long*) for (IN,IN,OUT,OUT)
Note:assume int and long as other types(struct).
December 30th, 2009 at 10:31 pm
Hi Anteater,
If you want to make it obvious, for those pointers that are (IN), you can take them as const pointers instead.
The IN and OUT are meant to be documented as code docs written above the function definition itself.
The parameter names themselves should be meaningful instead of numbered names.
Lastly, IN/OUT are used only when the same pointer passed in will be mutated and is expected to be an output as well.
I’m not sure if I’m answering your question here.. do let me know if you need more clarifications.
Regards,
Jeremy
December 31st, 2009 at 1:49 pm
ah.. makes sense.. i forgot about const