TweetFollow Us on Twitter

PowerPlant Tips

Volume Number: 15 (1999)
Issue Number: 4
Column Tag: PowerPlant Workshop

PowerPlant Tips, Tricks, and Gotchas

by John C. Daub, Austin, Texas USA

Tidbits of knowledge to improve your use of PowerPlant

Hello, Again

I hope you've been enjoying my "series" of articles on PowerPlant as they've appeared here in MacTech Magazine. I've enjoyed writing it. Just about a week before I started writing this article, one of the hard drives on my Mac at home starting making a strange clicking noise, so I've disabled it - there went my development drive. So since I don't have all my normal resources at my immediate disposal, I thought this month I'd turn to the Internet for some input and posted a question to the comp.sys.mac.oop.powerplant newsgroup. I asked the readers to post their favorite PowerPlant "tip, trick, or gotcha". I received a few, but one reader had a lot to say. I've dedicated a special section of this article to his experiences :-)

This is the sort of stuff that you learn through experience. As much as Carl tries to document PowerPlant, most of these things you just don't appreciate until you get bitten, and especially if they save your hide or find that niggling bug that you haven't been able to squash for the past three months. This isn't to say I'm the most experienced programmer out there, but rather to try to put some of those experiences of mine and your fellow PowerPlant-users down in print to document them. A magazine article's a good medium for this purpose. So I tried to collect from various peoples and sources and put down what's relevant today. Some of this might still be relevant years from now, others obsolete, or maybe all of this moot.

But still, I remember what it was like when I started. It's a steep learning curve to get into programming, and definitely into PowerPlant. Once you're there it's not always easy, but it's certainly not as difficult as when you started. Hang in there. I remember all of the wonderful people that helped me when I was starting (Marco, Dan, Tim, Eric, Greg) and still help me today (especially you, Greg) and so I'm just trying my best to return the favor. It's this really cool Mac developers community that we have full of openness and helpfulness. Maybe that's why Mac folk seem to find linux-ppc really cool (and can't wait for OS X). And that's why I love to stick with Mac. If we can keep this kind of community of sharing going on, that'll benefit us in the long run.

So read on. I hope you'll learn something new. I know that I did.


Let's start out with some general tips for using PowerPlant. These should remain valid for a while as they are more principles of PowerPlant's design and implementation style than matters of code. After Tips will come the Tricks, and following that the Gotchas. The numbers are present for ease of reference. I present these in no particular ordering, except for this first one:

1. Always read the release notes (or, RTFM)

This cannot be stressed enough. PowerPlant is a very dynamic framework that is ever-evolving to suit the needs of Mac OS software developers. It's certainly frustrating at times to have to keep up with each release, but you must. Reading the release notes is the shortcut to seeing what changed and learning how to quickly update your project to suit.

There are sections in the release notes that deal with new features, a detailed change log, possible referrals to documents and PowerPlant TechNotes (yes, we have them too). And most importantly, we try to catch all of the places where existing projects will have to tweak something and report those first in the notes file. Heed them and it should eliminate most of your upgrading pains and posting to the newsgroups (or emailing Metrowerks Support). You'll get faster turnaround if you RTFM first.

And if you do have trouble, of course email Metrowerks Support at <> and/or post to the comp.sys.mac.oop.powerplant newsgroup. There's usually someone just a keyboard away that can help.

2. Check the Reference CD

On your Reference CD in the PowerPlant Examples folder we keep a great deal of information about PowerPlant. Not only examples, but also archives of obsolete code, TechNotes, and archived release notes. Yes, all PowerPlant release notes archived back to DR/1. Eases those upgrades when you jump a couple releases.

PP TechNotes is our folder of PowerPlant Technical Notes (just like Apple's TechNotes). This is a good place to go for explanation on greater concepts like the design of the Appearance Classes, or understanding the Networking and Internet Classes, docs for Andy Dent's More Table Classes, and many other items of interest. Worth checking out.

3. Keep up with OS evolution

Although PowerPlant is written in C++ and is an application framework (see my "What is PowerPlant?" article , December, 1998 MacTech), it remains very close to the Toolbox. A lot of people are after higher levels of abstraction and encapsulation these days, mostly because it aids porting to other platforms. PowerPlant doesn't want to prevent you from going cross-platform, but it'd rather focus on the Mac. Do one thing really well instead of trying to do more and failing.

And so PowerPlant must keep pace with the Mac OS as it evolves. ATSUI, Carbon, OS X, OpenGL. There are some great things in store for the future of Mac. PowerPlant will be there, we all have to keep up.

4. This is C++. Take advantage of what the language has to offer

As the C++ language has evolved and compilers race to implement the standard, PowerPlant has made more and more use of the C++ language. By the time you read this, CodeWarrior Professional 5 should have shipped and hopefully you have a copy on your desk. PowerPlant now uses a real exception class, LException, publicly inheriting from std::exception as the means of providing information about error conditions (instead of the ExceptionCode, which is just a typedef'd long). Templates are being used more (TArray, I'm working on a template version of UMemoryMgr for post-Pro 5). RTTI is utilized throughout the framework. C++ is a good language - strive to take advantage of it.

So as well, use classes like TArray to manage your lists of data; use LString and friends LStr255, LStringRef, and TString (see my article on LString in the January, 1999 issue of MacTech). Be typesafe. Learn how great stack-based classes are for resource management and exception safety. Work logically.

5. Use compiler warnings

Compiler warnings are there to help you. They are not errors, which mean something is certainly wrong, but rather they are warnings - informative messages to try to point out potential problems. Turn them on, use them on your code. They can prevent problems.

Figure 1. CW Pro 4 C/C++ Warnings Preference Panel.

As you can see in Figure 1, there is a good deal of coverage performed by the Metrowerks C/C++ compiler. I'd like to talk about three warnings: Implicit Arithmetic Conversions, Non-Inlined Functions, and Hidden Virtual Functions.

Implicit Arithmetic Conversions is a warning issued "if the destination of an operation isn't large enough to hold all possible results. For example, assigning a value of a variable of type long to a variable of type char will result in a warning if this option is on" (Metrowerks 1998, CCR-26). PowerPlant strives to compile cleanly with this warning on, but some areas do slip through (e.g. Grayscale Implementations of the Appearance Classes). Barring that one point, PowerPlant should compile cleanly with this option.

Non-Inlined Functions is a warning that provides you with information on what you asked the compiler to inline but it did not for some reason (remember, inline is just a suggestion). Aside from the usual guidelines for inlining, the inline depth, a C/C++ compiler setting, does affect the output of this warning. Depending upon your inline setting, you may receive these warnings on PowerPlant code. Play around with the inline levels and see how that affects your resulting binary. Choose what works for you. Generally I leave this option off and just use it when I wish to fine-tune my inline settings for maximum gain from this C++ language feature.

Hidden Virtual Functions is another warning with which PowerPlant should compile cleanly. PowerPlant does compile cleanly with this warning enabled, but not without a little help from a #pragma. TArray hides some inherited virtual functions, but due to the design and intent of the class we can guarantee this hiding is safe; in fact it is needed for the intent of the class. But when you use a TArray, you receive several hidden virtual function warnings. To quiet the compiler perform an explicit instantiation of the template or just at the point of instantiation, wrap it with #pragma warn_hidevirtual off/reset like this:

   #pragma warn_hidevirtual off
         template class TArray<FooData*>;
   #pragma warn_hidevirtual reset

Using a technique like this you are able to ensure quiet and clean compiles with TArray. Look in the PowerPlant sources - this technique is used throughout the framework.

All compiler warnings are useful and generally I leave all on except the non-inline and arithmetic conversions warnings, turning those on as needed. Refer to the C Compilers Reference on your CodeWarrior Reference CD for more information on the warnings and other language preferences.

6. Explore unfamiliar areas of PowerPlant

I do not know all of PowerPlant, but I do my best to at least have a familiarity with all of PowerPlant. It takes time to learn and master every aspect, and with PowerPlant so large it's difficult to know it all. But if you are at least familiar with what PowerPlant has to offer then in the future when you have a new project, by knowing what PowerPlant has to offer you can determine if it's the right tool for your new job. Or at least you can avoid reinventing the wheel. I've seen a lot of people spend hours writing something only later to find that PowerPlant already had something better.

For example, do you know what LSharable is? Have you tried using the AppleScripting support? What about the Networking and Internet Classes? Thread Classes? Debugging Classes? Keep exploring PowerPlant. It has a lot to offer.

6a. Check the Contributed Class Archives

Located off Metrowerks' website you will find the PowerPlant Contributed Class Archives, sometimes called the "contrib archives", or some similar abbreviation. This is an archive of PowerPlant classes submitted by your fellow PowerPlant users for your use. The story usually goes that someone had a problem they needed solved, they couldn't find an existing answer so they invented their own. The Warrior believed their situation wasn't a unique experience, so they submitted their code to the archives for public distribution. You can find a lot of great stuff on there.

Some of the code is old, might be out of date, might not even work. Metrowerks can do nothing about this. All Metrowerks can and does do is provide the web and ftp space, and the search engine. Metrowerks cannot vouch for the code on the archive, cannot support, cannot fix, etc. that code. For any issues with items on the contrib archive, you will need to contact the author of the code for support.

And now to speak a bit more on a code level....

7. Never assume the drawing state. Before you perform any operation that draws, always set the drawing state exactly as you need it.

That could be as simple as ensuring FocusDraw() is called (and don't forget that FocusDraw() returns a Boolean if it succeeded in focusing or not - check it), or it might require you to load text traits, ApplyForeAndBackColors, or a host of other things. The key to remember is to never assume the drawing state and always establish it as you need.

One common "rule" has been to save the current state, set it as you need, then restore the original state. Usually this was done because too many assumptions were being made, be it that the state wouldn't change or that nothing else could change the state or people just want to be paranoid and safe. If however you base upon the premise that everything must set their state before they draw, then the save and restore becomes unnecessary as there are no assumptions being made. To then save and restore adds extra overhead and bloat you just don't need.

Of course there are times where you must save and restore, and sometimes this rule has to bend a bit, but those are exceptions and not the rule.

8. Always check return codes

Exceptions are a wonderful part of C++, but the Toolbox doesn't throw exceptions; instead it returns error codes. Sometimes the error codes are a return code (FSWrite), sometimes returned as a parameter (TempNewHandle), sometimes indirectly (NewHandle, returns nil on failure), sometimes signaling to look elsewhere (NewHandle returning a nil Handle tells you to call MemError), sometimes in other places (ResError). But no matter where or how you obtain the error checking, always perform it and handle it as you can. If you can handle and recover, then do so. If you cannot, do what you can, perform any cleanup, and report that error (typically throwing an exception, e.g. ThrowIfOSErr_()).

If you don't check return codes and don't put a mechanism in place to handle those situations, then don't be surprised when you cannot explain the strange new behavior your application has now taken on.


9. Option-click to use the color picker in Constructor

The color popup button/menu that you see in Constructor editors such as the Text Traits editor (to edit the color of the 'Txtr') or the LWindow Property Inspector (to edit the 'wctb' resource) normally pops up a menu/palette of colors to choose from. However, if you find using the Color Picker more to your needs, option-click on the button and the Color Picker will appear.

10. Use drag and drop to create instances of widgets not within Views in Constructor

When you create a 'PPob' resource, typically you create a container View (LView, LWindow, LDialogBox, etc.) and place other widgets inside of the container. But what if you want to create a single instance of a widget, like a single LPushButton; perhaps to use as a "template" for some widgets you need to create on the fly. Simple. Drag an instance of the widget from the Catalog window into Constructor's Project Window (not to be confused with the IDE's project window). That will create a 'PPob' resource containing nothing but that particular widget. No container view. Totally independent. You can establish its properties like you normally do in Constructor (double-click, open Resource in the Layout Editor, double-click on what you see to open the Property Inspector window for the widget). To instantiate the widget at run time, use UReanimator::ReadObjects().

11. Use Constructor's hidden preferences

Did you notice that Constructor has no Preferences dialog? Constructor does have a Preferences file and it does have preferences, but no user-accessible means of manipulating those preferences. And with Constructor currently in maintenance mode, it's difficult to justify the effort required to add a Preference dialog. But that doesn't mean there aren't preferences that you can modify - you just have to know where to look.

Open a copy of the Constructor application in your favorite resource editor (I prefer Resorcerer). Look for the 'INSP' Resource. The contents of this Resource (all one byte of it) control the "pinned" state of the Property Inspector window. If the byte of this Resource is zero, there is only one Property Inspector window shared by all objects (of course you can pin individual instances of the window by clicking the "pin" icon). If this byte is a non-zero value, then all Property Inspector windows open "pinned". If you find yourself pinning the Property Inspector window a great deal, you may want to consider turning this byte on to ease your mouse-clicking finger.

Another hidden preference lies in 'STR#' 1099, index 4. The Constructor application ships with this string as "AM". In this mode, Constructor will attempt to use Appearance Implementations when rendering widgets in the Layout Editor (some widgets are hard-coded to use Grayscale Implementations due to Resource Manager limitations). If you change this string to "GA", Grayscale Implementations will be forced in the Layout Editor.

12. Mark your Resources as purgeable

When you access a Resource, the Mac OS Resource Manager first looks to see if that Resource is already in memory. If so, it returns a Handle to that existing instance of the Resource, else it loads a copy from disk. This is a wonderful way of reducing memory footprint as everyone shares a single instance of the Resource data. But what if you wish to have multiple instances of a particular picture in a window, and this picture comes from 'PICT' resource? If they all share the PICT data, if when a window closes it calls ReleaseResource upon its cached PicHandle, the next time the other window needs to redraw and accesses that now-released Resource via its now-stale PicHandle? You'll probably crash. It's not good to pull the rug out from under someone.

So instead, you could DetachResource, but that wastes memory by having multiple instances of the same (and typically read-only) data. Still, it's a way to go. Or, just never assume you have a valid Handle (as someone may pull the rug out from under you) - just GetResource every time you need it and cannot be certain of the Handle state. Then to follow through, never call ReleaseResource on that Handle; instead mark your Resource as purgeable and let the Memory Manager handle releasing it for you. Why release it before you need it? Your app will feel a lot slower if it has to load Resources from disk every time it needs them, so why not let them be cached in memory? Just remove them if you need it, and ensure they are always there when you need them. Again, never assume, always make the state known and certain.

PowerPlant works to practice this in all possible places, like handling 'Txtr' Resources (UTextTraits) and 'PICT' Resources (LPicture::DrawSelf()). Other Resources include: 'STR ', 'STR#', 'ICON', 'icl8' and other icon Resources, etc. It might make reports from tools like Spotlight a little noisy, but that's ok. I consider it a bug in Spotlight ;-)

13. Use the PowerPlant Resources folder

A lot of people do not realize that we do provide the necessary Resources for working in PowerPlant. In fact we do, and they are within the PowerPlant Resources folder. Here you will find all of the necessary resource files you need, like PP Action Strings.rsrc, PP AppleEvents.rsrc, and a host of other files needed for PowerPlant to work correctly. Then also some ones needed to get certain features to work: GetDirectory.rsrc, EmptyMenu.rsrc, CustomTextColorCDEF.rsrc. Finally, some utility files like Resorcerer and ResEdit TMPL (template) files, and PowerPlant.r for you Rez-lovers out there.

13a. Use Rez/DeRez for localization

Use the above PowerPlant.r file and Rez/DeRez your .ppob files to help you localize your PPob's. The IDE does support both Rez and DeRez.

14. The class browser is your friend

Learn what the Class Browser is and learn to love it. It's really great at helping you navigate code. I find it's a useful reference tool (what better way to know what a function or class does than to read source!). I also find looking up information in my project much easier (that Catalog window is wonderful). Class browsing is more effective for C++ (and other OO languages) because it lets you see the natural structure of the class, including what was inherited, overridden, overloaded, etc. Source alone sometimes cannot give you the full picture.

Also, to make life browsing (and debugging, and just in development overall), don't use precompiled header files (.pch, .pch++, etc.). I've found them to be evil. You're welcome to use them if you'd like, but I've found that a good prefix file is all you really need and quite less headache in the long run. Yes the compile speedup from pch's is nice, but hey... gives me time to refill my water mug at work. Use prefix files, don't use pch files.

15. Pay attention to new releases and possible changed code dependencies

PowerPlant is an evolving framework. Occasionally some code falls by the wayside sooner or later for some reason or other (no time to work on it, engineer transferred to another team, no longer a Metrowerker, obsolete technology (PowerTalk, PowerPart/OpenDoc)). To ease transition, any code that is moving obsolete we try to place into a Will Be Obsolete folder. Next release of PowerPlant, code in this folder becomes obsolete. Obsolete code means that it will no longer be actively developed by Metrowerks, no longer supported, no more bugs fixed, no more features added, no more upkeep, nothing. We'll ship it on the Reference CD in the archives and you're welcome to use it, but Metrowerks no longer supports them.

So to help you make the move, shield this folder and reset your access paths. Make. See what happens. Adjust as needed. Refer to Release Notes and Change Logs for complete details and information. Don't forget the Release Note archives on the Reference CD as mentioned in item 2.


16. When storing in an LArray, use TArray instead. Or if you really must use LArray, don't forget to pass by address.

One of the problems with LArray is its use of void*'s to pass arguments of data to and from the Array. This is needed to allow the storing of any data type, but is hardly typesafe - the void* will allow anything through, and there will be no help from the compiler to alert you to a potential problem situation. This is why TArray was created. It's a wrapper for LArray, but provides an Array class that is typesafe. It also simplifies using the Array classes as having type information allows TArray to handle some aspects for you (such as always providing the sizeof the data).

Another benefit of having type information is the elimination of using void* as an argument. Instead the methods of TArray take the data by its type and then by reference. This eases use in code and improves safety. But if you must use LArray, do remember to pass your data by address and not value. The following snippet should illustrate.

   LArray      theLArray(sizeof(int));
   int         theInt = 3;

      // This is wrong
   theLArray.AddItem( theInt, sizeof(int) );

      // This is right
   theLArray.AddItem( &theInt, sizeof(int) );

      // Or using TArray, this is a bit easier
   TArray<int>   theTArray;

17. Avoid conflicts. Adhere to PowerPlant conventions

Chapter 3 of the PowerPlant Book discusses the PowerPlant conventions. Even if you do not wish to adopt them for yourself, at least be aware of PowerPlant's style so you can know where to look to try to resolve conflict, and/or to avoid conflict altogether. You have a copy of this book on your Reference CD.

18. Erase On Update

Before drawing you ordinarily want to erase what was there before so there is no drawing of ghost and/or incorrect images. As an optimization for most needs, PowerPlant provides the typical topmost view (LWindow) with a feature to Erase On Update. By doing this the entire port is cleaned of artifacts so the subpanes can now draw freely as LWindow::Draw() iterates over them.

If you do not like the flicker this creates, you are welcome to turn the feature off. However, by turning the feature off, you assume the responsibility for erasing the drawing area. This may change the behavior of many Pane classes as they are written upon the assumption of this feature being active.

19. Create Appearance Controls first

With the advent of Themes and the concept of a root control, it is necessary to ensure this root control is truly the first control made in a window. PowerPlant creates a root control in LAMControlImp::MakeMacControl(). Ensure this is called (and hence CreateRootControl) before a call to NewControl is made. If you do not do this, both the call to GetRootControl and subsequently CreateRootControl will fail.

Typically you end up in this problem situation from mixing both Appearance and "Classic" Controls in the same window, e.g. a LWindow with an LStdButton and an LImageWell (respectively, in order of visual hierarchy). More specifically, it is usually because the Standard Control was created before the Appearance Control (hence NewControl is being called before CreateRootControl). This situation often arises if you have an LScroller or LActiveScroller in your PPob and it's created "first" (before any Appearance widgets are). Use LScrollerView instead.

Solutions: 1) Don't mix Control types within a Window. Use all Appearance or all "Classic". 2) Ensure at least an Appearance Control is created first (if you have to, embed a hidden widget like an LSeparatorLine just to force things through). That's about it. We thought through many possible solutions and none were truly optimal, but this worked the best given the nature of the situation (which if you're not familiar with, try DejaNews). IMHO, go with solution one.

20. Use LApplication as it was intended - to manage the Application object

This tip comes from Julien Guimont. He noted that as a beginner, he placed all of his application's behaviors into the Application object. It's certainly very tempting for a beginner to do this as it's very central, provides most of what you need, and you don't really need to branch out much more. Furthermore, if you're coming more from a procedural programming background, this is perhaps a more familiar and comfortable style. Although perhaps effective, it's not very PowerPlant.

Instead, factor your behaviors out to your objects. For example, your windows should be "self-sufficient" and able to manage itself and its own behaviors. Learn to think more about the data and how to manipulate it instead of how to get from point A to point B. If you want some Button to do some thing when some occurrence happens, then have the button Listen for that occurrence instead of some totally unrelated object listening for it and then dispatching to the Button. This is what object-oriented programming is all about.

21. Let LPane come first in class declarations

Greg Dow wrote: "When deriving from LPane (or any subclass of LPane) and one or more other mix-in classes, make LPane (or the LPane subclass) the first base class in the class declaration. For example:

   class MyWindow : public LWindow, public LListener // Right
   class MyWindow : public LListener, public LWindow // WRONG

When creating objects from a PPob resource, UReanimator and URegistrar cast pointers to LPane objects to/from (void*). If the LPane class or subclass isn't the first base c lass, these casts may not work properly."

This is less of an issue today since RTTI is used throughout PowerPlant, but some people still avoid RTTI and use unsafe C-style casts. Be safest and adhere to this rule.

David Phillip Oster

David Phillip Oster I have never met in person, but have interacted with a great deal on comp.sys.mac.oop.powerplant. David's always been one of the active and helpful members of our community of PowerPlant users and readers/posters to the PowerPlant newsgroup. When I posted to the newsgroup asking for contributions to this article, David responded with three lengthy and detailed posts. I was quite happy to have elicited this sort of response (and wondered just how much free time David must have).

With David's permission I am reprinting the full text of his posts. I did perform some editing for typos and layout, but the meat of the following text is all David's. These posts were made to the comp.sys.mac.oop.powerplant newsgroup on 16 January and 18 January, 1999. Although not all of the items pertain directly to PowerPlant, they certainly will aid the PowerPlant developer. David, thanx a bunch.

Here are a few, off the top of my head:

DebugNew is your friend

DebugNew.cp is your friend. Read it, use it. Configure it in your debug builds to do full checking. Learn to interpret its leaks report. Use "NEW" instead of "new".

Assert_() is your friend

Know when to use Assert_() (to catch programmer-time errors) versus when to call Throw_, or one of its variants (to catch run-time errors). Sprinkle your code with Assert_() sanity checks:

   LPane* thePane = someView->FindPaneByID(pane_TheFooPane);
   Assert_(thePane != nil);

This will fire if you munged your resources in Constructor (a programmer-time error). We use Assert_() because if thePane is nil here, then we've got a wildly inappropriate resource and we need to get the programmer's attention to fix it now. But we don't necessarily need to check this in the release build (where debugging, including Assert_(), is turned off) because this error can only happen in the release build if the program is corrupt on disk, and if we are corrupt on disk, all bets are off. Its like checking to see if the power is on. Versus:

   ThrowIfOSErr_(FSMakeFSSpec(0, 0, "\pFooFile", &fooFS));

which is a run time error. The user deleted the FooFile, which we were expecting to be present. We use (a variant of) Throw_ so the program can detect the error, report and recover as appropriate, and keep going.

[The Debugging Classes have a nifty FindPaneByID_() macro that simplifies the former situation.]

Spotlight is your friend

Test with Spotlight. It will show you bugs that nothing else will.

Fat binaries are your friend

Build both 68K and PPC versions of your app. Test them both. This roots out bugs that just happen to work on one architecture or the other because of random chance and the memory layout.

[And do test your 68K binary on a real 68K machine, if you have access to one. I have seen a 68K binary run perfectly under emulation on a PPC but crash horribly on a real 68K. It turned out to be user coding errors, but they would never have been noticed if 68K was not tested under real 68K conditions.]

Scripting is your friend

Since you need to test them both anyway, make your app scriptable, and run it through a test script. When you find a bug, augment your test script so it tests that case and the result. That keeps the bug from creeping back in.

Avoid allocation in constructors. Where it is unavoidable, preflight

Avoid allocation in constructors. Where it is unavoidable, preflight. The Metrowerks C++ runtime doesn't gracefully handle a throw from a constructor. (Since an object is partially built the corresponding destructor isn't called, so that owned sub-objects can't be deleted. Not Metrowerks fault. It is a flaw built in to the C++ language.

  class Ca{
      Ca() { mAp = nil; mAp = NEW LArray; }
      ~Ca() { delete mAp; }

      LArray*   mAp;

   class Cb : public Ca{
      Cb() { mB2p = mB1p = nil; mB1p = NEW LArray;
               mB2p = NEW LArray; }
      ~Cb() { delete mB1p; delete mB2p; }

      LArray*   mB1p;
      LArray*   mB2p;

Suppose we say:

      Cb b;

First the constructor of Ca is called, allocating mAp. Then the constructor for Cb is called, allocating a mBp. The destructors reverse the process.

Now, let's try it again. Only this time, as we allocate mB2p, we run out of RAM, and NEW throws an exception. The destructor ~Cb can't be called, because the run-time system knows that it never finished constructing Cb. So, the super-class destructor is called, mAp is freed. mB1p is never freed. Since we threw because we are low on memory, it is sad that the memory is never freed.

How does PowerPlant handle this problem? It doesn't. Instead it has an LGrowZone class that reserved a 20K emergency buffer. If NEW eats in to the buffer, then the next time WaitNextEvent is called, you'll get a "Memory is Low. Please close some open documents" Alert. The buffer allows PowerPlant applications to eat in the reserve and still successfully allocate. Note that 20K isn't very much, so it is wise to check the free space in the heap before, say, opening a major window.

[There are ways around the situation David illustrated above, such as creating the LArray's as local objects instead of pointers to objects allocated on the heap, performing the allocation in an Init() function that users must call after object creation, or using a class like StDeleter.]

Try to use TArray<LType*> rather than LArray

LArray is sometimes confusing [see item 16 above]. Use the type-safe variant instead.

Use stack based classes like StValueChanger for exception safety

StValueChanger cleanly sets temporarily changed values back to their original value. When you can't use it, consider writing your own stack based class along the same lines, rather than cluttering code with an inline try/catch block. One advantage of the stack-based classes is that you don't need to know what type of exception to catch, cleanup after, and then re-throw. [files such as UMemoryMgr.cp/.h contain many stack-based classes].

Avoid calls to NewRoutineDescriptor(), use BUILD_ROUTINE_DESCRIPTOR

NewRoutineDescriptor() allocates memory, and if called in a loop can be a source of memory leaks. Build your own macros like this:

#define DeclareModalFilterUPP_(var, proc) \
   static RoutineDescriptor   var##RD = \
                           (uppModalFilterProcInfo, (proc));\
   ModalFilterUPP             var = (ModalFilterUPP) &var##RD

#define DeclareModalFilterYDUPP_(var, proc) \
   RoutineDescriptor          var##RD = \
   ModalFilterYDUPP         var = (ModalFilterYDUPP) &var##RD

#define DeclareModalFilterUPP_(var, proc)     \
            ModalFilterUPP     var = proc
#define DeclareModalFilterYDUPP_(var, proc)     \
            ModalFilterYDUPP   var = proc

Call them like this:

DeclareModalFilterUPP_(ucStandardFileModalFilterUPP, StandardModalFilter);

DeclareModalFilterYDUPP_(ucStandardFileModalFilterYDUPP, StandardModalYDFilter);

This defines variables named ucStandardFileModalFilterUPP and ucStandardFileModalFilterYDUPP which you can use in either 68K or PPC code, that are initialized at run time to either a procedure pointer or a universal proc pointer. Putting the macro definitions in a header file keeps the clutter out of your code.

Ignore constructor, use Rez.(or: #define is your friend)

If your define all your resources except pictures in Rez, then you get access to the C preprocessor. This gives you a number of advantages:

  1. You get consistent symbols between your PPobs and C++ code. Just include a header file written in the common subset of both languages and your paneIDs will be consistent, from C++ to Rez.
  2. You can use search and replace and spell checkers on your strings.
  3. You can parameterize your resources by target. (I build both the Pro and Regular versions of ImageAXS from the same source. In the header of each target I #define TARGET PRO or #define TARGET REGULAR and include Header.h. It has:
   #if PRO == TARGET
      #define programS "ImageAXS Pro"
   #elif REGULAR == TARGET
      #define programS "ImageAXS"
      #error unkown target

Then, in all my dialogs, strings, and menus, I build up the strings I need with expressions like:

   resource 'MENU' (kAppleMenu show_Apple) {
      kAllItems - (kItem2) ,
      {   /* array: 2 elements */
          /* [1] */   "About " kProgramS "S∨", kMenuItemStuff,
          /* [2] */   "-", kMenuItemStuff

That way, if the program name changes during development, I just change it in the header, and know the change is correctly applied through the whole program.

A similar trick separates out language dependent information (mostly strings and the screen rects that need to grow to accommodate long ones.) Another similar trick separates out style guides: you want a gutter of 4 pixels between items, and a 10 pixel border around all the items of the window? Just specify their positions using a #define macro that is an arithmetic expression of appropriate constants. Once you get in to this mode of thinking, you can design your own resource types that are compatible with Reanimator, and use it for persistent storage.

Make PowerPlant read-only

Use a version control tool, or a tool like File Buddy to set all the source code of PowerPlant (headers and .cp files) to be read-only. That way, you won't accidentally modify it. This will save you trouble when a new version of PP comes out and you want to upgrade.

[I prefer a version control tool as Finder locks are annoying. Furthermore, having PowerPlant in version control helps ensure your PowerPlant stays in sync, tracks any changes you might make to the PowerPlant sources, and if you're working in a multi-developer situation, ensures all team members are using the exact same PowerPlant.]

Adding your own code to a basic PowerPlant class with mix-in classes

Balloon Help is great for letting a user know what some graphical object on one of your windows does. It is even better for letting the user know why some object is disabled. Unfortunately, the Mac Toolbox support for Balloon Help only allows for a single disable string, so programmers generally put in a catch-all string listing all of the reasons that an object might be disabled.

A better way is, in your Balloon help support, try to dynamic_cast the pointer to the object that the mouse is currently over to CBalloonHelpMixIn. Recurse up the view hierarchy trying to dynamic_cast, until you get a non-nil pointer. If you get a non-nil pointer, call the BalloonCommandStatus() method, passing in the paneID. Then, in your classes, mix-in CBalloonHelpMixIn as an super-class, as appropriate, and implement BalloonCommandStatus(). This lets your window, or some portion of it, return a Balloon Help Record, referencing a 'STR#' and strID, so that the Balloon Help system can explain to the user exactly why he can't do that right now.

By implementing this as a mix-in class, you write functionality once, where you need it, and mix it in to all of your windows and views that need it. An alternative would be to actually go into the source for LPane or LView. But, then you'd have trouble moving your changes to the next version of PowerPlant.

Adding your own code to a basic PowerPlant class by editing a PowerPlant file

If you really need to change something in PowerPlant, create a PP Overrides folder, and put it ahead of the PowerPlant folder in the Access Paths panel of the Target(s)' Settings. That way, your modified version of LPane.cp will be found before the official one. It will be in a separate folder, so that when you go to a new version of PowerPlant you won't accidentally overwrite it, and you'll be able to quickly compare your file with the official one to see if your changes are still appropriate.

Read the source code for PowerPlant

Often, when you have a design problem, you'll find that the designers of PowerPlant have already coped with that problem or a similar one, so you'll find a debugged pattern for solving your problem.

For example, in ImageAXS Pro an ImageAXS Pro document has many windows, and when the last window is closed, the document should close. But, when the app is closed, it closes all open documents, and when the document is closed, it closes all open windows. If you just wrote the code naively to handle the first case, you'd get in to a situation where the destructor of the window called the destructor of the document and the destructor of the document calls the destructor of the window. This is not only an infinite loop, but it is a call to a destructor of an object that has already been deleted (so DebugNew helps you crash early, while enough of the environment remains so you can still see what is going on). It's bad.

Let's look inside the source code of PowerPlant to see how it handles an analogous problem: Listeners and Broadcasters similarly keep track of each other, also LArrays and LArrayIterators. By making the window a Broadcaster and broadcasting the message msg_BroadcasterDied just before it dies (with itself in the refCon of the message), the document can clear out its pointer to the window and decide whether to die too (since it clears its pointer, its destructor will no longer try to destruct the now dead window.)

LSharable is another choice: if you re-write the way you handle assignments to variables, LSharable implements reference counting with deletion when the reference count is zero.

The Broadcaster/Listener pattern was a better fit for my application.

Read it again

When you get a new release from Metrowerks, read the source again. Metrowerks often adds new methods to existing classes and entirely new classes, and the best way to find out what is new is to read the source code.

Hope This Helped

What you've read here may or may not be news to you. If it is new, I'm glad that you've learned something new. If it's all old-hat to you, consider sharing some of your knowledge and experience with others on forums like comp.sys.mac.oop.powerplant to help further the community. And if you already share on the newsgroups, I'll see you there.

I'd like to thank DejaNews for its services; David Phillip Oster for his permission to reprint his postings; Greg Dow for correcting me (a lot), and letting me reprint some of his tips (read the TechNotes, you'll see).

If there's any single item I'd like you to remember from this article, not only should you read the PowerPlant source, but just make sure you read the Release Notes.

And then read them again. :-)

Happy programming!


  • Daub, John. "Arrays, Iterators, and Comparators... Oh My!" MacTech Magazine. Vol 15, No. 2. February, 1999.
  • Daub, John. "The Ultra-Groovy LString Class" MacTech Magazine. Vol 15, No. 1. January, 1999.
  • Daub, John. "What Is PowerPlant?" MacTech Magazine. Vol. 14, No. 12. December, 1998.
  • Metrowerks Corporation. Constructor Manual. 1998.
  • Metrowerks Corporation. PowerPlant Book. 1998.
  • Metrowerks Corporation. PowerPlant Reference. 1998.
  • Metrowerks Corporation. C Compilers Reference. 1998.
  • <>
  • <>
  • <>

John C. Daub is one of Metrowerks Corporation's PowerPlant engineers. He finds he enjoys framework authoring a little too much to be considered healthy. You can reach John via email at


Community Search:
MacTech Search:

Software Updates via MacUpdate

TotalFinder 1.12.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Features... Read more
Duet - Use your iPad as an exter...
Duet is the first app that allows you to use your iDevice as an extra display for your Mac using the Lightning or 30-pin cable. Note: This app requires a $9.99 iOS companion app. Version Read more
FileMaker Pro Advanced 18.0.3 - Powerful...
FileMaker Pro Advanced is the tool you use to create a custom app. You also use FileMaker Pro Advanced to access your app on a computer. Start by importing data from a spreadsheet or using a built-in... Read more
OsiriX Lite 10.0.6 - 3D medical image pr...
OsiriX Lite is an image processing software dedicated to DICOM images (".dcm" / ".DCM" extension) produced by medical equipment (MRI, CT, PET, PET-CT, ...) and confocal microscopy (LSM and BioRAD-PIC... Read more
Ableton Live 10.1.5 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Burn 2.7.8 - Easily burn data, audio, vi...
Burn... There are a lot of ways to approach burning discs. Burn keeps it simple, but still offers a lot of advanced options. Create data discs with advanced data settings like, file permissions, the... Read more
Malwarebytes - Adware remova...
Malwarebytes (was AdwareMedic) helps you get your Mac experience back. Malwarebytes scans for and removes code that degrades system performance or attacks your system. Making your Mac once again your... Read more
Acorn 6.5.3 - Bitmap image editor.
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Fantastical 2.5.13 - Create calendar eve...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
A Better Finder Rename 11.05 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more

Latest Forum Discussions

See All

Eternal Warfare is a new idle clicker fo...
Idle games are a popular genre on mobile, they might not be to everyone's taste but they're made with such regularity and receive a lot of downloads, so it's hard to argue it's not big business. Eternal Warfare is set to join the sea of idle games... | Read more »
New heroes and balance updates set to ar...
It feels like Hearthstone: Battlegrounds only launched yesterday, and already the auto batter addition to Blizzard's megahit card game is set to receive new heroes and balance updates. [Read more] | Read more »
Pre-register for Hello Kitty AR: Kawaii...
Hello Kitty — the cute cat that launched a multi-billion-pound franchise — has been brought to life… sort of. Sanrio has teamed up with the Bublar Group to create a new mobile game that uses AR tech to turn the real world into Hello Kitty’s... | Read more »
Gorgeous and tranquil puzzler Spring Fal...
One-man indie studio SPARSE//GameDev has now launched its tranquil puzzler, Spring Falls. It's described as "a peaceful puzzle game about water, erosion, and watching things grow". [Read more] | Read more »
Black Desert Mobile gets an official rel...
Pearl Abyss has just announced that its highly-anticipated MMO, Black Desert Mobile, will launch globally for iOS and Android on December 11th. [Read more] | Read more »
Another Eden receives new a episode, cha...
Another Eden, WFS' popular RPG, has received another update that brings new story content to the game alongside a few new heroes to discover. [Read more] | Read more »
Overdox guide - Tips and tricks for begi...
Overdox is a clever battle royale that changes things up by adding MOBA mechanics and melee combat to the mix. This new hybrid game can be quite a bit to take in at first, so we’ve put together a list of tips to help you get a leg up on the... | Read more »
Roterra Extreme - Great Escape is a pers...
Roterra Extreme – Great Escape has been described by developers Dig-It Games as a mini-sequel to their acclaimed title Roterra: Flip the Fairytale. It continues that game's tradition of messing with which way is up, tasking you with solving... | Read more »
Hearthstone: Battlegrounds open beta lau...
Remember earlier this year when auto battlers were the latest hotness? We had Auto Chess, DOTA Underlords, Chess Rush, and more all gunning for our attention. They all had their own reasons to play, but, at least from where I'm standing, most... | Read more »
The House of Da Vinci 2 gets a new gamep...
The House of Da Vinci launched all the way back in 2017. Now, developer Blue Brain Games is gearing up to deliver a second dose of The Room-inspired puzzling. Some fresh details have now emerged, alongside the game's first official trailer. [Read... | Read more »

Price Scanner via

B&H offers $100 discounts on 4-Core and 6...
B&H Photo has new 4-Core and 6-Core Mac minis in stock and on sale today for $100 off Apple’s MSRP. Prices start at $699. Overnight shipping is free to many addresses in the US: – 3.6GHz Quad-... Read more
Save $200 today on a 2019 13″ MacBook Air wit...
Apple has a full line of Certified Refurbished 2019 13″ MacBook Airs available starting at only $929 and up to $200 off the cost of new Airs. Each MacBook features a new outer case, comes with a... Read more
New Verizon Pre-Black Friday 2019 deal: Buy o...
Buy one new Apple iPhone 11 model or 2018 iPhone XS model at Verizon and get a second one for free. One new line of service required. Offer is valid from November 21, 2019 to November 27, 2019. Here... Read more
AirPods with Wireless Charging Case on sale t...
Abt Electronics has 2019 AirPods with the Wireless Charging Case on sale today for $163 shipped. Their price is $36 off Apple’s MSRP, and it’s currently the cheapest price for these AirPods from any... Read more
Apple continues to offer 2017 13″ Dual-Core n...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros still available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases... Read more
Save up to $120 on the new 16″ MacBook Pro at...
Apple’s resellers are starting to receive stock of new 16″ MacBook Pros, and the first set of sales & deals are now available: (1) Amazon 16″ MacBook Pros start on sale for $100-$116 off Apple’s... Read more
Apple Watch Series 3 models on sale at Amazon...
Amazon has Apple Watch Series 3 GPS models on sale for $30 off MSRP, starting at only $169. There prices are the lowest we’ve ever seen for these models from any Apple reseller. Choose Amazon as the... Read more
The ‘Mac Potpourri’ Mailbag: Edition #1- Info...
COMMENTARY: 11.20.19- Welcome to the inaugural edition of the “Mac Potpourri” Mailbag where we take a look at correspondence received from readers of this column from all over the world who write in... Read more
13″ 2.4GHz MacBook Pros available for up to $...
Apple has a full line of Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros available starting at $1529 and up to $300 off MSRP. Apple’s one-year warranty is included, shipping is... Read more
New at T-Mobile: Switch to T-Mobile, and get...
T-Mobile is offering a free 64GB iPhone 8 for new customers who switch to T-Mobile and open a new line of service. Eligible trade-in required, and discount applied over a 24 month period. The fine... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**747303BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Store NUmber or Department:** 001413-Cypress-Store **Job Description:** **What Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**743221BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Store NUmber or Department:** 000230-Greenwood-Store **Job Description:** At Best Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**747338BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Store NUmber or Department:** 000254-Superstition Springs-Store **Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**745516BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Store NUmber or Department:** 001101-Manhattan-Store **Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**746655BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Store NUmber or Department:** 002518-Atlantic Center-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.