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.