|
Look like one of the reason people like me don't use C++ at all at work....
|
|
|
|
|
I think this thread would be more valuable, in the long run if it were in the C# language forum.
Yegor Bugayenko has some interesting views on this topic: [^].
cheers, Bill
«There is a spectrum, from "clearly desirable behaviour," to "possibly dodgy behavior that still makes some sense," to "clearly undesirable behavior." We try to make the latter into warnings or, better, errors. But stuff that is in the middle category you don’t want to restrict unless there is a clear way to work around it.» Eric Lippert, May 14, 2008
modified 24-Jan-17 3:34am.
|
|
|
|
|
I disagree that this would be more valuable in the C# forum. This is not just a C# case, but has implications on the thought processes of developers in any OOP language as well as procedural languages.
As to Yegor's article, I have to admit that it's not very compelling. While he says that the features he wants don't exist in a modern OOP language, he then says "stick with one and try and look like pure OOP" without justifying why this is purer OOP (I would argue heavily that it isn't). Statements without clear arguments backing up the approach means that I'm more likely to ignore a proposal, especially as his argument completely fails to address issues such as cyclomatic complexity.
This space for rent
|
|
|
|
|
Hi, Pete,Pete O'Hanlon wrote: This is not just a C# case, but has implications on the thought processes of developers in any OOP language as well as procedural languages Of course, I agree, CP does not have, now, a popular, widely used, forum for such "generic" discussions; in fact, I think there is much more "value meat" in content where such design principles are salient in articles "grounded" in usage in specific languages, and frameworks.
I'd consider your recent articles here an "epitome" of exactly such design content grounded in real-world coding contexts.
The issue I bring up here is one I have "banged the drum" about many times: imho there is technical content, like this thread, that could very valuable for many CP users in the future ... if it were not "submerged" in the spate of the ever-overflowing Lounge collage of bonhomie, brain-farts, puzzles, rambles, etc.
Since there are forums designed for specific languages, here, which are very popular, my idea, based on sheer pragmatism, was to have a way to hoist this content out of the Lounge, put it on one of those forums, and, hopefully, enable further technical discussion.Pete O'Hanlon wrote: his argument completely fails to address isusues such as cyclomatic complexity. You can bet yor boots (... err ... jodhpurs ?) I and others here would like to hear your thoughts on cyclomatic complexity and the issue of when, and how, to "return" !
Finally, I linked to Yegor's blog post because it was cited in one of the other posts mentioned, and because I thought it would add a contrarian perspective to the discussion. I do not endorse Yegor's views.
Personally, I favor using explicit multiple 'returns, rather than "fall through" to one 'return.
cheers, Bill
«There is a spectrum, from "clearly desirable behaviour," to "possibly dodgy behavior that still makes some sense," to "clearly undesirable behavior." We try to make the latter into warnings or, better, errors. But stuff that is in the middle category you don’t want to restrict unless there is a clear way to work around it.» Eric Lippert, May 14, 2008
|
|
|
|
|
I can't read the articles (company rule blocking Internet access) but I give my experience on the subject.
At first I believed - and wrote my code in such a way - that returning early from a function wasn't a bad choice because it allowed to avoid conditional execution of subsequent actions should one prerequisite not be met and because all in all it's faster: if something's already over then why execute other operations?
Then reality hit me hard:
1) Intermediate return points are easy to miss and lead to a waste of time during debug, as I'd have to set breakpoints on every return statement and as I said they are easy to miss;
2) Adding further logic that check complex conditions in order to decide the course of action requires more work as you'd have to substitute the clean return with the very same state variables that you'd have to use in case of a single point of exit;
3) Logging at the end of a function with the whole state of the operations is done extremely cleanly if there is one or only a few return points. The more exit points the more recaps you have to write... and that's redundance, with all the known problems it carries;
4) clean-ups!!! With the bare minimum of return points it's very easy to place the various free(), delete, WSACleanup, whatever you need: just before the return! If there are previous triggerable returns chance are that in those cases, which may be rare, the cleanups are not called. Hilarity ensues.
So while I'm not a taliban of single point of exit (sometimes they are the best choice) as of now I prefer going in that direction as my experience proved me that on the long run it's the most stable and observable pattern.
Take my two cents for what they are worth.
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
I'm a little different on that: I prefer to do all validation at the top of the method and if it fails, exit immediately.
For example:
DateTime dob;
if (!DateTime.TryParse(tbDOB.Text, out dob))
{
... report problem to user ...
return;
}
double itemPrice;
if (!Double.TryParse(tbItemPrice.Text, out itemPrice))
{
... report problem to user ...
return;
}
... The alternative to this is to nest the ifs:
DateTime dob;
if (!DateTime.TryParse(tbDOB.Text, out dob))
{
... report problem to user ...
}
else
{
double itemPrice;
if (!Double.TryParse(tbItemPrice.Text, out itemPrice))
{
... report problem to user ...
}
else
{
...
}
} Which starts to get the "meat" of the function pushed out well to the right, which I don't like at all.
Once you are into the main function, then yes: single entry, single exit is preferable if possible.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Validation usually ends the function right there, I was speaking of the core of the function. In the end, for uniformity, I often change validation too to a single poit of exit - but once the code is something like
int function(...){
bool condition_ok = true;
int returnvalue = ERR_OK;
if (condition_ok) {
if (failed) then {
condition_ok = false;
returnvalue = errcode1;
}
}
if (condition_ok) {
if (failed) then {
condition_ok = false;
returnvalue = errcode2;
}
}
...
return returnvalue;
}
Then for uniformity it comes very easy to add validation also in this form. Functions like this are usually hardware initialization, connections, communications with retries and manual error corrections...
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
Surely validation would be in its own class rather than inside methods doing other things? Single responsibility and all that?
var validator = new MyValidator();
if (validator.Validate(objectToValidate).IsValid)
{
}
return;
Ah, I see you have the machine that goes ping. This is my favorite. You see we lease it back from the company we sold it to and that way it comes under the monthly current budget and not the capital account.
modified 31-Aug-21 21:01pm.
|
|
|
|
|
NO NO NO NO. Each method is responsible of validating its input data. Always.
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
So there are never cases when you'd want to do the same validation in more than one place?
[Edit] You might find life easier passing objects and using something like FluentValidation
Ah, I see you have the machine that goes ping. This is my favorite. You see we lease it back from the company we sold it to and that way it comes under the monthly current budget and not the capital account.
modified 31-Aug-21 21:01pm.
|
|
|
|
|
The same validation in different places calls for a function - or method, it's the same - of course, this is just plain old code reusability. But every function should validate its input parameters and the data it has access to before using them, unless extreme circumstances (i.e a hyper fast customized SIMD function).
And that carries over to checking pointers for non nullity before using them alongside the use of exceptions (where supported) and so on.
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
den2k88 wrote: every function should validate its input parameters and the data it has access to before using them
I agree with that, the question is what method you use to check them. If you're passing over one or two type parameters, then a null check is okay, but for anything other than that I'd pass an object and have a separate validator for that object type.
FluentValidation is a pretty good library for abstracting validation away from your methods. Your method should really focus on what it's supposed to be doing, not extraneous tasks.
Just my two cents anyway
Ah, I see you have the machine that goes ping. This is my favorite. You see we lease it back from the company we sold it to and that way it comes under the monthly current budget and not the capital account.
modified 31-Aug-21 21:01pm.
|
|
|
|
|
Brent Jenkins wrote: Your method should really focus on what it's supposed to be doing, not extraneous tasks. Checking if it's going to plunge to a horrible and toothy death is very well part of its responsibility. Adding classes on classes on classes for a couple of operations that strongly depend on what the method is about to do on data* is a good way to create OOP spaghetti code. Been there done that got the t-shirt and I'm still maintaining that frigging mess, whose debug caused me a lot of lost hair and temper.
* I may have a parameter that should be != 0 but to the current method it does not matter, or on the other hand each other method would accept a != 0 but he needs it to be strictly > threshold. Adding a validator class and method for its conditions only is overkill. Just check them and go on. Also it help to contextualize logging and debugging wherever reflection is not an option.
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
den2k88 wrote: I may have a parameter that should be != 0 but to the current method it does not matter, or on the other hand each other method would accept a != 0 but he needs it to be strictly > threshold.
Sounds like an inconsistent architecture to me, but whatever works for you is fine
Ah, I see you have the machine that goes ping. This is my favorite. You see we lease it back from the company we sold it to and that way it comes under the monthly current budget and not the capital account.
modified 31-Aug-21 21:01pm.
|
|
|
|
|
I skipped a detail, sorry: I'm mixing private methods with public ones. For public methods a validator or similar construct is the cleanest solution. What I said before, if applied to interface methods, would be inconsistent at best!
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
For me it comes down to how many times I'm writing the same code.
If it's more than once, then it should probably be in a method in a class of it's own unless it's complete unavoidable because of the limitations of the language/framework. To me, that's SOP for OOP code.
Ah, I see you have the machine that goes ping. This is my favorite. You see we lease it back from the company we sold it to and that way it comes under the monthly current budget and not the capital account.
modified 31-Aug-21 21:01pm.
|
|
|
|
|
Thanks for the link, Brent; looks like a valuable resource.
cheers, Bill
«There is a spectrum, from "clearly desirable behaviour," to "possibly dodgy behavior that still makes some sense," to "clearly undesirable behavior." We try to make the latter into warnings or, better, errors. But stuff that is in the middle category you don’t want to restrict unless there is a clear way to work around it.» Eric Lippert, May 14, 2008
|
|
|
|
|
Just use lots of GOTOs, to go to the end of the function.
I wanna be a eunuchs developer! Pass me a bread knife!
|
|
|
|
|
Sometimes, when code gets too nested and adding a nest causes me pain, I actually consider that.
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
I do parameter validation at the top of a method and throw exceptions if necessary. After that, one exit to rule them all.
".45 ACP - because shooting twice is just silly" - JSOP, 2010
- You can never have too much ammo - unless you're swimming, or on fire. - JSOP, 2010
- When you pry the gun from my cold dead hands, be careful - the barrel will be very hot. - JSOP, 2013
|
|
|
|
|
Ah that could be a solution but I can't throw exceptions in my code as we use Win32 APIs so we're stich with SEH and then we have a master controller in VB6, so return codes are the best solution, also most of the code is 10-15 years old written by a chemist turned programmer.
Also I came to respect the return-value based flow control as most of the times in the fields I work on is the most reasonable way to proceed. Of course on a strictly OOP environment exceptions make the rule!
CALL APOGEE, SAY AARDWOLF
GCS d--- s-/++ a- C++++ U+++ P- L- E-- W++ N++ o+ K- w+++ O? M-- V? PS+ PE- Y+ PGP t++ 5? X R++ tv-- b+ DI+++ D++ G e++>+++ h--- ++>+++ y+++* Weapons extension: ma- k++ F+2 X
If you think 'goto' is evil, try writing an Assembly program without JMP. -- TNCaver
"Go ahead, make my day"
|
|
|
|
|
|
My daughter bought a photo, taken by one of those critical-moment-snap-on-rollercoaster cameras, where everyone in the cars fore and aft is screaming, and I'm gazing off nonchalantly to my left as if nothing interesting is happening.
That's pretty much normal, for me. If there's no real danger, what is there to be afraid of?
My daughter is screaming in the picture, too. Must be her mother's bad genes.
I wanna be a eunuchs developer! Pass me a bread knife!
|
|
|
|
|
Mark_Wallace wrote: If there's no real danger, what is there to be afraid of? Someone with bad genes might puke all over you... That's my biggest concern in rollercoasters
|
|
|
|
|
OK, That I could be scared of!
I wanna be a eunuchs developer! Pass me a bread knife!
|
|
|
|
|