I have a large Macforth application that I would like to make
available on Intel Macs. Alas - I get no answer when I've written to
Megawolf lately and I now expect there will be no intel release.
So, what options do I have to run on Intel Macs? As well I wish to
port to Windows.
It seems to me my only real option is to re-write the whole
application in C++
This involves far too much work to produce a result in a reasonable
time-frame. In order to minimise the work I have in mind to do the
port in stages. The first stage being to just rewrite the user
interface elements in C++ and pass the data to the existing forth
engine. I have already ported the forth engine to Windows using
Proforth, and so I have both a Mac and windows version of the engine.
I have started the UI project, and we are using WXWidgets to create a
cross platform application.
On Mac I would like to be able to call the existing Mac application as
a shared library. And this is where I need help.
Is it possible to create a MacForth turnkey that is a shared library?
Can anyone help me with this?
Thanks
Chris
> Hi there,
>
> I have a large Macforth application that I would like to make
> available on Intel Macs. Alas - I get no answer when I've written to
> Megawolf lately and I now expect there will be no intel release.
>
> So, what options do I have to run on Intel Macs? As well I wish to
> port to Windows.
So far my MacForth things run under Rosetta without problems.
Slower than it would run native on Intel of course, but that's only
apparent in benchmarks, not in my applications.
>
> It seems to me my only real option is to re-write the whole
> application in C++
>
> This involves far too much work to produce a result in a reasonable
> time-frame. In order to minimise the work I have in mind to do the
> port in stages. The first stage being to just rewrite the user
> interface elements in C++ and pass the data to the existing forth
> engine. I have already ported the forth engine to Windows using
> Proforth, and so I have both a Mac and windows version of the engine.
>
> I have started the UI project, and we are using WXWidgets to create a
> cross platform application.
>
> On Mac I would like to be able to call the existing Mac application as
> a shared library. And this is where I need help.
>
> Is it possible to create a MacForth turnkey that is a shared library?
> Can anyone help me with this?
Not much, but:
MacForth shared library, not that I know of (wish list).
PowerMops allows to make shared libraries, never done it though.
PM runs ok under Rosetta last time I checked.
Following this path , means porting MF code to PM of course, which is
do-able (I think).
regards
-Roelf
> In article <1179455096....@n59g2000hsh.googlegroups.com>,
> ch...@createng.com.au wrote:
>
> > On Mac I would like to be able to call the existing Mac application as
> > a shared library. And this is where I need help.
> >
> > Is it possible to create a MacForth turnkey that is a shared library?
> > Can anyone help me with this?
>
> Not much, but:
>
> MacForth shared library, not that I know of (wish list).
Mmm, some rethinking.
If I'm right in assuming you want to access functions from your
turnkeyed MacForth from the outside.
I can think of 3 possible alternatives, all three needing a running
MacForth, not necessarily in the foreground.
1. Set up MacForth as a background server listening to a predefined
port. The outside app connects to this port and makes requests.
The MacForth topword part could be:
begin listen connect receive evaluate again
Your 'exported' functions could be inside a sealed vocabulary.
The outside app connects to your port and sends its request in the form
of the name of the function.
For MacForth you can use sockets, somewhere on:
http://home.uni-one.nl/bmbcon2/toolshed/mac/pmf/
An application like Supercollider works this way. It has two parts:
a server and a gui interface connected via sockets.
2. Use Unix communication stuff like a fifo/pipes/shared memory.
Not really looked in to it.
3. Use an intermediate framework.
Again you have your MF running, doing nothing, just be there.
Outside app and MF include the intermediate framework, which has
one or more functions which call your MF functions as callbacks.
No idea if this will work: sharing the same bundle!
Example, download from Apple:
http://developer.apple.com/samplecode/CFM_MachO_CFM/index.html
put the build Test.bundle found in:
CFM_MachO_CFM/MachO\ Bundle/build/
somewhere like
/User/you/Library/Frameworks
In MF:
\ include the shared framework or bundle
globalbundle /Users/you/Library/Frameworks/Test.bundle
\ select it
test.bundle
\ import needed function
2 machofunc _GetFooWithCallBack
\ our word to execute
: beeps 0 ?do 5 _SysBeep 100 ms loop ;
\ prepare as callback
1 ' beeps MakeMachCallback *beeps
\ run it as callback
5 *beeps _GetFooWithCallBack drop
--------
This gives the idea.
You need to change the setting: Mf and the bundle.
Of course your outside app doesn't know the MF callback pointers,
so MF on boot loads the bundle, stores the appropriate pointers in
some global variables inside the bundle. The public bundle function(s)
will run a modified GetFooWithCallBack which uses the needed callback
pointer to run the requested action. Or whatever.
A lot of variations possible. It wouldn't surprise me if there's a much
simpler solution. Love to learn about it.
regards
-Roelf
> So far my MacForth things run under Rosetta without problems.
> Slower than it would run native on Intel of course, but that's only
> apparent in benchmarks, not in my applications.
>
Hi Roelf,
Thanks for your comments. My turnkey application runs fine on
Rosetta, and so for now, that is a good solution. But as you might
guess customers are wary of buying a Mac application that is PowerPC
only. And somehow I would like to produce an intel version. For
years I have thought I may have to switch from MacForth to a more
common language, but so far I have resisted. And up until now there
has always been a new MacForth in respinse to OS changes such as
PowerPC and Carbon. Alas, now it seems I must abandon forth.
I need to rewrite much of the user interface to support the OSX look -
even to continue as just a PowerPC product there is a big rewrite to
use NIBS. So if I have to rewrite the user interface code anyway, it
might as well be rewritten in C++. And if I'm going to do this, it
might was well be written using WXWidgets, so it becomes cross
platform.
And so this leads to the need to be able to continue to use the forth
engine. So thats my thinking. More later in response to your next
message.
Cheers
Chris
> Mmm, some rethinking.
> If I'm right in assuming you want to access functions from your
> turnkeyed MacForth from the outside.
Yep. I wish to do this as an intermediate step in my porting. Just
so I don't have to rewrite all the computational code as well as user
interface stuff. This makes it a shorter journey to getting an app
that uses the user interface widgets of OSX.
> I can think of 3 possible alternatives, all three needing a running
> MacForth, not necessarily in the foreground.
>
I have experimented with AppleEvents in this manner. In the test
another application was handling user interface interactions and I
wrote an add-on for that application (ArchiCAD) which would simply
send an AppleEvent message to my application running in the
background. This was really fast. As part of this test I also
ported my app to windows using ProForth - well just the engine of the
app, so that used ANSI forth, and so the code was mostly unchanged.
The part that needed complete rewriting was the message despatching
which on Windows used DDE.
But I was hoping for a way that didn't need MacForth app to be
running.
I will try to understand better your suggestion for the intermediate
shared bundle. Forgive me for my slowness, I'll need to tinker with
this to understand it.
back later
Chris
> I will try to understand better your suggestion for the intermediate
> shared bundle. Forgive me for my slowness, I'll need to tinker with
> this to understand it.
In essence it's about two running applications using a framework
or bundle to communicate; i.e. both apps are literally sharing a
loaded framework/bundle. The calling app uses exported functions
from the framework, which will execute code from the serving app,
in a callback context. The framework is an in-between.
I tried this, but failed so far. The MF part and framework works.
Connecting another app to this same framework fails, which makes
sense in this OS of course. Perhaps I'm missing out on something
and it _is_ possible. I give myself 2 more days hitting my head
against the wall. It's probably just a dead end.
For info sake, here's the code:
apologies for the C code, I'm not used to it.
In XCode create a new project named: test
type: Carbon Framework
Code is set to PPC only using the project info tab
Use the supplied code for main.c file,
Build framework is stuffed in /Library/Frameworks/
<c code>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdio.h>
typedef void (*FunctionPtr)(void);
FunctionPtr foo;
short setCallBack( FunctionPtr callBackFunctionPtr )
{
foo = callBackFunctionPtr;
return( 0 );
}
short runCallBack()
{
foo();
return( 0 );
}
</c code>
the Forth code, loaded in a running MF.
<forth code>
\ include the shared framework or bundle
globalbundle /library/frameworks/test.framework
\ select it
test.framework
\ import needed functions
1 machofunc _setCallBack
0 machofunc _runCallBack
\ MF 'exported' word
: beeps 5 0 do 5 _SysBeep 100 ms loop ;
\ prepare as callback for macho-o environment
0 ' beeps MakeMachCallback *beeps
\ store it in functionpointer from framework
*beeps _setCallBack drop
\ run it, here to test, in reality runCallBack should be run by
\ another app connected to the same framework.
: bbbbbeep _runCallBack drop ;
prior.stream
</forth code>
If, big if, it could be made to work as intended, then add an init
function in the framework which launches MF, which after booting
connects to the framework and initializes the pointers with the exported
MF word tokens and then just 'sits' doing nothing, just providing the
context for the exported words.
The other application just loads the framework, imports what's needed,
calls init and do whatever it wants to do next.
groeten
-Roelf
Thanks for your suggestion and the code example. I have the
opportunity to experiment more with it today with a C programmer, and
maybe he can shed some light on it.
I'm wondering if I can make a bundle to which both of these apps
belong - I'll have to read more about that in Apples tech notes.
For the purpose of testing I'm thinking that the calling app could be
a MacForth turnkey, and I may be able to understand this better than
trying to get my head around C or C++
Back when I make some progress.
Cheers
Chris
Hi Chris,
I was just about to report about my days of headbanging.
None of this is fixing the Intel native question I'm afraid.
First, I agree that turnkeying MF in a shared library a.k.a. framework
or bundle, is the simplest and most straight forward way. The vendor is
sorely missed!!!
Now , the problem I ran against has to do with the process memory spaces,
especially moving pointers to executable code around.
BTW, sharing data space between processes is no problem.
What I tried might be of help.
My apologies to your C programmer for the code :-)
See if new app is launched in memory space calling app.
First I compiled the following C code in XCode.app as a framework
and put it in /Library/Frameworks/
<c code>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdio.h>
typedef void (*FunctionPtr)(void);
FunctionPtr foo;
short setCallBack( FunctionPtr callBackFunctionPtr )
{
foo = callBackFunctionPtr;
return( 0 );
}
short runCallBack()
{
foo();
return( 0 );
}
short init()
{
// Launch MF
OSErr err;
OSErr err_fsref;
FSRef fileRef;
err_fsref = FSPathMakeRef((StringPtr)"/Users/roelf/Applications/F O R
T H/MacForth/C A R B O N/testing MF/Carbon MacForth 6.0.5/My Extended
Kernel pre", &fileRef, NULL);
if (err_fsref == noErr) {
err = LSRegisterFSRef(&fileRef, true);
if (err_fsref == noErr)
err = LSOpenFSRef(&fileRef, NULL);
}
return( 0 );
}
</c code>
Next I launched a copy of MacForth, and loaded the framework, imported
the init call and execute it:
<forth code>
\ include the shared framework or bundle
globalbundle /library/frameworks/test3.framework
\ select it
test3.framework
\ import needed function
0 machofunc _init
_init drop
</forth code>
The launched MF is another version btw.
In the new MF, here MF2, I try to connect to the same framework, not a
copy!
<forth code>
1 externfunc _CFBundleGetBundleWithIdentifier ( CFStringRef:bundleID --
CFBundleRef )
\ id comes from the plist in bundle, look for CFBundleIdentifier
0" com.apple.carbonframeworktemplate" >cfstring
_CFBundleGetBundleWithIdentifier .
</forth code>
It returns zero.
Just to be sure, in MF2:
<forth code>
globalbundle /library/frameworks/test3.framework
\ select it
test3.framework
\ access data in bundle
2 externfunc _CFBundleGetDataPointerForName ( CFBundleRef CFStringRef --
datapointer )
0" com.apple.carbonframeworktemplate" >cfstring
_CFBundleGetBundleWithIdentifier
0" foo" >cfstring _CFBundleGetDataPointerForName .s
constant foo
foo ?
0 ok <0>
1234 foo !
ok <0>
</forth code>
In MF1 get 'foo' in the same way as in MF2 and fetch its content:
0, zero, nada, nothing.
So we don't share the same memory space. At least this is not the
way to do it.
Don't give up.
Look in the lower system calls: unix, mach, bsd.
One way of sharing memory between different applications is:
<forth code>
\ ---------- both processes ------------
\ fast and dirty, for quick testing, no semaphores etc.
\ not complete, no detaching implemented, memory leaks ;-)
system.framework
3 machofunc _shmget ( key size flag -- id )
3 machofunc _shmat ( id addr flag -- addr )
\ fixed settings for now
001000 constant IPC_CREAT
IPC_CREAT 0666 or constant SHMFLAG
\ change to usage
256 constant SHMSIZE
ascii share constant SHMKEY
: share ( key size -- addr )
shmflag _shmget dup -1 = abort" no sharing"
0 dup _shmat dup -1 = abort" no attaching" ;
prior.stream
\ ---------- this process ------------
shmkey shmsize share value myshares
c" going down" myshares over count nip char+ move
myshares count type
prior.stream
\ ---------- other process ------------
shmkey shmsize share value hisshares
hisshares count type
prior.stream
</forth code>
Launch two MF's, include the stuff for both apps and execute 'this' in
MF1 and 'other' in MF2.
Alas, storing xt's in the shared memory and have them executed by the
other MF, won't work.
Ways to (re)mapping (virtual) memory areas is what's needed I guess.
Still there is a way:
Launch the turnkeyed MF, MF declares a piece of shared memory. MF goes
in the following loop: begin break myshares count evaluate again
A calling app connects to the shared memory, puts the wanted action
into it, wakes MF and there you go.
The sleeping/breaking waking/kicking goes via unix signalling.
<forth code>
System.framework
lacking _getpid 0 machofunc _getpid ( -- pid )
lacking _kill 2 machofunc _kill ( pid n -- ret )
\ avoid overloading Forth Multitasking words Stop, Sleep
\ 17 = SIGSTOP ( stop/suspend a process )
: HALT ( pid -- ret ) 17 _kill drop ;
\ halt yourself
: BREAK ( -- ) _getpid halt drop ;
\ avoid overloading Forth Multitasking word Wake
\ 19 = SIGCONT ( continue a stopped process )
: KICK ( pid -- ret ) 19 _kill drop ;
</forth code>
The calling app does a 19 kill to wake MF, you need the MF's pid though.
Several ways and means to do that ;-)
If you use a framework as global interface:
The init function in the framework could be extended to connect to
the shared memory. Other exported calls just 'set' the memory and
kick the turnkeyed MF into action.
Thoughts:
Evaluating the buffer might be too much overhead or overkill, simply
passing an index to an execution table could be enough. Which might
also help in the size of the turnkey, no need for the interpreter.
MF's output can use a shared buffer as well, obviously.
Not tried in this context but interesting to look into: fork, clone, exec
So far for now.
-r