Prev: "Problems"
Next: How well can TextOut() handle Unicode?
From: Hector Santos on 13 Feb 2010 17:57 Joseph M. Newcomer wrote: >> In general, IPC DESIGN is independent of language, although some >> language lend themselves to IPC designs. > **** > IPC == Inter-Process Communication. I see no IPC here; Gawd, I knew you were going to say that!! To communications people, The "I" can means both INTER and INTRA! You are missing the boat here since this is so fundamental and to get lost with statements that its a C hack, well, it really inviting yet another worthless debate with you. Because of that I "try" will refrain from further comment. -- HLS
From: Hector Santos on 13 Feb 2010 18:44 Joseph M. Newcomer wrote: > Again, you are dragging IPC into a discussion where IPC was not involved. You can do > callbacks such as event sinks without exposing the ugliness of the raw callback mechanism. Ok, lets try to get this debate civilly. I don't care for history so please try to refrain from personal opinions there, i.e. callback is a C hack. What do you consider is a raw callback mechanism? Now, in the previous message, you stated that if it didn't provide a user-define value, then YES, I will agree that a callback mechanism that doesn't take into account: 1) Rentrancy 2) Provide for user-defined object/data access, then yes, it is a poor implementation. I agree, and when dealing with 3rd party software with callback logic lacking the above, especially #2, then absolutely, its problematic. But that is an implementation issue, and not a language or "C Hack" issue. You can say that the C++ layer provide a cleaner interface, and I agree, but I will also note that these are generally based on a lower level callback abstraction. In fact, I seem to recall dealing with 3rd party software where it lacked a user-defined value and using a C++ wrapper help resolved that by making the callback static in the class, and combining it with TLS. I forget that project, but I do recall going through those motions. So it depends on what kind of design you are referring too. Writing an C++ implementation is excellent and most of our stuff is written in this way using interfaces. But I guess I have to wonder why we use C++ in general. I think it because of its - natural scoping capabilities, - constructors and destructors, - polymorphisms and interface. The virtual interface, a large part, but not the only part. Keep in mind you can duplicate all the above within pure C if you provide the library code to do so. -- HLS
From: Joseph M. Newcomer on 13 Feb 2010 22:32 See below... On Sat, 13 Feb 2010 18:44:35 -0500, Hector Santos <sant9442(a)nospam.gmail.com> wrote: >Joseph M. Newcomer wrote: > >> Again, you are dragging IPC into a discussion where IPC was not involved. You can do >> callbacks such as event sinks without exposing the ugliness of the raw callback mechanism. > > >Ok, lets try to get this debate civilly. I don't care for history so >please try to refrain from personal opinions there, i.e. callback is a >C hack. **** Actually, a callback is an assembly code hack, translated into C. This is not an opinion, it is a statement of fact. Callbacks at this level exist because the languages were unable to provide a suitable abstraction, so instead of some clean mechanism, the old let's-use-a-pointer technique was recast from assembler to C. It is a hack in that it is simply a translation of a machine-level concept directly into a high-level language. OTOH, the notion of virtual methods as a means of invoking operations is consistent with the linguistic design of C++, and although it is done *exactly* by using a pointer to redirect the call, it is done in a framework that is semantically consistent with a high-level abstraction. **** > >What do you consider is a raw callback mechanism? ***** call [eax] substitute other register names, it is isomorphic to renaming. Expressed in C, it is implemented by passing in a function pointer as a parameter to a call, with the purpose that the specified function is called when there is a desire to invoke some operation. It is limited to a single function in general. ***** > >Now, in the previous message, you stated that if it didn't provide a >user-define value, then YES, I will agree that a callback mechanism >that doesn't take into account: > > 1) Rentrancy > 2) Provide for user-defined object/data access, > >then yes, it is a poor implementation. I agree, and when dealing with >3rd party software with callback logic lacking the above, especially >#2, then absolutely, its problematic. **** Sadly, most 3rd party software fails in both the above. The Windows API has far too many callback-style APIs that fail in the same way, including, inexcusably, some that were added in either 2000 or XP, when the issues were well-known, but totally ignored. **** > >But that is an implementation issue, and not a language or "C Hack" >issue. You can say that the C++ layer provide a cleaner interface, >and I agree, but I will also note that these are generally based on a >lower level callback abstraction. In fact, I seem to recall dealing >with 3rd party software where it lacked a user-defined value and using >a C++ wrapper help resolved that by making the callback static in the >class, and combining it with TLS. I forget that project, but I do >recall going through those motions. ***** The issue is in confusing an interface that works directly and "in-your-face" with raw function pointers, and one which uses the syntactic features of the language (like virtual methods, a first-class concept) to disguise the implementation details and provide for a degree of cleanliness and elegance. For example, compare try/catch/throw or _try/_except to setjmp/longjmp as mechanisms. All are stack unwinders. But setjmp/longjmp is an inelegant kludge compared to _try/_except, and neither will work in C++ because of the need to invoke destructors as the stack unwinds. Note that in what is now my 47th year as a programmer, I have used callbacks under all kinds of conditions; I have grown to despise it as a way of life, particularly because it is so easily misused and abused. I have written stack unwinders, I have implemented try/catch mechanisms, I have implemented event notification systems, interrupt handlers, and pretty much everything that is possible to do with pointers-to-functions. And I prefer to use the C++ virtual function model. Note that the message map is actually a poor kludge; in a sane world, they would have been virtual methods, but in 16-bit windows the vtables would have gotten too large and the message map was invented as a more compact representation of the abstraction. It might have even worked well if they had gotten just a few more details right, such as always accepting the parameters passed to the superclass (this is a failure of design and implementation of a callback mechanism!). So I've seen all possible mistakes, and even made quite a few of them, particularly in my first ten years or so of programming. So I recognize the difference between a low-level hack and a methodology that is part of a linguistically consistent framework. I was shocked when Ada did not allow the passing of function pointers, wondering how it was possible to specify callbacks. Then I realized the language had alternative mechanisms that eliminated most of the need for user-specified callback functions (internally, the generated code performed callbacks, but you didn't have to see that). This was in the late 1970s, and since then I have realized that the raw callback is just a continuing hack to get an effect that should be achievable in other ways. The C++ virtual method (or C#, or Java virtual method) is one of these mechanisms. And there are those who will argue that even virtual methods are a hack, and that embedding, using interfaces, is the only way to go (e.g., COM, and the new Google GO language, which I have seen only little bits of). Ultimately, we want to get away from the decades-old concepts (like the computed GOTO) that were done to allow high-level programmers create constructs that compiled into efficient code at the low level, and go for clean abstractions (which most decent compilers can compile into incredibly good code at the low level, with no effort on the part of the programmer. I used to say that in a good language with a good compiler, you can write six levels of abstraction that compile into half an instruction. I've worked with good languages and good compilers, and have done this, repeatedly). It's too easy to get captivated by the implementation and forget that the implementation details are best left to automated mechanisms. Compilers are really good at this sort of grubby detail. At the lowest level, the implementation might be the instruction call [eax] but you should never have to think of this at the coding level. The construct function(args) where 'function' is actually a pointer is too close to the call [eax] to be really comfortable. Fortunately, the tools we have for MFC eliminate many of the visible details of how message maps are actually dispatched. At the lowest level, it really is call [eax] (in fact, if you single-step through the AfxWndProc assembly code far enough, this is what you will see, isomorphic to renaming of the register). But as an MFC programming, I have a very high-level concept: add an event handler. I don't need to see the details of the implementation. It just works. Well, sort-of-works, but I've already described the problems there. Virtual methods, if you accept derivation as the way of creating new classes, do the same job. **** > >So it depends on what kind of design you are referring too. Writing >an C++ implementation is excellent and most of our stuff is written in >this way using interfaces. But I guess I have to wonder why we use >C++ in general. I think it because of its > > - natural scoping capabilities, > - constructors and destructors, > - polymorphisms and interface. > >The virtual interface, a large part, but not the only part. > >Keep in mind you can duplicate all the above within pure C if you >provide the library code to do so. **** And you can write in assembler, too, but it doesn't mean it is a good thing most of the time. I'm working on a course in assembly code, because there are still people who need to work with it (yes, I was surprised, but the uses are legitimate). One of the surprises was the number of people who need to write really tight, high-performance SIMD code (apparently the x64 intrinsics don't produce particularly good code when they are used for this). But it doesn't mean that people should write apps in assembler. If my customer base accepted it, I'd be writing in C# or WPF, but they don't want this. In fact, are opposed to it (I'm not sure I follow the reasoning, but they write the checks, and I want to take their money). So I write in C++, and in a few cases in C (and I just finished a library in C, several thousand lines of code, in which callbacks form a particularly important part of the functionality. But I'd rather have done it in C++ and just supplied a pure virtual method. You would not BELIEVE what I saw done there when I got the library; it was a particularly ugly callback, well, I can't really call it "design", and "kludge" gives it too much dignity, but as redesigned, it is essentially a virtual method mechanism and therefore intellectually manageable, as well as handling a ton of problems the old mechanism simply ignored). So I still use them, but they *should* be avoided as a way of life in most coding. I nearly made all the complexity of the earlier kludge disappear in the new design, which took major rework to get complete and consistent. Doing a callback *right* isn't easy, and most people, I have found, take the easy solution. If you assume that you should avoid them, then you use them only when necessary, and ideally wrap enough syntactic sugar around them to make them go down easily (e.g., virtual methods in C++). joe ***** Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Hector Santos on 13 Feb 2010 23:23 Man, reading you, one has to wonder why the world has blown up yet or gotten this far. Everything was wrong, badly designed, hacked and no one ever used it right or differently. Its all one way with you. Just consider your inconsequential historical callback note had nothing to do with the OP issue or question, nor contributed to the problem. I'm sure until the code is posted, you would not exclude it as a possibility. I say its mostly likely unrelated. Anyway, it would interesting to hear your critic on the design faults of the human brain! :) -- Joseph M. Newcomer wrote: > See below... > On Sat, 13 Feb 2010 18:44:35 -0500, Hector Santos <sant9442(a)nospam.gmail.com> wrote: > >> Joseph M. Newcomer wrote: >> >>> Again, you are dragging IPC into a discussion where IPC was not involved. You can do >>> callbacks such as event sinks without exposing the ugliness of the raw callback mechanism. >> >> Ok, lets try to get this debate civilly. I don't care for history so >> please try to refrain from personal opinions there, i.e. callback is a >> C hack. > **** > Actually, a callback is an assembly code hack, translated into C. This is not an opinion, > it is a statement of fact. Callbacks at this level exist because the languages were > unable to provide a suitable abstraction, so instead of some clean mechanism, the old > let's-use-a-pointer technique was recast from assembler to C. It is a hack in that it is > simply a translation of a machine-level concept directly into a high-level language. > > OTOH, the notion of virtual methods as a means of invoking operations is consistent with > the linguistic design of C++, and although it is done *exactly* by using a pointer to > redirect the call, it is done in a framework that is semantically consistent with a > high-level abstraction. > **** >> What do you consider is a raw callback mechanism? > ***** > call [eax] > > substitute other register names, it is isomorphic to renaming. Expressed in C, it is > implemented by passing in a function pointer as a parameter to a call, with the purpose > that the specified function is called when there is a desire to invoke some operation. It > is limited to a single function in general. > ***** >> Now, in the previous message, you stated that if it didn't provide a >> user-define value, then YES, I will agree that a callback mechanism >> that doesn't take into account: >> >> 1) Rentrancy >> 2) Provide for user-defined object/data access, >> >> then yes, it is a poor implementation. I agree, and when dealing with >> 3rd party software with callback logic lacking the above, especially >> #2, then absolutely, its problematic. > **** > Sadly, most 3rd party software fails in both the above. The Windows API has far too many > callback-style APIs that fail in the same way, including, inexcusably, some that were > added in either 2000 or XP, when the issues were well-known, but totally ignored. > **** >> But that is an implementation issue, and not a language or "C Hack" >> issue. You can say that the C++ layer provide a cleaner interface, >> and I agree, but I will also note that these are generally based on a >> lower level callback abstraction. In fact, I seem to recall dealing >> with 3rd party software where it lacked a user-defined value and using >> a C++ wrapper help resolved that by making the callback static in the >> class, and combining it with TLS. I forget that project, but I do >> recall going through those motions. > ***** > The issue is in confusing an interface that works directly and "in-your-face" with raw > function pointers, and one which uses the syntactic features of the language (like virtual > methods, a first-class concept) to disguise the implementation details and provide for a > degree of cleanliness and elegance. For example, compare try/catch/throw or _try/_except > to setjmp/longjmp as mechanisms. All are stack unwinders. But setjmp/longjmp is an > inelegant kludge compared to _try/_except, and neither will work in C++ because of the > need to invoke destructors as the stack unwinds. > > Note that in what is now my 47th year as a programmer, I have used callbacks under all > kinds of conditions; I have grown to despise it as a way of life, particularly because it > is so easily misused and abused. I have written stack unwinders, I have implemented > try/catch mechanisms, I have implemented event notification systems, interrupt handlers, > and pretty much everything that is possible to do with pointers-to-functions. And I > prefer to use the C++ virtual function model. Note that the message map is actually a > poor kludge; in a sane world, they would have been virtual methods, but in 16-bit windows > the vtables would have gotten too large and the message map was invented as a more compact > representation of the abstraction. It might have even worked well if they had gotten just > a few more details right, such as always accepting the parameters passed to the superclass > (this is a failure of design and implementation of a callback mechanism!). So I've seen > all possible mistakes, and even made quite a few of them, particularly in my first ten > years or so of programming. > > So I recognize the difference between a low-level hack and a methodology that is part of a > linguistically consistent framework. > > I was shocked when Ada did not allow the passing of function pointers, wondering how it > was possible to specify callbacks. Then I realized the language had alternative > mechanisms that eliminated most of the need for user-specified callback functions > (internally, the generated code performed callbacks, but you didn't have to see that). > This was in the late 1970s, and since then I have realized that the raw callback is just a > continuing hack to get an effect that should be achievable in other ways. The C++ virtual > method (or C#, or Java virtual method) is one of these mechanisms. And there are those > who will argue that even virtual methods are a hack, and that embedding, using interfaces, > is the only way to go (e.g., COM, and the new Google GO language, which I have seen only > little bits of). Ultimately, we want to get away from the decades-old concepts (like the > computed GOTO) that were done to allow high-level programmers create constructs that > compiled into efficient code at the low level, and go for clean abstractions (which most > decent compilers can compile into incredibly good code at the low level, with no effort on > the part of the programmer. I used to say that in a good language with a good compiler, > you can write six levels of abstraction that compile into half an instruction. I've > worked with good languages and good compilers, and have done this, repeatedly). > > It's too easy to get captivated by the implementation and forget that the implementation > details are best left to automated mechanisms. Compilers are really good at this sort of > grubby detail. At the lowest level, the implementation might be the instruction > call [eax] > but you should never have to think of this at the coding level. The construct > function(args) > where 'function' is actually a pointer is too close to the call [eax] to be really > comfortable. > > Fortunately, the tools we have for MFC eliminate many of the visible details of how > message maps are actually dispatched. At the lowest level, it really is > call [eax] > (in fact, if you single-step through the AfxWndProc assembly code far enough, this is what > you will see, isomorphic to renaming of the register). But as an MFC programming, I have > a very high-level concept: add an event handler. I don't need to see the details of the > implementation. It just works. Well, sort-of-works, but I've already described the > problems there. Virtual methods, if you accept derivation as the way of creating new > classes, do the same job. > **** >> So it depends on what kind of design you are referring too. Writing >> an C++ implementation is excellent and most of our stuff is written in >> this way using interfaces. But I guess I have to wonder why we use >> C++ in general. I think it because of its >> >> - natural scoping capabilities, >> - constructors and destructors, >> - polymorphisms and interface. >> >> The virtual interface, a large part, but not the only part. >> >> Keep in mind you can duplicate all the above within pure C if you >> provide the library code to do so. > **** > And you can write in assembler, too, but it doesn't mean it is a good thing most of the > time. > > I'm working on a course in assembly code, because there are still people who need to work > with it (yes, I was surprised, but the uses are legitimate). One of the surprises was the > number of people who need to write really tight, high-performance SIMD code (apparently > the x64 intrinsics don't produce particularly good code when they are used for this). But > it doesn't mean that people should write apps in assembler. > > If my customer base accepted it, I'd be writing in C# or WPF, but they don't want this. In > fact, are opposed to it (I'm not sure I follow the reasoning, but they write the checks, > and I want to take their money). So I write in C++, and in a few cases in C (and I just > finished a library in C, several thousand lines of code, in which callbacks form a > particularly important part of the functionality. But I'd rather have done it in C++ and > just supplied a pure virtual method. You would not BELIEVE what I saw done there when I > got the library; it was a particularly ugly callback, well, I can't really call it > "design", and "kludge" gives it too much dignity, but as redesigned, it is essentially a > virtual method mechanism and therefore intellectually manageable, as well as handling a > ton of problems the old mechanism simply ignored). So I still use them, but they *should* > be avoided as a way of life in most coding. I nearly made all the complexity of the > earlier kludge disappear in the new design, which took major rework to get complete and > consistent. Doing a callback *right* isn't easy, and most people, I have found, take the > easy solution. If you assume that you should avoid them, then you use them only when > necessary, and ideally wrap enough syntactic sugar around them to make them go down easily > (e.g., virtual methods in C++). > joe > ***** > Joseph M. Newcomer [MVP] > email: newcomer(a)flounder.com > Web: http://www.flounder.com > MVP Tips: http://www.flounder.com/mvp_tips.htm -- HLS
From: Joseph M. Newcomer on 14 Feb 2010 12:05 On Sat, 13 Feb 2010 23:23:52 -0500, Hector Santos <sant9442(a)nospam.gmail.com> wrote: > >Man, reading you, one has to wonder why the world has blown up yet or > gotten this far. Everything was wrong, badly designed, hacked and no >one ever used it right or differently. Its all one way with you. Just >consider your inconsequential historical callback note had nothing to >do with the OP issue or question, nor contributed to the problem. I'm >sure until the code is posted, you would not exclude it as a >possibility. I say its mostly likely unrelated. **** What continues to amaze me is that ideas we knew were bad in 1970 keep getting re-invented by another generation who doesn't understand why they were abandoned. We worked for decades to improve the quality of programming, and here we are, in 2010, where the state-of-the-art is stuck essentially at C, and the C fanatics wonder why we say there are problems. There are problems because nobody actually pays attention to the past, looks at past successes or failures, but just start, from scratch, reinventing the same bad ideas over and over and over again. We just re-invented timesharing, which we realized by the early 70s was a Bad Idea. Now we call it "cloud computing". Duh. For all the same reasons that timesharing was bad in the 1970s, cloud computing is bad. So if I seem overly cynical, remember that this is not the FIRST time I've seen bad ideas re-invented; I've been around long enough to see most of them re-invented two or three times. Approximately a generation apart. Unfortunately, I'm not the kind of old codger who longs for the "good old days". The best part of the good old days is that they are in the past, and we have grown beyond them. And then someone comes along and tells me that the good old days were the best time, and the ideas we tried and abandoned are essential to good software. I am skeptical of this. Why aren't we programming in functional languages? Why do we even still have compilers that run as separate preprocessors to execution? (Seriously: trace-based compilation systems exist, and run, and are used every day, and here we sit with C and C++ and VB and C# compilers, stuck in the punched-card model that I had abandoned by 1969, forty years ago. C/C++ used to have a working edit-and-continue system until it was broken, and while C# and VB make such a system trivial, they never seem to have had it. Duh. We've gone backward since VS6, instead of forward. Callbacks were a bad idea; we knew better how to handle this in the early 1970s. Look at LISP closures, for example, and the large number of languages that managed to implement closures by the 1980s. Mutex-style synchronization was dead by the end of the 1980s, but not one of the good implementations of interthread synchronization made it to commonly-used languages. So we see deadlock problems. Callbacks, by the way, introduce problems in reasoning about program logic which makes reasoning about deadlock causes much harder, so my observations are *not* irrelevant to the OP. I've been doing multithreading since 1975 as a way of life, and was even doing multithreading back in 1968. And I learned that explicit locking is usually a mistake. It is a hack to solve a problem that should be solved by architecture, and the low-level lock we are familiar with, although it needs to exist, should be completely invisible to the programmer (example: putting elements in queues requires a lock at the lowest level, but I should never see that or have to reason about it). People who worried about these issues have won Turing awards (the computer profession's equivalent of the Nobel Prize) yet not a single one of their ideas exists in our programming languages. The "synchronize" capabilities of Java and C# are deeply flawed (a friend of mine just got his PhD for building a program that finds synchronization errors in Java, and his comment is, "Everyone gets threading wrong all the time. Start with that as your premise" and has the experience of examining, with his program, over half a million lines of Java written by some of the best Java multithreading experts to demonstrate that even they made serious errors). So yes, we are, for all practical purposes, programming largely using 1970 technology, except when we are using 1960 technology. In the 1980s, I was one of the founding scientists of the Software Engineering Institute, and was examining the best-of-the-best technology so we could figure out how to get it into the mainstream. The mainstream didn't want it; they were content with 1960s technology because they were comfortable with it. Learning new stuff, and better ways to do things, was not an acceptable agenda. I left the SEI when I realized that (a) it had nothing to do with the actual *Engineering* of software, but was concerned with the *management* of the process and (b) industry didn't want to change what it was doing for any reason, no matter how cost-effective it might be in the long run. I really don't want to live in the past, and when I complain that yet again we are replicating the technology of the 1950s and 1960s, somebody comes along to explain that it is necessary we do so because there aren't better ways. There have *always* been better ways. Consider: JavaScript, the darling of AJAX, is just Simula-67 done badly. The heart of AJAX, XML, was done better in both 1977 (in a project I was responsible for) and 1981 (in a PhD dissertation done by a friend, an outgrowth of refining the problems we discovered in the existing 1977 implementation). DTDs were designed by people who had no experience designing languages or grammars (just ask any professional language designer. We still have quite a few around, including a friend of mine who designed the Ada competitor). Those of us in our 60s, who were there at the leading edges in the 1970s through 1990s, look around and see that there has been very little progress made in forty years. What we lament is not that the good old days are gone, but they are, alas, still with us. joe **** > >Anyway, it would interesting to hear your critic on the design faults >of the human brain! :) > >-- > >Joseph M. Newcomer wrote: > >> See below... >> On Sat, 13 Feb 2010 18:44:35 -0500, Hector Santos <sant9442(a)nospam.gmail.com> wrote: >> >>> Joseph M. Newcomer wrote: >>> >>>> Again, you are dragging IPC into a discussion where IPC was not involved. You can do >>>> callbacks such as event sinks without exposing the ugliness of the raw callback mechanism. >>> >>> Ok, lets try to get this debate civilly. I don't care for history so >>> please try to refrain from personal opinions there, i.e. callback is a >>> C hack. >> **** >> Actually, a callback is an assembly code hack, translated into C. This is not an opinion, >> it is a statement of fact. Callbacks at this level exist because the languages were >> unable to provide a suitable abstraction, so instead of some clean mechanism, the old >> let's-use-a-pointer technique was recast from assembler to C. It is a hack in that it is >> simply a translation of a machine-level concept directly into a high-level language. >> >> OTOH, the notion of virtual methods as a means of invoking operations is consistent with >> the linguistic design of C++, and although it is done *exactly* by using a pointer to >> redirect the call, it is done in a framework that is semantically consistent with a >> high-level abstraction. >> **** >>> What do you consider is a raw callback mechanism? >> ***** >> call [eax] >> >> substitute other register names, it is isomorphic to renaming. Expressed in C, it is >> implemented by passing in a function pointer as a parameter to a call, with the purpose >> that the specified function is called when there is a desire to invoke some operation. It >> is limited to a single function in general. >> ***** >>> Now, in the previous message, you stated that if it didn't provide a >>> user-define value, then YES, I will agree that a callback mechanism >>> that doesn't take into account: >>> >>> 1) Rentrancy >>> 2) Provide for user-defined object/data access, >>> >>> then yes, it is a poor implementation. I agree, and when dealing with >>> 3rd party software with callback logic lacking the above, especially >>> #2, then absolutely, its problematic. >> **** >> Sadly, most 3rd party software fails in both the above. The Windows API has far too many >> callback-style APIs that fail in the same way, including, inexcusably, some that were >> added in either 2000 or XP, when the issues were well-known, but totally ignored. >> **** >>> But that is an implementation issue, and not a language or "C Hack" >>> issue. You can say that the C++ layer provide a cleaner interface, >>> and I agree, but I will also note that these are generally based on a >>> lower level callback abstraction. In fact, I seem to recall dealing >>> with 3rd party software where it lacked a user-defined value and using >>> a C++ wrapper help resolved that by making the callback static in the >>> class, and combining it with TLS. I forget that project, but I do >>> recall going through those motions. >> ***** >> The issue is in confusing an interface that works directly and "in-your-face" with raw >> function pointers, and one which uses the syntactic features of the language (like virtual >> methods, a first-class concept) to disguise the implementation details and provide for a >> degree of cleanliness and elegance. For example, compare try/catch/throw or _try/_except >> to setjmp/longjmp as mechanisms. All are stack unwinders. But setjmp/longjmp is an >> inelegant kludge compared to _try/_except, and neither will work in C++ because of the >> need to invoke destructors as the stack unwinds. >> >> Note that in what is now my 47th year as a programmer, I have used callbacks under all >> kinds of conditions; I have grown to despise it as a way of life, particularly because it >> is so easily misused and abused. I have written stack unwinders, I have implemented >> try/catch mechanisms, I have implemented event notification systems, interrupt handlers, >> and pretty much everything that is possible to do with pointers-to-functions. And I >> prefer to use the C++ virtual function model. Note that the message map is actually a >> poor kludge; in a sane world, they would have been virtual methods, but in 16-bit windows >> the vtables would have gotten too large and the message map was invented as a more compact >> representation of the abstraction. It might have even worked well if they had gotten just >> a few more details right, such as always accepting the parameters passed to the superclass >> (this is a failure of design and implementation of a callback mechanism!). So I've seen >> all possible mistakes, and even made quite a few of them, particularly in my first ten >> years or so of programming. >> >> So I recognize the difference between a low-level hack and a methodology that is part of a >> linguistically consistent framework. >> >> I was shocked when Ada did not allow the passing of function pointers, wondering how it >> was possible to specify callbacks. Then I realized the language had alternative >> mechanisms that eliminated most of the need for user-specified callback functions >> (internally, the generated code performed callbacks, but you didn't have to see that). >> This was in the late 1970s, and since then I have realized that the raw callback is just a >> continuing hack to get an effect that should be achievable in other ways. The C++ virtual >> method (or C#, or Java virtual method) is one of these mechanisms. And there are those >> who will argue that even virtual methods are a hack, and that embedding, using interfaces, >> is the only way to go (e.g., COM, and the new Google GO language, which I have seen only >> little bits of). Ultimately, we want to get away from the decades-old concepts (like the >> computed GOTO) that were done to allow high-level programmers create constructs that >> compiled into efficient code at the low level, and go for clean abstractions (which most >> decent compilers can compile into incredibly good code at the low level, with no effort on >> the part of the programmer. I used to say that in a good language with a good compiler, >> you can write six levels of abstraction that compile into half an instruction. I've >> worked with good languages and good compilers, and have done this, repeatedly). >> >> It's too easy to get captivated by the implementation and forget that the implementation >> details are best left to automated mechanisms. Compilers are really good at this sort of >> grubby detail. At the lowest level, the implementation might be the instruction >> call [eax] >> but you should never have to think of this at the coding level. The construct >> function(args) >> where 'function' is actually a pointer is too close to the call [eax] to be really >> comfortable. >> >> Fortunately, the tools we have for MFC eliminate many of the visible details of how >> message maps are actually dispatched. At the lowest level, it really is >> call [eax] >> (in fact, if you single-step through the AfxWndProc assembly code far enough, this is what >> you will see, isomorphic to renaming of the register). But as an MFC programming, I have >> a very high-level concept: add an event handler. I don't need to see the details of the >> implementation. It just works. Well, sort-of-works, but I've already described the >> problems there. Virtual methods, if you accept derivation as the way of creating new >> classes, do the same job. >> **** >>> So it depends on what kind of design you are referring too. Writing >>> an C++ implementation is excellent and most of our stuff is written in >>> this way using interfaces. But I guess I have to wonder why we use >>> C++ in general. I think it because of its >>> >>> - natural scoping capabilities, >>> - constructors and destructors, >>> - polymorphisms and interface. >>> >>> The virtual interface, a large part, but not the only part. >>> >>> Keep in mind you can duplicate all the above within pure C if you >>> provide the library code to do so. >> **** >> And you can write in assembler, too, but it doesn't mean it is a good thing most of the >> time. >> >> I'm working on a course in assembly code, because there are still people who need to work >> with it (yes, I was surprised, but the uses are legitimate). One of the surprises was the >> number of people who need to write really tight, high-performance SIMD code (apparently >> the x64 intrinsics don't produce particularly good code when they are used for this). But >> it doesn't mean that people should write apps in assembler. >> >> If my customer base accepted it, I'd be writing in C# or WPF, but they don't want this. In >> fact, are opposed to it (I'm not sure I follow the reasoning, but they write the checks, >> and I want to take their money). So I write in C++, and in a few cases in C (and I just >> finished a library in C, several thousand lines of code, in which callbacks form a >> particularly important part of the functionality. But I'd rather have done it in C++ and >> just supplied a pure virtual method. You would not BELIEVE what I saw done there when I >> got the library; it was a particularly ugly callback, well, I can't really call it >> "design", and "kludge" gives it too much dignity, but as redesigned, it is essentially a >> virtual method mechanism and therefore intellectually manageable, as well as handling a >> ton of problems the old mechanism simply ignored). So I still use them, but they *should* >> be avoided as a way of life in most coding. I nearly made all the complexity of the >> earlier kludge disappear in the new design, which took major rework to get complete and >> consistent. Doing a callback *right* isn't easy, and most people, I have found, take the >> easy solution. If you assume that you should avoid them, then you use them only when >> necessary, and ideally wrap enough syntactic sugar around them to make them go down easily >> (e.g., virtual methods in C++). >> joe >> ***** >> Joseph M. Newcomer [MVP] >> email: newcomer(a)flounder.com >> Web: http://www.flounder.com >> MVP Tips: http://www.flounder.com/mvp_tips.htm Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 5 Prev: "Problems" Next: How well can TextOut() handle Unicode? |