|
PIEBALDconsult wrote: Where do you declare it?
I usually have a module with any global variables that are required by my macros. Not wonderful, but I don't know any better approach in C.
PIEBALDconsult wrote: I've only seen that in the GCC documentation where it talks about "swallowing the semi-colon".
I've certainly never seen it in production code, and I hope I never do.
When trying to write a macro that behaves like a void function but contains an 'if' statement, something like:
#define foo(x) do {if (x > 0) do_this();} while(0) is clearer than:
#define foo(x) if (x > 0) do_this(); else while nonetheless avoiding the difficulties associated with:
#define foo(x) {if (x > 0) do_this();} or worse, the bug-prone
#define foo(x) if (x > 0) do_this() which makes me cringe.
PIEBALDconsult wrote: And I don't write a loop that isn't a loop.
What would be your preferred way of allowing a procedure to be done in multiple steps and abort at any step, while ensuring that any necessary cleanup would be completed? If you prefer a "goto" label to a do/while(0) structure with break statements, fine. That's actually my usual preference. Can you suggest anything better than the do/while(0) for people who dislike goto?
|
|
|
|
|
I don't mind it hidden in a macro; I expect to find odd things there, but I don't want to see it hung on the wall for all to see.
Anyway, the original post's logic doesn't require anything like that.
|
|
|
|
|
FatBuddha wrote: bool doSomething = false;
do{
if(!flagA)break;
if(!flagB)break;
if(!flagC)break;
if(!PromptUser())break;
doSomething = true;
}while(0);
What about
if (!(
!flagA
|| !flagB
|| !flagC
|| !PromptUser()))
DoSomething();
else
DoOtherThing();
Greetings - Gajatko
Portable.NET is part of DotGNU, a project to build a complete Free Software replacement for .NET - a system that truly belongs to the developers.
|
|
|
|
|
Still has all the needless negation; the version I posted earlier is still the cleanest, easiest to read, easiest to maintain.
But I'm glad I'm not the only one who's not afraid to break an if onto multiple lines.
|
|
|
|
|
Not sure if this made one of the previous replies, but what I found works best, and is easy to follow, is to iteratively step thru what I do not want, making use "else if" statements.
After weeding thru all the crap, then what I am left with, is (usually) what I want.
if(!flagA)
{
DoOtherThing();
}
else if(!flagB)
{
DoOtherThing();
}
else if(!flagC)
{
DoOtherThing();
}
else if(!PromtUser())
{
DoOtherThing();
}
else
{
DoSomething();
}
|
|
|
|
|
That's simply the first snippet inverted and made even uglier. It's still a maintenance nightmare.
|
|
|
|
|
I usualy perfer "goto cleanup" statement (where cleanup is lable inside the function) instead of making code hard to read.
Manish Agarwal
manish.k.agarwal @ gmail DOT com
|
|
|
|
|
|
Nah,
goto is a horror itself. If you have the need for it, then you can be sure that there is something seriously wrong in your design...
Regards
Thomas
(Btw: Does anybody know why it's still there at all in C#?)
www.thomas-weller.de
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. Programmer - an organism that turns coffee into software.
|
|
|
|
|
When you do need a goto (and I haven't since I quit BASIC) it's best to just use it.
C# -- for switch statements.
|
|
|
|
|
Georgi, I agree with you. "Return in the middle" is an horror!
****************************
Strong congruence
for strong people;
with a compatible behaviour.
For a semantical way of life.
|
|
|
|
|
I would tend do follow Michael Jackson's advice:
"At this point you might be tempted to introduce a flag.
Avoid such Satanic practices."
I quote from memory not having read it in the last 10 years.
Use of 'flags' to control program flow is often a bad code smell.
|
|
|
|
|
While having many return statements might be questionable, I think readability is much more important. So I would always allow for multiple return statements to avoid deep nesting. Deep nesting makes the code going out of the right side of the display and it is also harder to understand. Over time, this can become a big maintainability issue.
In your (first) example, we can do without a single return . We can be explicit and well structured at the same time like so:
bool needToDoSomething = false;
if (FlagA && FlagB && FlagC)
{
needToDoSomething = PromptUser();
}
if (needToDoSomething)
{
DoSomething();
}
else
{
DoOtherThing();
}
You should always write your code such that a potential reader can quickly understand your original intention without the need to scroll in any direction.
Another issue arises with the code above when it comes to unit testing and code coverage: We cannot setup code coverage for every single condition in if (FlagA && FlagB && FlagC) - we can do this only for the whole line. If we want to be accurate with this and setup an individual test case for every single condition, we can only do this by using a waterfall-like coding style:
bool needToDoSomething = false;
if (!needToDoSomething)
needToDoSomething |= FlagA;
if (!needToDoSomething)
needToDoSomething |= FlagB;
if (!needToDoSomething)
needToDoSomething |= FlagC;
if (needToDoSomething)
needToDoSomething = PromptUser();
if (needToDoSomething)
{
DoSomething();
}
else
{
DoOtherThing();
}
Probably not the most elegant solution and surely not the shortest one, but it is easy to read/understand and it has a much better testability than the first example. And this in my view is much more important than any other argument.
Regards
Thomas
www.thomas-weller.de
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. Programmer - an organism that turns coffee into software.
|
|
|
|
|
Thomas Weller wrote: but it is easy to read/understand
No it isn't, I can barely figure it out. Shouldn't needToDoSomething start off true and get changed to false if any test fails?
bool needToDoSomething = true;
if (needToDoSomething) needToDoSomething = FlagA;
if (needToDoSomething) needToDoSomething = FlagB;
if (needToDoSomething) needToDoSomething = FlagC;
if (needToDoSomething) needToDoSomething = PromptUser();
(Dang, C# doesn't have a &&= operator, that'd be useful here.)
Maybe it's a language issue, C# has an actual boolean type, C/C++ doesn't, which are you using?
|
|
|
|
|
Personally, I have no real problem with returns in the middle of a function as long as there are no cleanup considerations, and as long as the procedure remains clear and readable.
For cleanup issues, I believe C++ leads the way through RAII (Resource Acquisition Is Initialisation - RAII (Wikipedia), allowing code to be written in whichever manner makes it clearest to read - any resources are then guaranteed to be cleaned up on exit from the routine.
|
|
|
|
|
In case of complex preconditions separate those to another function.
This prevents structured-ifs and keeps the calling-function readable. If the
conditions change (in thight iterative development this always happens), adaption gets easy.
bool ShouldDoSomething()
{
if(!flagA) return false;
if(!flagB) return false;
if(!flagC) return false;
if(!PromptUser() ) return false;
return true;
}
void callingFunction()
{
if( ShouldDoSomething() )
{
DoSomething();
}
else
{
DoOtherThing();
}
}
|
|
|
|
|
Then why not:
bool ShouldDoSomething()
{
return ( flagA && flagB && flagC && PromptUser() ) ;
}
which is much easier to read?
|
|
|
|
|
you're right... got a bit infected by the horror around.
|
|
|
|
|
Be aware of Junioria Developerus animal. Those species may decide that PromptUser() && flagA && flagB && flagC looks prettier.
Cyclomatic complexity for && and if is the same. Some people believe that cyclomatic complexity number is directly related to code maintenance. And in this thread we did not really lower the number. Is the original code fine as is?
|
|
|
|
|
notmasteryet wrote: Junioria Developerus
I suppose that would be a reason not to distance the tests from the code that uses it.
And not to use a lot of negation.
|
|
|
|
|
Ever thought about using && ?
|
|
|
|
|
blockpoint2=BLOCKS+1;
codeblockpos=0;
addbyte(0x89); addbyte(0xFA);
addbyte(0xC1); addbyte(0xEA); addbyte(12);
addbyte(0x8B); addbyte(0x0C); addbyte(0x95);
addlong(vraddrl);
addbyte(0xF6); addbyte(0xC1); addbyte(1);
addbyte(0x75); addbyte(4);
addbyte(0x8B); addbyte(0x14); addbyte(0x39);
addbyte(0xC3);
addbyte(0x57);
addbyte(0xE8);
addlong(readmemfl-(uint32_t)(&rcodeblock[blockpoint2][codeblockpos+4]));
addbyte(0x89); addbyte(0xF9);
addbyte(0xC1); addbyte(0xE9); addbyte(12);
addbyte(0x83); addbyte(0xC4); addbyte(0x04);
addbyte(0x89); addbyte(0xC2);
addbyte(0x8B); addbyte(0x0C); addbyte(0x8D); addlong(vraddrl);
addbyte(0xC3);
There is written to memory and executed there. The only way I can think of making this worse is to remove the comments.
|
|
|
|
|
Timothy Baldwin wrote: The only way I can think of making this worse is to remove the comments.
I agree. It would serve as potential job security though.
"The clue train passed his station without stopping." - John Simmons / outlaw programmer
"Real programmers just throw a bunch of 1s and 0s at the computer to see what sticks" - Pete O'Hanlon
"Not only do you continue to babble nonsense, you can't even correctly remember the nonsense you babbled just minutes ago." - Rob Graham
|
|
|
|
|
Ohhh... so that's how you do that...
|
|
|
|
|
There are cases where I would consider such code appropriate. If the routine in question will represent 90% of the running time of the program in which it resides, writing the routine as indicated will reduce its own running time by 60%, the running time of the program will be significant, and there is no other practical way to achieve such a speedup, then such code could be reasonable.
Such code may also be reasonable in cases where one wishes to thwart disassembly. In such cases, one may have to tolerate some messiness in the source to obfuscate the machine code. If obfuscation of the machine code is a bona fide and legitimate goal, the nasty source code may be an acceptable price to pay.
The above code doesn't seem to contain any loops, so it wouldn't have much use as a speedup method. It might be designed to discourage reverse-engineering, though there would be better ways of accomplishing that.
|
|
|
|