Forum Navigation
You need to log in to create posts and topics.

Re: [FUTUREBASIC] Parameters by Reference/PPC Toolbox

Posted by: rbrown <rbrown@...>

Brian Victor wrote:
> What is the proper way to pass a variable by reference to a local function
> and modify it within that function in FB?

There are two different ways to pass a variable by reference in FB. In
either case, the important thing to remember is that the "formal
parameter" (that is, the variable that's in the parameter list in the
LOCAL FN statement) will contain a _pointer_ to the referred variable
(rather than containing the referred variable's value). This pointer is
a memory address in the form of a long integer: for that reason, that
formal parameter must always be a long integer (regardless of the data
type of the "actual" parameter that's used when the FN is called).

The two ways to pass by reference are:

1. (when declared): LOCAL FN myFunction(varAddress&)
(when called): FN myFunction(address_of_variable)

Example:
LOCAL FN StringThing(stringVarAddr&)
:
FN StringThing(@stringVar$)

or:

2. (when declared) LOCAL FN myFunction(@varAddress&)
(when called) FN myFunction(variable)

Example:
LOCAL FN StringThing(@stringVarAddr&)
:
FN StringThing(stringVar$)

These two examples produce exactly identical results. Note you can put
the "@" symbol either in front of the actual parameter OR in front of
the formal parameter, but not in front of both. Which syntax you choose
is sometimes just a matter of style. I usually use syntax "1" when I'm
actually planning to alter the variable, and syntax "2" when I'm not
planning to alter it (for example, when I need to pass a pointer to a
record, but I'm not actually going to alter the record). Syntax "1"
also has the advantage that you can pass an arbitrary long integer in
(it doesn't strictly _have_ to be a variable's address). For example, I
could set things up so that "FN StringThing(x, @myString$)" could mean
"return (some) string into myString$", while "FN StringThing(x, _nil)"
could mean, "ignore the 2nd parameter." You couldn't to this little
trick if you used syntax "2".

In the code you provided, your formal parameter declaration looks like
this:

>LOCAL FN myPPCOpen(@thePortRefNum&,@nbpRegisteredFlag&)

I presume this means that you want to pass back the port ref. num and
the NBP-registered flag to the caller, so you're probably calling it
somewhat like this:

FN myPPCOpen(thePortRefNum%, nbpRegisteredFlag%)

Here's my advice: First, as a matter of style, I would change the names
of the formal parameters to something like this:

LOCAL FN myPPCOpen(@thePortRefNumAddr&, @nbpRegFlagAddr&)

This is just to remind myself that thePortRefNumAddr& does _not_ contain
the port ref. num, but rather contains the address of a variable;
likewise, nbpRegFlagAddr& does _not_ contain the NBP-registered flag,
but rather contains the address of a variable.

Now, here's how you're currently retrieving those values:

myPPCOpen=FN PPCOpen(@thePPCOpenPBRec,_false)
thePortRefNum&=thePPCOpenPBRec.portRefNum
nbpRegisteredFlag&=thePPCOpenPBRec.nbpRegistered&

(Or, using my "preferred naming convention"):

myPPCOpen=FN PPCOpen(@thePPCOpenPBRec,_false)
thePortRefNumAddr&=thePPCOpenPBRec.portRefNum
nbpRegFlagAddr&=thePPCOpenPBRec.nbpRegistered&

The mistake here is that you're just overwriting the two "..Addr&"
variables. But what you _want_ to do is to replace the contents of the
two variables that the "..Addr&" variables _point_ to. Here's a
concrete example of what I mean:

Before calling FN myPPCOpen:
* variable "thePortRefNum%" contains 12 (for example);
* address of "thePortRefNum%" variable is: 13762494;

When FN myPPCOpen is called (FN myPPCOpen(thePortRefNum%,
nbpRegisteredFlag%)):
* The number 13762494 is assigned to the variable thePortRefNumAddr&;
* You call FN PPCOpen(@thePPCOpenPBRec,_false). This causes
67 (for example) to be stored into the
"thePPCOpenPBRec.portRefNum" field;
* You then do this:
thePortRefNumAddr& = thePPCOpenPBRec.portRefNum
This causes the value of thePortRefNumAddr& to change from
13762494 to 67; but the contents of the bytes located at address
13762494 do _not_ change.

After FN myPPCOpen returns:
* variable "thePortRefNum%" still contains 12 (but you wanted 67).

What you really wanted to do here was to put the value "67" into
address 13762494. So you want to change this:

thePortRefNumAddr& = thePPCOpenPBRec.portRefNum

...to this:

POKE WORD thePortRefNumAddr&, thePPCOpenPBRec.portRefNum

(You use POKE WORD (vs. "POKE" or "POKE LONG") because portRefNum is
defined as a 2-byte field and you're sticking it into a 2-byte variable
(thePortRefNum%); POKE WORD pokes 2 bytes).

To return the NBP-registered flag you'd do something similar; however
it's only a 1-byte field, so the syntax would be like this:

POKE WORD nbpRegFlagAddr&, PEEK(@thePPCOpenPBRec + _nbpRegistered)

(Note that you have to use PEEK rather than just
"thePPCOpenPBRec.nbpRegistered"; that's because FB does not currently
handle the "dot" syntax nicely for 1-byte fields: this will probably
change in FB^3. Note also that you still use POKE WORD (rather than
just a 1-byte POKE); that's because the variable in the FN call (which I
called nbpRegisteredFlag%) is a 2-byte integer, and you must use POKE
WORD to set its contents correctly.)

>The second is that when the theLocationNameRec.nbpType$="PPC Example"
>line is uncommented, the program crashes with an error type 1.

FB's built-in constant _nbpType won't work here: it's defined as 102,
but you need an offset constant that's defined as 2. You can either use
FB's constant _ppcNBPTypeLocation, like so:

theLocationNameRec.ppcNBPTypeLocation$ = "PPC Example"

...or you can define your own constant (set it to 2) and use that; e.g.:

_myNBPType = 2
:
theLocationNameRec.myNBPType$ = "PPC Example"

(Note: there are _lots_ of built-in FB constants which don't match the
field offset numbers of the like-named fields in Inside Macintosh.
There are also quite a few FB constants which don't even match the
values published in FB's own "Constants" tool. Whenever I need to use a
field-offset constant, I've gotten in the habit of _verifying_ its value
("PRINT _constantName") before I use it in my program. Saves much
heartache later.)

Hope all of this helps.
- Rick