[comp.sys.mac.hypercard] Forwarding func & proc calls

tonyrich@titanic.cs.wisc.edu (Anthony Rich) (01/19/90)

In article <MZgwHNi00UhWE6fXoa@andrew.cmu.edu> ba0k+@andrew.cmu.edu
(Brian Patrick Arnold) writes:

> Rhetorical Complaint #336237: could somebody show me how to "send" a
> function call and get its result?

The examples below show how to forward function calls and procedure calls.

I wrote these to solve the following problem.  I had written a HyperCard
prototype of a user interface, using several stacks.  The stacks weren't
big, but their scripts were!  I had to put the common scripts that were used
by all the stacks into the Home stack, which eventually exceeded its 32K
limit on script space.

To get around that, I wrote the "message forwarding" routines below which
allowed me put the big scripts back in the application stacks where they
belonged.  The trick is to have two forwarding routines in the Home stack,
one for procedure calls and one for function calls.  To make it work,
instead of calling a procedure (say) the normal way:

       Do_Something a, b, c

you write it like this:

       Call_Proc Do_Something,a,b,c

which sends the Do_Something message name and its arguments to the
Call_Proc forwarding routine in the Home stack.  That routine repackages
the Do_Something message so it looks right to HyperCard, goes to the
stack where the script for DoSomething is, and sends the message to that
stack directly.

Bouncing procedure and function calls off the Home stack slows things
down, of course, but then when you're out of script space, who ya gonna
call?  (Stackbusters?! :^)

In addition to the forwarding routines, I've included a simple procedure,
a function, and a test-button script so you can try them out.

The forwarding routines always forward messages to a stack named "Examples",
but of course you can change that to send them wherever you want.

Disclaimer:  These worked before I posted them, but I know from experience
             that bugs invade anything made public...

     Tony (tonyrich@titanic.cs.wisc.edu)

-----------------------------------------------------------------------------
-- Place these "Call_Proc" and "Call_Func" handlers in the Home stack script.
--
-- Call_Proc routes messages (that it receives as arguments) to a stack 
-- named "Example" below.  The arguments to the Call_Proc routine form
-- the actual message to be executed.  Call_Proc strips off the "Call_Proc"
-- header and fixes up the other arguments into a command (a name and a
-- parameter list of the form required by the "send" command.  It's a
-- a messy thing to have to do, but necessary since "send" evaluates
-- the parameter list before sending it!).  Finally, we go to the Example
-- stack and send it the message that was intended for it all along.
--
-- Call_Func does exactly the same thing, only for function calls.
--
-- The "send" command requires that we are AT the "Example" stack
-- when we do the send, so to keep the screen from changing we first lock
-- it and push the card we're currently on.  Any handler that receives a
-- forwarded message must first pop back to the original card
-- (so it isn't at the wrong card when it starts) and unlock the
-- screen so that nothing looks different from when the message was sent.
-----------------------------------------------------------------------------
on Call_Proc
  
  put second word of the params into command_name
  delete first char of command_name
  put space into last char of command_name
  
  put empty into param_list
  repeat with param_nr = 2 to the paramCount
    if param_nr > 2 then put "," after param_list
    put the param of param_nr after param_list
  end repeat
  
  put command_name & "param_list" into command
  
  lock screen
  push card
  go to stack "Example" -- which must contain the called proc's script
  send command
  
end Call_Proc


-------------------------------------------------------------------------
-- Call_Func allows functions to be called in the same way that Call_Proc
-- allows procedures to be called.
--
-- Unfortunately, each argument's value must be placed in a container,
-- then the NAME of the container must be placed in a comma-separated
-- list within the command to be sent.
--
-- I didn't take the time to use a fancy method to generate different
-- container names and insert commas; instead, I just check the number of
-- arguments and "hand-assign" them single-letter names (a,b,c,d,and e)
-- in the series of IF statement below.  Since it only goes up to
-- "e", this routine assumes that no function will have more than 5
-- arguments.  Of course, more IF's can be added if that's not enough.
-------------------------------------------------------------------------

function Call_Func
  
  if the paramCount > 6 then  -- (1 function name + 5 arguments)
    answer "Too many args to " & the param of 1
    exit to HyperCard
  end if
  
  -- 'the param of 1' holds the name of the function actually called.
  
  put empty into command_args
  
  if the paramCount >= 2
  then
    put the param of 2 into a
    put "a" into command_args
  end if
  
  if the paramCount >= 3
  then
    put the param of 3 into b
    put ",b" after command_args
  end if
  
  if the paramCount >= 4
  then
    put the param of 4 into c
    put ",c" after command_args
  end if
  
  if the paramCount >= 5
  then
    put the param of 5 into d
    put ",d" after command_args
  end if
  
  if the paramCount >= 6
  then
    put the param of 6 into e
    put ",e" after command_args
  end if
  
  put the param of 1 & "(" & command_args & ")" into command
  
  lock screen
  push card
  go to stack "Example"  -- which must contain the function's script
  
  return Value(command)
  
end Call_Func


--------------------------------------------------------------------------
-- Place these two test routines in the script of a stack named "Example".
-- The Call_Proc or Call_Func handler in the Home stack forwards the
-- message from there to stack "Example".  To make this happen unseen,
-- each routine must do "pop card" and "unlock screen" to reverse the
-- effects of the "push card" and "lock screen" done by the forwarding
-- handler in the Home stack.  Then it can do anything.
--------------------------------------------------------------------------

on Test_It x
  pop card        -- to undo Call_Proc's "push card"
  unlock screen   -- to undo Call_Proc's "lock screen"
  
  answer "Test_It executing with a value of " & x & "."
  
end Test_It

----------------------------------------------------------------
-- Addup is a simple function that returns the sum of 3 numbers.
----------------------------------------------------------------

function Addup n1, n2, n3
  pop card      -- to undo Call_Func's "push card"
  unlock screen -- to undo Call_Func's "unlock screen"
  
  answer "Addup executing with values " & n1 & ", " & n2 & ", " & n3
  return n1 + n2 + n3
  
end Addup


---------------------------------------------------------------------
-- Place this testing script in a button in any stack, then press it!
--
-- As shown below, calls to the forwarding routines should look like:
--
--     Call_Proc <proc_name>, <arg1>, <arg2>, ... <argN>
--     Call_Func <func_name>, <arg1>, <arg2>, ... <argN>  (5 maximum)
---------------------------------------------------------------------

on mouseUp
  
  Call_Proc Test_It,5                    -- test a procedure call

  put Call_Func(Addup,3,7,5) into total  -- test a function call
    
  answer "The answer is " & total & "."
  
end mouseUp
--
------------------------------------------------------------------------
Email:       tonyrich@titanic.cs.wisc.edu  Phone:  608-271-8450
Disclaimer:  The opinions above are mine.  Others may agree or disagree.
------------------------------------------------------------------------