Casting away common sense



  • Just found this one in some seriously obsolete C code:


    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = (GrafPtr)inWindow))  {





  • That's old Mac code... bringing back memories...



  • [quote user="Carnildo"]Just found this one in some seriously obsolete C code:


    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = (GrafPtr)inWindow))  {



    [/quote]

    Eh, that's pretty minor. I went and looked it up in Inside Macintosh Volume I (I found the original series at a used book sale -- Volume 6 is fascinating, if hefty) and:

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. (Of course, in later years Apple tried to make the whole thing opaque, and discouraged this sort of cast, but the API allowed this to work all the way up to the last version of the "Classic" Mac OS if I recall correctly, so this isn't such a big deal. May or may not work on Carbon, though, and of course this is utterly irrelevant in Cocoa.)



  • [quote user="The Vicar"][quote user="Carnildo"]Just found this one in some seriously obsolete C code:


    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = (GrafPtr)inWindow))  {



    [/quote]

    Eh, that's pretty minor. I went and looked it up in Inside Macintosh Volume I (I found the original series at a used book sale -- Volume 6 is fascinating, if hefty) and:

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. (Of course, in later years Apple tried to make the whole thing opaque, and discouraged this sort of cast, but the API allowed this to work all the way up to the last version of the "Classic" Mac OS if I recall correctly, so this isn't such a big deal. May or may not work on Carbon, though, and of course this is utterly irrelevant in Cocoa.)
    [/quote]

    Other than the completely pointless and portability-breaking cast, it's minor.  I found out about it while moving some code from CarbonLib/CFM (where it works) to Carbon Framework/Mach-O (where it doesn't).



  • [quote user="Carnildo"][quote user="The Vicar"][quote user="Carnildo"]Just found this one in some seriously obsolete C code:


    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = (GrafPtr)inWindow))  {



    [/quote]

    Eh, that's pretty minor. I went and looked it up in Inside Macintosh Volume I (I found the original series at a used book sale -- Volume 6 is fascinating, if hefty) and:

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. (Of course, in later years Apple tried to make the whole thing opaque, and discouraged this sort of cast, but the API allowed this to work all the way up to the last version of the "Classic" Mac OS if I recall correctly, so this isn't such a big deal. May or may not work on Carbon, though, and of course this is utterly irrelevant in Cocoa.)
    [/quote]

    Other than the completely pointless and portability-breaking cast, it's minor.  I found out about it while moving some code from CarbonLib/CFM (where it works) to Carbon Framework/Mach-O (where it doesn't).
    [/quote]

    Not surprising. One of the design goals for Carbon was to make system data structures opaque in order to make the code portable to other underlying systems. Most likely, Carbon/CFM is calling the original "Classic" code, where the first thing a WindowPtr points to is a valid GrafPtr, and Carbon/Mach-O is doing some extra legwork under the surface.

    Do you get to deal with an event loop, too, or did the genius who wrote the original code at least port the event loop to use Carbon Event Manager callbacks?



  • Is there a good reason they didn't just do 
     
    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = inWindow))  {

    That would seem to be the most logical way of doing it, as their method casts it to GrafPtr and then straight back again without even using it in between.



  • That, Kemp, is why it's a WTF :)



  • [quote user="The Vicar"][quote user="Carnildo"][quote user="The Vicar"][quote user="Carnildo"]Just found this one in some seriously obsolete C code:


    long pascal NewControl (WindowPtr inWindow, Str255 wTitle, rectangle *bounds)
    {
    WindowPtr w_ptr;
    if (!(w_ptr = (GrafPtr)inWindow))  {



    [/quote]

    Eh, that's pretty minor. I went and looked it up in Inside Macintosh Volume I (I found the original series at a used book sale -- Volume 6 is fascinating, if hefty) and:

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. (Of course, in later years Apple tried to make the whole thing opaque, and discouraged this sort of cast, but the API allowed this to work all the way up to the last version of the "Classic" Mac OS if I recall correctly, so this isn't such a big deal. May or may not work on Carbon, though, and of course this is utterly irrelevant in Cocoa.)
    [/quote]

    Other than the completely pointless and portability-breaking cast, it's minor.  I found out about it while moving some code from CarbonLib/CFM (where it works) to Carbon Framework/Mach-O (where it doesn't).
    [/quote]

    Not surprising. One of the design goals for Carbon was to make system data structures opaque in order to make the code portable to other underlying systems. Most likely, Carbon/CFM is calling the original "Classic" code, where the first thing a WindowPtr points to is a valid GrafPtr, and Carbon/Mach-O is doing some extra legwork under the surface.

    Do you get to deal with an event loop, too, or did the genius who wrote the original code at least port the event loop to use Carbon Event Manager callbacks?
    [/quote]

    I wish.  The original code was written for the Toolbox, back in the days when you had to worry about the differences between System 6 and System 7.  Untangling the event loop and converting it to Carbon Events sits somewhere after converting from QuickDraw to Quartz and getting rid of FSSpecs on the to-do list.

    The real WTF here is that this code was written between 1986 (oldest comment date I've found so far) and 1994 (newest), with no maintainence other than the minimum of changes needed for it to compile and run under CarbonLib.



  • [quote user="The Vicar"]

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. 

    [/quote]

      No you can't; or rather, just because it is possible doesn't imply it is either safe or correct.  You can run into type-aliasing problems in an optimising compiler if you play these sort of games.  It's fragile as well, because it breaks if anyone re-orders the struct.  And it would be completely out of the question if we were discussing C++ class objects wtih vtables.  If you want a pointer to the GrafPort which is the first member of the struct, what on earth's wrong with

                  &inWindow->grafport

    which has the advantages of being both type-safe and automatically future proof against any possible rearrangement of the struct?  It's not even any less typing the unsafe way, because what you lose by adding a member reference you gain by omitting the cast; with any reasonable-length member and type names, there can't be more than a few keypresses in it.

      Your level of cost-benefit tradeoff may be different from mine, but for me, the trivial gain of saving two or three keypresses when writing the code is vastly outweighed by the costs of the risk of silent bad code generation and the extra work of future maintenance.  In professional software engineering, those are both *serious* issues.

     

     

      Oh, and the *real* WTF is that both w_ptr and inWindow are WindowPtrs, and there's no need nor use of a GrafPtr at all, so it's utterly irrelevant and pointless as well as technically incorrect and potentially dangerous!

     



  • [quote user="DaveK"]

    [quote user="The Vicar"]

    1. A WindowPtr is a pointer to a Window Record
    2. A GrafPtr is a pointer to a GrafPort Record
    3. The first item in a Window Record is a GrafPort Record.

    So you can freely cast back and forth between the two. 

    [/quote]

     
    No you can't; or rather, just because it is possible doesn't imply it
    is either safe or correct.  You can run into type-aliasing
    problems in an optimising compiler if you play these sort of
    games.  It's fragile as well, because it breaks if anyone
    re-orders the struct.

    [/quote]

    Technically yes, but reordering the struct was not possible. This is a structure whose content was part of Apple's public documentation since day one, back in 1984 or thereabouts. Apple couldn't reorder the struct without redefining the Mac Toolbox API as a completely new set of libraries and headers. (Which is, in fact, what happened -- that's what Carbon is. And, as you would have seen if you had read the entire thread and seen the later exchange, even then this code only broke if you used the redefined API to program the sort of binary which only runs on Mac OS X.)

    [quote user="DaveK"]

    And it would be completely out of the
    question if we were discussing C++ class objects wtih vtables. 

    [/quote]

    Yes. Amazingly enough, though, we weren't. What a coincidence! You'd think I actually read the post and recognized the context, or something.

    [quote user="DaveK"] If
    you want a pointer to the GrafPort which is the first member of the
    struct, what on earth's wrong with

                  &inWindow->grafport

    which
    has the advantages of being both type-safe and automatically future
    proof against any possible rearrangement of the struct?  It's not
    even any less typing the unsafe way, because what you lose by adding a
    member reference you gain by omitting the cast; with any
    reasonable-length member and type names, there can't be more than a few
    keypresses in it.

      Your level of cost-benefit tradeoff may
    be different from mine, but for me, the trivial gain of saving two or
    three keypresses when writing the code is vastly outweighed by the
    costs of the risk of silent bad code generation and the extra work of
    future maintenance.  In professional software engineering, those
    are both serious issues.

      Oh, and the real WTF is that both w_ptr and inWindow
    are WindowPtrs, and there's no need nor use of a GrafPtr at all, so
    it's utterly irrelevant and pointless as well as technically incorrect
    and potentially dangerous!

    [/quote]

    Wow, really? Gosh, it must be nice to have such God-like intellect that you can notice such an obscure point! I would nev-- oh, wait, anyone with more than six brain cells and a knowledge of C syntax figured that out.

    Calm down, Sparky. Nobody's defending bad code. I'm just pointing out that the API this was written for was stable for about 15 years and explicitly made it clear from the very beginning that this bizarre over-typecasting would work. It's only because of the transfer from the Classic API to the Carbon API that this failed.

    (In case you're as ignorant of Mac APIs as your post makes you seem: Carbon is a reworking of the old Mac API with minimal differences in terms of function and data type names. The idea was to allow old Mac code to be recompiled to run on Mac OS X with the fewest possible changes -- ideally, a program could just have the Carbon libraries and headers swapped in for the old stuff -- or else to allow programmers to write for both the Classic Mac OS and Mac OS X at once, since Carbon shipped before Mac OS X did. If you have a background in Windows programming, this isn't exactly analogous to Win32 and Win64, because some things were killed off, changed, or replaced in the transition to Carbon, but it isn't the worst analogy either.)



  • [quote user="Carnildo"]The original code was written for the Toolbox, back in the days when you had to worry about the differences between System 6 and System 7.  Untangling the event loop and converting it to Carbon Events sits somewhere after converting from QuickDraw to Quartz and getting rid of FSSpecs on the to-do list.


    The real WTF here is that this code was written between 1986 (oldest comment date I've found so far) and 1994 (newest), with no maintainence other than the minimum of changes needed for it to compile and run under CarbonLib.
    [/quote]

    Ouch! How many lines of code is this? (If you don't mind my asking.) It might be easier just to rewrite it from scratch if it's really that awful. Just throw together an interface in Interface Builder and use custom NSViews to implement any non-control drawing.



  • Technically yes, but reordering the struct was not possible. This is a structure whose content was part of Apple's public documentation since day one

    I don't see why that necessarily makes reordering the struct impossible; things would still be source-code compatible if the member names remained the same but were in a different order (or, more likely, aligned to larger word boundaries or something of that sort). Okay, so it would require a recompile for the OS after they changed it, which would be a pain and make it unlikely to happen, but I wouldn't put it past MS for sure.

    Even if it was 'guaranteed' to work, it's still (imho) a WTF to cast instead of use the reference to the first element, unless it's much faster (don't see why it would be, both should compile to the same thing). But that's why you submitted it, right ;).



  • Not all programs are written in C.  Someone's m68000 assembler program relies on that structure having a particular layout just as much as the C program does.  You're right about casting pointers between unrelated types being a WTF, though.



  • [quote user="Bob Janova"]

    Technically yes, but reordering the struct was not possible. This is a
    structure whose content was part of Apple's public documentation since
    day one

    I don't see why that necessarily makes reordering the struct impossible; things would still be source-code compatible if the member names remained the same but were in a different order (or, more likely, aligned to larger word boundaries or something of that sort). Okay, so it would require a recompile for the OS after they changed it, which would be a pain and make it unlikely to happen, but I wouldn't put it past MS for sure.

    [/quote]

    What part of "Apple's public documentation" and the earlier reference to "Inside Macintosh" made you think Microsoft had anything to do with this? It's code from an old Mac application, which is being ported to the revised API. Unlike Microsoft, Apple tries not to deliberately break system-level stuff that third parties rely on.



  • [quote user="The Vicar"]Unlike Microsoft, Apple tries not to deliberately break system-level stuff that third parties rely on.[/quote]

    What a screwy business model! They'll never hope to succeed if they don't make a concerted effort to consistantly piss off their dev community. Sheesh!



  • [quote user="The Vicar"][quote user="Carnildo"]The original code was written for the Toolbox, back in the days when you had to worry about the differences between System 6 and System 7.  Untangling the event loop and converting it to Carbon Events sits somewhere after converting from QuickDraw to Quartz and getting rid of FSSpecs on the to-do list.


    The real WTF here is that this code was written between 1986 (oldest comment date I've found so far) and 1994 (newest), with no maintainence other than the minimum of changes needed for it to compile and run under CarbonLib.
    [/quote]

    Ouch! How many lines of code is this? (If you don't mind my asking.) It might be easier just to rewrite it from scratch if it's really that awful. Just throw together an interface in Interface Builder and use custom NSViews to implement any non-control drawing.
    [/quote]

    Nobody knows or is ever likely to know.  The event loop is strictly confined to the cross-platform compatiblity library (the code this WTF came from), while QuickDraw is almost entirely found in the cross-platform and word-processing libraries.  FSSpecs, unfortunately, are found not only in the cross-platform library, but also in both database libraries, the spellcheck library, and even in the main program code.

    The full codebase is slightly over a million lines of code, accreted over somewhere between 15 and 25 years.  I think the move from the Apple II and C64 to Windows 3.0 and Macintosh System 6 represented a complete re-write, but I wouldn't swear to it.

    What I'd really like to do is replace the current cross-platform library with WXWidgets, drop the network-shared database and replace it with a proper client-server setup, completely separate the application code from the display code, and remove the legacy support for System 7 and Windows 3.1.  No way I'll get approval to do that, though.



  • [quote user="Carnildo"][quote user="The Vicar"][quote user="Carnildo"]The original code was written for the Toolbox, back in the days when you had to worry about the differences between System 6 and System 7.  Untangling the event loop and converting it to Carbon Events sits somewhere after converting from QuickDraw to Quartz and getting rid of FSSpecs on the to-do list.


    The real WTF here is that this code was written between 1986 (oldest comment date I've found so far) and 1994 (newest), with no maintainence other than the minimum of changes needed for it to compile and run under CarbonLib.
    [/quote]

    Ouch! How many lines of code is this? (If you don't mind my asking.) It might be easier just to rewrite it from scratch if it's really that awful. Just throw together an interface in Interface Builder and use custom NSViews to implement any non-control drawing.
    [/quote]

    Nobody knows or is ever likely to know.  The event loop is strictly confined to the cross-platform compatiblity library (the code this WTF came from), while QuickDraw is almost entirely found in the cross-platform and word-processing libraries.  FSSpecs, unfortunately, are found not only in the cross-platform library, but also in both database libraries, the spellcheck library, and even in the main program code.

    The full codebase is slightly over a million lines of code, accreted over somewhere between 15 and 25 years.  I think the move from the Apple II and C64 to Windows 3.0 and Macintosh System 6 represented a complete re-write, but I wouldn't swear to it.

    What I'd really like to do is replace the current cross-platform library with WXWidgets, drop the network-shared database and replace it with a proper client-server setup, completely separate the application code from the display code, and remove the legacy support for System 7 and Windows 3.1.  No way I'll get approval to do that, though.
    [/quote]

    Wow, in about 3 years, I fully expect this will appear on the front page. It will be titled "Write Once, Run Away". It will begin with "What do you call a GUI library that runs on every version of Windows from 3.1 up, and every Macintosh released since 1986?"

    Have you tried making a case with your superiors/users/whoever is granting approval that Mac OS X should be considered a new platform entirely? If you could convince them to let you write a version from scratch, you could build it with WXWidgets (which would at least be a step in the right direction) and then maybe later tinker with replacing the code for the other OSes which can use WXWidgets with the same (or only slightly modified) code.

    I don't know whether to be glad that this horrifying task is at least going to someone who Has A Clue (TM) and actually knows what the Mac Toolbox is like, or be deeply depressed that there's someone out there who Has A Clue and actually has some deep knowledge of Mac programming and they're wasting their time trying to port code like this...

    On the plus side, if you manage to pull it off, you will have a cross-platform code library which technically works on every Macintosh released since 1986, which is no mean feat. (Of course, I have a suspicion that applications built with the library will require more RAM than the Mac Plus can accommodate, but still...)



  • [quote user="The Vicar"]What part of "Apple's public documentation" and the earlier reference to "Inside Macintosh" made you think Microsoft had anything to do with this? It's code from an old Mac application, which is being ported to the revised API. Unlike Microsoft, Apple tries not to deliberately break system-level stuff that third parties rely on.[/quote]

    Hmm, I didn't mean it like that, but re-reading my post I can see that it wasn't clear. I meant, MS do this sort of thing all the time to the Windows API, so why is it a given that Apple wouldn't?



  • [quote user="The Vicar"]

    Amazingly

    What a coincidence!

    You'd think

    Wow, really? Gosh, it must be nice to have such God-like intellect
     
    Calm down, Sparky.
     
    ignorant

    [/quote]

      You're telling /me/ to calm down?  ROFL!  I was generalising the discussion to why it's a bad design idea in principle, not attacking you personally.  Seems you have a problem coping when people disagree with you.

     

      Oh, and a very Merry Christmas to you too  :-P~~~

     


Log in to reply