Pointer Operations in Lingo
October 14, 1998
by Brennan Young
One of the things that makes a programming language really powerful is pointers. Pointers are a very efficient way of processing data. Instead of shuffling it about from place to place, you just manipulate a reference to its actual location in RAM. This is especially useful for large volumes of data because they may be too unwieldy to move without significant performance problems. If you are working with a bunch of heavy data (such as all the plays of Shakespeare, each stored in seperate strings), you are going to benefit from a little understanding of pointers. Your movies will probably run better too.
If you use Director, you are already using pointers. All the high level datatypes are stored as pointers. The most obvious one is the cast member. Haven't you ever thought it remarkable that you can drag a multi-megabyte bitmap around in the cast and drop it into a new cast member location with the same ease that you would with, say, a 64 byte quickdraw shape? How does the score update itself when you move a cast member around which is in use by a sprite. What magic is this?
A cast member is a pretty fancy object, but most of the time you are barely touching it. A castlib does not really contain all those media objects, though the Director engineers have done a good job of convincing you that it does. A castlib is just a list of pointers. It takes care of showing the right icon, according to the media type of the data pointed to, and in most cases, displays a little thumbnail for each member which reminds us visually what it is pointed to. When you drag a member onto the score, a sprite object stores a pointer to that member in its "member" property.
OK, maybe this is rather useless information, even obvious perhaps, but I think it's important to have a more than skin-deep understanding of the tools you are using. Director is not just a "Black Box", we do need to poke around inside from time to time, if only to get our projects running smoothly.
Learning about pointers has certainly affected my Lingo. I recently wrote an article about sharing data between objects without using globals. To me that was a mini-revolution. The secret was pointers, but their use does not stop there.
Try this simple example in a moviescript
on test set referencedData to [] fill referencedData put referencedData end on fill thelist repeat with n = 1 to 100 add thelist, n end repeat end
If you type...
test
...in the message window, the result you get will be a list of integers from 1 to 100. Nothing unusual about that, but have a close look at the script. The first handler creates an empty list, then sends the list as a parameter to the second handler. The second handler takes the list parameter and fills it up.
There is no return statement.
When the first handler outputs the list (which it created) to the message window, the list is full up with integers. This might seem a little strange, but to a C programmer it's the normal state of affairs. This really blurs the boundaries between a procedure and a function because a return statement is not necessary, but you get a result back anyway. This is yet another reason why no-one can agree whether a procedure is a special kind of function or vice versa.
Lists are the lowest level Lingo datatypes to be passed as pointers. Fortunately it is in the nature of lists that they are container objects, so if there are any other types of data that you want to point to (instead of move around and duplicate in bulk), you need only pop them in a list. Other datatypes you could use are property lists and instances created from parent scripts or behaviors, each of these are 'passed by reference' (the traditional computer jargon for "stored as a pointer").
Let's take another example. A score script this time.
property myPath, mypos, stagerect on beginsprite me set mypos to 1 set myPath to [the loc of sprite the ¬ spritenum of me] set sw to the width of the rect of the stage set sh to the height of the rect of the stage set stagerect to rect(0,0,sw,sh) end on exitFrame me set mpos to point(the mouseH, the mousev) set pointCount to count(myPath) set mypos to mypos + 1 if mypos > pointCount then set mypos to 1 set the loc of sprite the spritenum of me to ¬ getat(myPath, mypos) if inside(mpos, stagerect) and the mousedown then if getat(myPath, pointCount) = mpos then return add myPath, mpos end if end on getPath me return mypath end
Drag a bitmap sprite onto the stage, sprite channel 1, and attach this script. Make sure your movie is set to looped playback with an appropriate framescript or in the control panel.
It's a fairly simple behavior which will record drag movements of the mouse on the stage, and the sprite will follow the path continuously. I don't want to go into detail with this script, but the point is that the sprite stores a list of positions - a path - as a property which can be retrieved with the getPath function.
Now here's a handler for a moviescript.
on offset pathlist, pOff set pos to 1 repeat with n in pathlist setat pathlist, pos, n+pOff set pos to pos + 1 end repeat end
Run your movie, drag around on the stage for a while and then type...
offset getPath(Sprite 1), point(0,50)
...in the message window. You should see the path immediately offset by 50 pixels.
Having urged everybody to go in for serious encapsulation in my previous article, some of you may think I am being "messy" with this example; going over the boundaries of an object in this way is maybe not very polite. Nonetheless, this example shows that an object can export a pointer to one of its fundamental properties. What an external object does with that pointer may affect the object significantly.
If we really want an object to guard its data, it should only export pointers to duplicates of its data.
on getPath me return duplicate(mypath) end
This prevents any messing around from outside, but don't you think the first example suggests some interesting possibilities? Sometimes, there are software designs which require an object to "bare all" to the outside world in this way. Those are objects which invite the outside world to modify them. A selection in an image-editing program (for example) is just an exported pointer to parts of a bitmap which is passed to whatever filters the user chooses. This is also what makes Photoshop's plug-in architecture possible.
There are no really rigid rules in OO, and having a clear understanding about object boundaries includes knowing the implications when an object has a function which returns a pointer to one of its properties. There's a strong case for clear boundaries, but there's an equally strong case for an object to open itself up from time to time.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.