|
Apart from unnecessary complexity of the code, there is one more problem with your approach, and it lies with overhead
I would advise you to step through this code with the SoftIce. You will have a chance to see how many
extra instruction have to be executed in both kernel and user mode, so you will get some idea about the overhead.
Unfortunately, this technique does not seem to be reasonable
Anton
|
|
|
|
|
Dirty hack and quite a mess! I don't see the benefit of mixing function parameters and exception handling - two semanticaly different concepts. Unpredictable behaviour on the long way.
interesting article
|
|
|
|
|
Are there any issues with the combination of the described method and standard C++ exceptions? Can I fetch a parameter this way inside a try / catch block? Thanks.
|
|
|
|
|
No, you can't use this method with standard C++ exceptions. This is because in C++ exceptions you don't have a chance to dismiss it if it is thrown. That's why SEH is more flexible: upon an exception you can 'fix' something and return execution to the same point.
|
|
|
|
|
Ok, thanks for reply. What I meant was the fact that you can not mix try / catch and __try / __except in one function. So the Func_Top() from your example can't have any try/catch block. Also any catch(...) in the callstack will catch your SEH exception and that will never reach __except block, where the parameter value is actualy filled.
Similary, if you throw C++ exception somewhere between Func_top() and Func_nested() this C++ exception will be stopped by the __except block in Func_top() and thus it will never reach it's respective catch. In fact returning EXCEPTION_CONTINUE_EXECUTION as a result of C++ throw statement terminates my test program with unhandled exception.
Eg.
<br />
void Func_Intermediate(int a)<br />
{<br />
MyClass *c = new MyClass;<br />
Func_Nested(a);<br />
}<br />
this code will terminate you program in case new fails and throws std::bad_alloc - even if you catch bad_alloc somewhere higher in the callstack.
In summary this method may work provided that you never use catch(...) in any function on the callstack and you never let any C++ exception propagate to Func_top() .
|
|
|
|
|
Thanks for being interested in my article, but you're wrong
1. This is true that C++ exceptions can't be used with SEH exceptions within one function. I don't know why, but that's how it is. So if you want a block with a C++ try - catch block you have to split it into a different function.
2. Microsoft C++ compiler implements C++ exceptions via SEH, with the exception code = 0xE06D7363. So that every your throw statement is in fact RaiseException(0xE06D7363, EXCEPTION_NONCONTINUABLE, 3, pArgs) whereas 3 exception's parameters are something C++ - specific, via which C++ catch block (which is __except of course) knows to filter the needed thrown type, and return EXCEPTION_EXECUTE_HANDLER if the thrown type matches.
3. When you write C++ catch (/*some type*/) the compiler translates it into an __except block that will return EXCEPTION_CONTINUE_SEARCH for a non-C++ exception (any exception with another code). So that using C++ exceptions in Func_Intermediate won't harm.
4. When you write C++ catch (...) it is identical to __except(EXCEPTION_EXECUTE_HANDLER) , and this thing catches every possible exception. I've mentioned this thing in the article. I believe that catching all exceptions in incorrect concept.
|
|
|
|
|
Whow! That's interesting! I always was confused about c++ exceptions versus SEH - now it looks much clearer to me. Thanks.
|
|
|
|
|
I once asked Stroustrup (couple years ago) at a public forum, "Why doesn't C++ offer programmers the means to change the number of parameters in a function header, as well as the 'type' for any of those parameters, FROM INSIDE THE FUNCTION ITSELF?"
I may not have asked the question well, or maybe it's just that he didn't want to embarrass me, but his reply was that I should submit the question in writing.
Later (after the forum), Scott Meyers asked me, "What would be the benefit of C++ doing something like what you suggested?"
I replied, "Several, including dealing with legacy code, performance flexibility, and enhancing the security of someone's code against piracy."
He didn't answer. I guessed I may have given him food for thought (or maybe, he too, didn't want to embarrass me).
Reading this article not only referenced one part of my question to Stroustrup, it ALMOST KNOCKED ME OUT OF MY CHAIR!! Here is something that's describing a way to dynamically add an extra parameter to a function that wasn't existing as part of the function's signature, earlier when the program started executing, but which is now being added, "mid-stream" (so to speak). This is heady stuff.
As expected, "revolutionary thoughts" are most often crushed, rejected, and berated. History has REPEATEDLY shown this to be the typical treatment given to ideas which challenge our "comfort zone", and for the thinkers of such thoughts, they usually fare no better. (Wonder why some people keep their thoughts to themselves?)
I have read this article several times over, not because I didn't understand it, but because I didn't want to miss ANYTHING EXTRA I could glean from it. AFAIC, this is a "revolutionary thinker" who has provided this community with an exceptional piece of work, and I only wish I could have voted more than '5' for his work.
A big "Thank You" is in order, and to him, I tip my hat!!
William
Fortes in fide et opere!
|
|
|
|
|
As expected, "revolutionary thoughts" are most often crushed, rejected, and berated. History has REPEATEDLY shown this to be the typical treatment given to ideas which challenge our "comfort zone", and for the thinkers of such thoughts, they usually fare no better. (Wonder why some people keep their thoughts to themselves?)
(Before I start, I want to make it 100% clear I am not talking about the author of this article.)
Be very careful when wrapping yourself in that blanket. History is full of people, and their followers, who defend their ideas with statements such as these. However, the vast majority of these people are crackpots. The goal of this statement is to associate the idea with famous instances of great ideas being suppressed. But if you look at the statement logically, it is totally flawed. The assertion is basically that "Great ideas are vilified. Since I am being vilified, I must have a great idea." See Martin Gardner's "Fads & Fallacies in the Name of Science" for a long list of this defense being used.
To the author's credit, he as not used this approach to defend his article. I might not agree with him but I do not consider him a crackpot by a long shot. He has shown a great ability to "think out of the box" but that still doesn't mean that the ideas get a "free pass".
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
The author of this article certainly doesn't get a "free pass" from you (not that I believe he was looking for any, or even needs one), and is not something over which I believe he'll lose any sleep.
The same line of reasoning you use to state your case, could also be used to negate it. And while I would not mix 'apples' with 'oranges' (to use the proverbial phrase) in bunching genuine revolutionary ideas with crackpot ones, distinction needs to be made to understand when dealing with one from the other.
I use history, and reference history in making my point, because it is the greatest teacher mankind has, but like most everything else, it is also subject to distortion and misinterpretation (sometimes even up to today) by those with self-serving agendas. It is mostly to that end, that the author of the book you quoted, sought to expose (differentiate) sound scientific pursuits from misguided ones. So while science itself provides certain quantitative means by which unsound scientific claims can be exposed before being labelled disingenuous, let's not forget that some scienctific ideas (themselves) when first introduced, were classified as groundless (and I don't have to go very far to remember the reception Quantum Mechanics received when it first surfaced).
Without the rigors of scrutiny, the use of logic that "Great ideas are often vilified. This is an idea being vilified. Therefore this must be a great idea," may indeed have purpose and cause for use if the idea has NOT been proven that it lacks soundness ("staying power"), workability (does it do what it claims to do), and benefit. Devoid of these checks, to quote it in the face of someone's work is reminiscent of the Dark Age and the time of The Inquisition, popularly used by skeptics, diehard disbelivers, and those in fear of upsetting the status quo because of self-serving agendas (Galileo comes to mind as one who was at the receiving end from people like those).
It is pretty much a foregone conclusion that revolutionary thinkers don't get the recognition of their outstanding work until they've been either verbially scourged, ostracized, or dead (sometimes all three).
William
Fortes in fide et opere!
|
|
|
|
|
http://www.datanation.com/fallacies/index.htm[^]
You are making many logical mistakes here. The statement is prejudicial and irrelevant. As you openly admit, it is used to provide an argument of validity where none might exist.
Ideas must live and die on their own merit. It is not the job of the critic to disprove a hypothesis when the hypothesis has yet to be proven. (Just because something has yet to been proven doesn't mean it is true.) I have already stated at length why "I" feel that this stuff isn't a good idea. All the author has done is stated that he feels it is a good idea without evidence to backup his statement. However, given that this is Code Project and not a medical journal, that is totally acceptable and warranted. But that still doesn't shield the author from criticism.
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
I don't expect you to say anything differently to the negative scores you've been putting forward. It's what skeptics, naysayers, and disbelievers do best. It's their shining moments. They even assert logic under the pretense that it validates their claim to righteousness, which to me seem pretty desperate in the face of self-serving rhetoric.
If you believe that, "Ideas must live and die on their own merit," then TEST the idea this revolutionary thinker has put forward. GO AHEAD AND TEST IT!!! TEST IT!!! Test it to see if it's workable, and if turns out all that he said it would do. Test it to see if he knows what he's talking about. Test it to see if it has "merit" for others to fairly judge whether they'd want to adopt his techniques. TEST IT!!! GO AHEAD AND TEST IT!!! Put what you have forwarded about ideas living and dying on their own merit,... to the TEST!!! GO AHEAD AND TEST IT!!! Then (as expected) you'll either change your tune, or quietly fade away (naysayers are famously known for doing that also.)
William
Fortes in fide et opere!
|
|
|
|
|
Thank you for your words, I'm really touched
Our discussion became phylosophycal indeed
|
|
|
|
|
Ok, thanks for your attention.
I hope no one is really going to shot me, I'm too young to die .
About storing all the needed parameters in a struct and passing a pointer to it - of course this is a correct thing to do. That's how OOP works, all the data resides within an object and every memeber function receives a this pointer. But this is not always available, sometimes you have to supply your callback function which doesn't have a user-supplied parameter.
Also strictly speaking accessing variables via a pointer to the structure holding them is a bit slower than just accessing them as is. It is usually insignificant, but sometimes it can make scence.
Once I had to rewrite an open-source audio codec written in C, which used only global variables. The first thing I've done - united all the parameters into a struct, pointer to which is passed to all functions. But than I noticed another issue: For every arithmetic operation this codec called a small function. And every such a function upon an overflow had to set a flag. Before I modified the code this flag was just a global variable, but now it became the struct member. So, for every arithmetic operation I had to provide a pointer to this struct too. And that was something that I tried to avoid.
EXCEPTION_FETCHPARAM_B - I didn't defined it, sorry. I just meant that you can have different exception codes for different variables to fetch.
Now, EXCEPTION_POINTERS holds exception information. What concerns us here is the exception code, and the user parameters provided with the exception. In our case it was a pointer to a location that expects the variable.
|
|
|
|
|
I meant that I didn't define EXCEPTION_FETCHPARAM_C .
BTW There is a rule about how to define an exception code. Some bits define its severity, some describe its nature (system, hardware, etc.).
But I don't remember those rules, and I can't find any articles about this.
|
|
|
|
|
Vladislav Gelfer wrote:
I hope no one is really going to shot me, I'm too young to die
Of course you will not be shot, I don't think I'll ever maintain your code
Vladislav Gelfer wrote:
Also strictly speaking accessing variables via a pointer to the structure holding them is a bit slower than just accessing them as is.
OTOH, accessing variables by throwing an exception can't be more effective than accessing them directly via a pointer .
I have great difficulties to believe in any significant difference between
func( var a, var b )
{
and
func( struct* a )
{
But articles like this makes a very good and thought-provoking read, I must stress.
|
|
|
|
|
So let me get this straight.
You have replaced globals variables with this obscure and somewhat abusive method of accessing data. This is a perfect example of why a poor understanding of the problem is a bad thing. People are told time and time again that global variables are bad without understanding why they are bad. They replace global variables god awful hacks and end up making software harder to maintain and support. So often I see people do silly things such as moving global variables into monster global structure (i.e. a subclass of MFC's application class) without realizing that all they have done is changed the semantics.
Technically, this is neat stuff. If you worked for me, I would say "Neat, now rip that crap out and do it right."
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
So, why use of global variables is bad from your point of view?
I described my opinion about that in the article. I believe every piece of code should be written in such a way that it can be reused in many different situations, even if it is really used only once. So-called object-oriented model. It makes extending and modifying the program very easy.
I totally agree that uniting all global variables into a single global struct is the same crap.
I also agree that this method should not be a common practice. I only meant that it is a good trick to retreive variables (and other things) in rare, even abnormal situations.
It eliminates the need in providing the desired parameter in all intermediate functions in the chain. Normally, when your program is object-oriented, you don't have to do so, since every member function automatically gets a pointer to the object. But there are exceptions. One perfect example I described in my 'Answers' reply. In fact than I used that trick for the first time, and I keep using it till now.
Also, as I already mentioned, sometimes you must provide a callback function without a user parameter. Examples of such situations can be use of SetWindowsHookEx (the hooking function doesn't get a user parameter), RasDial (RasDialFunc, lack of parameter), and there are more examples. Well, all new API functions take this issue into account, but still you want to use sometimes those old stupid functions.
BTW The window procedure also doesn't get a user parameter. Well, this situation can be fixed, because you get an HWND . At first message (which is WM_NCCREATE ) you can use a parameter from the CREATESTRUCT and immediately associate your window with it by using SetWindowLong , and than you can retreive it at any time by GetWindowLong . The MFC maintains a global map instead of this, which complicates integration of EXEs and DLLs written using MFC. That's is one of the reasons I don't like MFC.
Now, you tell that such a program would be hard to maintain. That is because such a trick is unusual for most of the programmers. I use exceptions not only to indicate an error. It can be used as a multiple-level return as well. Or it can be used to allocate some resources on demand. So, get used to exceptions, they are not as scaring as many people immagine
|
|
|
|
|
As you have already stated, the technique has a significant number of problems. But like most things in programming there are always the good and the bad side of any technique. The hard part of programming is being able to compare all the different implementations.
One of the first problems with this technique is the speed issue. In your example, the SEH version of the routine runs 300 times slower than a traditional approach. However, the flaw with this test is that it isn't real world. Chances are the difference would be almost eliminated when the functions contain real code doing real work. But you can't ignore the performance hit. If people abuse the technique without understanding the performance problem, they could significantly reduce the performance of their applications without knowing why. Just today I was talking to one of our graphics guys and he was telling me about a routine that was being executed over 10,000,000 times per frame. What if this routine had used SEH parameter passing? As the proportion of time it takes to do SEH parameter passing becomes significant compared to the amount of time the rest of the routine takes to execute, performance will become a problem. If you think I am wrong about this, I have had to replace an STL binary search routine on a list because the expense of going from link to link was more expensive than doing the actual compare. The brute force search starting at beginning of list was faster and improved the overall algorithm's performance. Your point on performance isn't correct.
The real problem with the technique is what could be characterized as inappropriate contracts on all routines that sit between the caller (with the catch) and the callee (who throws the exception). In general, all software can be thought of as contracts. When you call a routine, he has a contract stating that given series of inputs, the routine will operate in a well defined manner. An inappropriate contract is a contract imposed on the callee by the caller. Implementation (the contract) should always be defined by the callee and never by the caller. Your technique requires the placing of a contract on all routines that sit between the caller and the callee. Even though your callee and caller have negotiated a contract, you are also imposing a implementation contract on all intermediate routines.
Let us define d-call as the number of routines that sit between the caller and the callee. If d-call is zero or small, there is very little risk in your technique. Chances are, only a single programmer will be working on those routines and he will know about the caller and callee. But the truth is that as a project gets larger, you have more people working on foreign code that they do not fully understand. The chance of someone making an invalid change and not knowing about the special contract greatly increases as project size and d-call increases. The problem gets even more significant if the programming department doesn't use regression testing or if the SEH parameter passing cases are used in exceptional cases that fall outside of normal processing. You have already stated that being able to handle exceptional cases is a good place to use SEH parameter passing. The opposite is in fact true unless your regression tests are very thorough.
One of the cases you specifically mention is that you can use this technique where "... your function is called from within another function, which is not yours." Just because it works today doesn't mean it will work tomorrow. The programmer graveyard is full of people who utilized techniques that worked under one version of Windows and failed miserably under another. As you have already pointed out, your technique acts strange when done through the windows messaging system. Who is to say that when Longhorn comes out that this technique will start to fail. If an when it does happen, it does little good to blame Microsoft since you will be required to refactor your code. As it stands, the technique is problematic when used with code you have total control over, but it is down right dangerous when used with code you have no control over. It is foolish to impose contracts on software you don't control. The developers of that software have no knowledge of what you are doing and aren't required to respect it even if they did.
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
1. Is 'EXCEPTION_FETCHPARAM_B' a variable you created? (I asked because I couldn't find it anywhere in MSDN.)
2. Is the value you initialized 'EXCEPTION_FETCHPARAM_B' with, a system value, or is it one you created and assigned, yourself?
3. What sort of values does 'ExceptionInformation' hold? (I'm not talking about "type". It's an array of what? What does its values signify?) Richter describes it as, "An array of additional arguments that describes the exceptions." So, what does '1', '14', '15' (etc.) mean? (Considering Richter goes on to say, "For almost all exceptions, the array elements are undefined.") Could you explain this a little more?
==============================================
I am reading your article to mean 'parameter', and NOT some kind of 'value' to a parameter. Right? (That's what question #3 seeks to have answered.)
Good article so far!
William
Fortes in fide et opere!
|
|
|
|
|
See the first line of the source code snippet on this page..
Or is that stretching your limits?
--
Ich bin Joachim von Hassel, und ich bin Pilot der Bundeswehr.
Welle: Erdball - F104-G Starfighter
|
|
|
|
|
See the sentence near the end of my post that states, "I am reading your article to mean 'parameter' (etc.).", ... or are you BLIND!!
William
Fortes in fide et opere!
|
|
|
|
|
First of all, had I had to maintain an application written in this way, I would have demanded to have you shot
Just trying to figure out this definitely un-orthodox way of using exceptions - not to mention trying to get a feeling for the flow of the functionality included, would give an experienced developer ulcers. Why not pack the info into a struct and pass a pointer down the line? Expand the struct as necessary.
But: the article is totally intriguing. Even though I would never endorse the method, it's a - in my experience at least - completely novel concept. And so mindboggling.
Add to that a clear article, and what can I do except rate it 5? You are either a genius or a lunatic
|
|
|
|
|
Johan Rosengren wrote:
You are either a genius or a lunatic
I'm leaning towards lunatic.. This is nothing I'd like to face as a maintainence programmer. :shudder:
--
Ich bin Joachim von Hassel, und ich bin Pilot der Bundeswehr.
Welle: Erdball - F104-G Starfighter
|
|
|
|
|
Blame only your limitations!
I am leaning towards "genius".
William
Fortes in fide et opere!
|
|
|
|
|