Gunnar Dalsnes wrote:
In win32k i use macros for flow control in the NtUser syscalls
(functionally like a try/finally block) where its very important that
some cleanup (release a lock) is always done:
BOOL NtFunc()
{
DECLARE_RETURN(BOOL);
Lock();
if (Stuff) RETURN(FALSE);
....
RETURN(TRUE);
CLEANUP:
Unlock(Stuff);
DPRINT1("NtFunc returned %i\n", _ret_);
END_CLEANUP;
}
This is EXACTLY my point. I intended to make macros much like that to
handle error recovery and cleanup. The problem is that it is very hard
to understand. It is much more clear to have something like this:
NTSTATUS NtFunc()
{
Status = DoSomething();
if( !NT_SUCCESS( Status ) )
goto ret;
Status = DoSomething2();
if( !NT_SUCCESS( Status ) )
goto cleanup1;
Status = DoSomething3();
if( !NT_SUCCESS( Status ) )
goto cleanup2;
... etc
cleanup2:
Cleanup2();
cleanup1:
Cleanup1();
ret:
return Status;
}
At first it is temping to use macros to get rid of the duplicate if(
!NTSUCCESS... lines, but anyone who knows C can read that code. With
the macros, they must first figure out how the macros work, which is
often not very simple. The most important attribute code can have is
maintainability, and readability and simplicity lead to
maintainability. I finally realized that the real reason I wanted to
make macros was because I was sick of retyping the same line of code
over and over, but the readability of the code is far more important
than saving the writer a few keystrokes.
Your example code is one of the reasons that C++ developed exceptions.
People kept writing code that was death by macros exactly that way for
exactly that reason, so the developers of C++ decided it was a good idea
to put support directly into the language for the needed functionality,
without the use of macros.
If we should only invent stuff that ppl _already_ understand we just
had to stop inventing.
Inventing for the sake of inventing is a bad thing. We invent things
because they are useful. When you invent things that change the
language to the point that another person who is fluent in the language
can not understand what you are saying, that is not useful.
Recently I was reading over some apache code and they make extensive
use of macros for flow control. I found myself becoming frustrated
to no end because I could not figure out what the hell was going on.
They looked like nice, neat function calls, but I could not for the
life of me figure out where the code was that was being called.
So if they had been function calls and not macros it would have been
easier??? Or should we stop using functions also? ;-P
Yes, because with a function call it is immediately apparent to anyone
who knows C where the flow of code goes. I spent some time trying to
understand the flow of code there and finally gave up. The macros were
so complex that I couldn't figure them out in a reasonable amount of
time, so I could not follow the flow of the code, and hence, was unable
to maintain that code.
If you cant find a macro (simple text search) you probably wouldnt
have understod the code _without_ the macros either;-P
It isn't a matter of finding the macro. Taking valuable time out from
reading already complex code to go find a complex macro, then try to
parse it and figure out what the real code you were looking at gets
expanded to for the compiler, then figre out what the expanded code
actually does can get very complex very quickly. Remember, the number
of bugs grows exponentially with the complexity of the system, so
anything one can do to reduce complexity is a very good thing.
But off course you have to learn how the macros work to understand and
utilize them, but that goes for any code. I cant see how learning a
function call with 10+ params (typical nt syscall func) should be any
easier than learning a simple macro. Thats just being lazy.
G.
It is easier because when you see a function call you immediately know
exactly where the code flows to. It flows to the function that has that
name, with the parameters listed, and then back again. No more, and no
less. You don't have to figure out if it ends up passing control to
three other places you don't know about, and if control eventually
returns to this point or not. You can easily find the called function
which should have a comment block at the start explaining what the
parameters mean in plain english. Usually no further reading is
required. With a macro you have to find the macro, parse the terse SOB,
go back to the code you were trying to understand in the first place,
figure out how the compiler actually sees that code after the macro gets
ahold of it, and then figure out what THAT code does. It adds a lot of
work with no real benefit.