C++ Methods, Fortran
Volume Number: | | 7
|
Issue Number: | | 2
|
Column Tag: | | Jörg's Folder
|
Related Info: Quickdraw
C++ Methods In FORTRAN
By Jörg Langowski, Editorial Board
C++ Methods in FORTRAN
This article had to come. Next thing youll see, you may think, is calling Eiffel from Forth. But seriously, implementing cross-language calling very often helps you to understand how one particular language really works and how to get most out of it. Also, the Fortran run time system has some advantages, like well-designed console I/O that may help in debugging, file I/O, an easily accessible output window, and therefore adding it may be very helpful in some applications. So here we go, and write a C++ method in Language Systems Fortran.
C++ methods are really independent subroutines, and the association between a method and its class is done through the header file - on the source code side - and through the modified method name on the object code side. When you define two different classes with methods of the same name, as in
// 1
class aaa {
public:
void doit();
}
class bbbb {
public:
void doit();
}
the methods are usually defined in a place different from where they are actually used. In fact, most often they may be pre-compiled in a library. The linker can distinguish between the two different doit methods, because their symbolic name on the linker level is modified by the class name. The two methods names in this case would be: doit__3aaaFv and doit__4bbbbFv.
C++ Function Name Encoding
To remind you of the C++ linker naming conventions: the first part of the linker name is the method name (doit), followed by two underscores. Then follows the class name, preceded by the number of characters in that name. Appended to that are some symbols describing the parameter list. In our case, for methods taking no parameters, it is simply Fv. F stands for a function, v for a void parameter list. I am not describing all the modifiers that are used in creating a linker name out of a C++ method name; the full encoding scheme is described in the AT&T C++ release notes, chapter 6, page 22.
Usually, you never see the encoded names; only when e.g. a method is defined in a class header file, then referenced by some code using objects from that class, but never actually implemented, the linker might complain about a missing function. There, you will probably have seen those funny names already, and used the MPW unmangle tool to make sense out of them.
Here, we want to go the other direction. When we implement a C++ method in some non-object language, we must write a routine that has the encoded methods name. The easiest way to find out about the linker name of a C++ method is not to construct it yourself (you never get it right in the first place), but just to define the method in the class header, use it in the code, and never actually implement it. The linker will tell you in its error message what function it is looking for.
That way I found out that the name for the method pascal void FtnCall() in the class TMacTutorDocument is FTNCALL__17TMACTUTORDOCUMENT. The characters Fv are not appended here, and the name is written in all capitals, because of the pascal keyword. This also means that all external Pascal-type methods of the same name in the same class will have the same linker name, regardless of the argument list.
Language Systems Fortran, of course, uses Pascal calling conventions, so a method written in Fortran will have to be defined in C++ as pascal. We want to pass some parameters, so lets define an argument list:
{2}
pascal void FtnCall(short *menuitem, long k, float *r);
The method will later be called from a menu handler, just for the fun of it, and we pass the number of the menu item selected, and some numbers. Note one restriction here: LS Fortran symbolic names are limited to 31 characters, so dont use too complicated class and method names if you dont want to run out of space.
The Sample Program
We use the sample program that I showed you in V6#1: Apples C++ mini-application skeleton. You should now go back to your library and get that MacTutor issue, or reorder it, because we obviously cannot reprint its full source here. Listing 2 only shows those parts that have been changed.
In the constructor and destructor methods of the application class we have added calls to InitFortran() and ExitFortran() for initializing the Fortran run time system at the start of the program and leaving it properly at the end. During the program, then, the run time system is at our disposal. The Fortran method is defined in the class TMacTutorDocument. It is used in the TMacTutorApp class, in its DoMenuCommand method:
// 3
x = 4.567;
fMacTutorCurDoc->FtnCall(&menuItem,3456,&x);
This way we have some calls by reference (standard Fortran calling convention), and one call by value.
The Fortran routine (listing 1) receives the three parameters in the same order. Furthermore, on every method call a handle to the methods current object (this) is pushed on the stack after all the methods parameters. The Fortran code has to take that extra parameter into account. Thus, the first line of the Fortran routine will be
C 4
subroutine FTNCALL__17TMACTUTORDOCUMENT
(menuitem,%val(k),r,%val(this)) .
menuitem and r are passed by reference, k is received by value (Language Systems Fortran has an option for receiving parameters by value in the subroutine definition). this is also received by value. Through this, we can access the instance variables of the methods current object. We also define the list of instance variables, which becomes rather complicated as we have to dereference handles in the (Languages Systems) Fortran way. The structure definitions at the beginning of the Fortran routine show you how to do that.
this points to the beginning of the objects instance variables, starting with those of the topmost ancestor class and descending through the class hierarchy. In our case, we have a window pointer fDocWindow and the pointer to the virtual methods table vptr from the TDocument class, then the instance variables of TMacTutorDocument, fItemSelected and fDisplayString.
The main body of the routine displays the passed parameters and some information about the instance variables in the Fortran output window.
Thats all! Youre now able to add Fortran code to C++ programs as you like, and have it behave like real C++ methods.
Listing 1: C++ method written in Fortran
CC++ test method written in Language Systems Fortran
CFor editing, some of the lines had to be split. I have not
Ccreated proper Fortran continuation lines, because a. C Im lazy and
b. I think the text can be read better that
Cway -- jl --
subroutine FTNCALL__17TMACTUTORDOCUMENT
(menuitem,%val(k),r,%val(this))
Cpascal void FtnCall__17TMacTutorDocument
(short *menuitem, long k, float *r,
struct TMacTutorDocument **);
include Quickdraw.f
structure /MTDocVars/
record /WindowPtr/ fDocWindow
integer*4 vptr
integer*2 fItemSelected
record /StringPtr/ fDisplayString
end structure
structure /MTDocPtr/
pointer /MTDocVars/ p
end structure
structure /MTDocHdl/
pointer /MTDocPtr/ h
end structure
record /MTDocHdl/ this
integer*2 menuitem
integer top,left,bottom,right
call MoveOutWindow (20,260,490,340)
write (*,*) Arguments to FtnCall:,menuitem,k,r
write (*,*) fItemSelected = ,this.h^.p^.fItemSelected
write (*,*) fDisplayString =
,this.h^.p^.fDisplayString.sptr^
top = this.h^.p^.fdocwindow.wp^.portrect.top
bottom = this.h^.p^.fdocwindow.wp^.portrect.bottom
left = this.h^.p^.fdocwindow.wp^.portrect.left
right = this.h^.p^.fdocwindow.wp^.portrect.right
write (*,*) doc window = ,top,left,bottom,right
return
end
Listing 2: Changes to the .cp and .h files from the V6#1 example
File MacTutorApp.make:
OBJECTS = TApplication.cp.o TDocument.cp.o
MacTutorApp.cp.o MacTutorDoc.cp.o
MacTutorGrow.cp.o FtnCall.f.o
HEADERS = MacTutorApp.h MacTutorDoc.h
MacTutorGrow.h
TApplication.cp.o TApplication.cp TApplication.h
CPlus TApplication.cp
TDocument.cp.o TDocument.cp TDocument.h
CPlus TDocument.cp
MacTutorApp.cp.o MacTutorApp.make
{HEADERS} MacTutorApp.cp
CPlus MacTutorApp.cp
MacTutorDoc.cp.o MacTutorApp.make
{HEADERS} MacTutorDoc.cp
CPlus MacTutorDoc.cp
MacTutorGrow.cp.o MacTutorApp.make
{HEADERS} MacTutorGrow.cp
CPlus MacTutorGrow.cp
FtnCall.f.o MacTutorApp.make FtnCall.f
Fortran -mc68020 -mc68881 -opt=3 -bkg=0 FtnCall.f
MacTutorApp MacTutorApp.make {OBJECTS}
Link -w -t APPL -c JLMT
{CLibraries}CRuntime.o
{OBJECTS}
{Libraries}Interface.o
{CLibraries}StdCLib.o
{CLibraries}CSANELib.o
{CLibraries}Math.o
{CLibraries}CInterface.o
{CLibraries}CPlusLib.o
#{CLibraries}Complex.o
{FLibraries}FORTRANLib.o
-o MacTutorApp
MacTutorApp MacTutorApp.make MacTutorApp.r
Rez MacTutorApp.r -append -o MacTutorApp
File MacTutorApp.h:
class TMacTutorApp : public TApplication {
public:
TMacTutorApp(void); // Our constructor
~TMacTutorApp(void);
// need a destructor to call EXITFORTRAN
private:
// routines from TApplication we are overriding
long HeapNeeded(void);
unsigned long SleepVal(void);
void AdjustMenus(void);
void DoMenuCommand
(short menuID, short menuItem);
// routines for our own devious purposes
void DoNew(void);
void Terminate(void);
};
File MacTutorDoc.h:
class TMacTutorDocument : public TDocument {
protected:
short fItemSelected;
// string corresponding to menu item selected
StringPtr fDisplayString;
void DrawWindow(void);
public:
TMacTutorDocument(short resID, StringPtr s);
~TMacTutorDocument(void);
// routine to access private variables
void SetDisplayString (StringPtr s) {fDisplayString = s;}
short GetItemSelected(void) {return fItemSelected;}
void SetItemSelected(short item) {fItemSelected = item;}
// methods from TDocument we override
void DoUpdate(void);
// Fortran calling;
pascal void FtnCall(short *menuitem, long k, float *r);
};
File MacTutorApp.cp:
pascal void initFortran();
pascal void exitFortran();
// Methods for our application class
TMacTutorApp::TMacTutorApp(void)
{
Handle menuBar;
initFortran(); // initialize Fortran runtime system
// read menus into menu bar
menuBar = GetNewMBar(rMenuBar);
// install menus
SetMenuBar(menuBar);
DisposHandle(menuBar);
// add DA names to Apple menu
AddResMenu(GetMHandle(mApple), DRVR);
DrawMenuBar();
// create empty mouse region
fMouseRgn = NewRgn();
// create a single empty document
DoNew();
}
TMacTutorApp::~TMacTutorApp(void)
{
exitFortran(); // exit Fortran runtime system
}
void TMacTutorApp::DoMenuCommand
(short menuID, short menuItem)
{
short itemHit;
Str255 daName;
short daRefNum;
float x;// for testing the Fortran call
WindowPtrwindow;
TMacTutorDocument* fMacTutorCurDoc =
(TMacTutorDocument*) fCurDoc;
window = FrontWindow();
switch ( menuID ) {
case mApple:
switch ( menuItem ) {
case iAbout: // About box
itemHit = Alert(rAboutAlert, nil);
break;
default: // DAs etc.
GetItem(GetMHandle(mApple), menuItem, daName);
daRefNum = OpenDeskAcc(daName);
break;
}
break;
case mFile:
switch ( menuItem ) {
case iNew:
DoNew();
break;
case iClose:
if (fMacTutorCurDoc != nil) {
fDocList->RemoveDoc(fMacTutorCurDoc);
delete fMacTutorCurDoc;
}
else CloseDeskAcc(((WindowPeek)
fWhichWindow)->windowKind);
break;
case iQuit:
Terminate();
break;
}
break;
case mEdit: // SystemEdit for DA editing & MultiFinder
if ( !SystemEdit(menuItem-1) ) {
switch ( menuItem ) {
case iCut:
break;
case iCopy:
break;
case iPaste:
break;
case iClear:
break;
}
}
break;
case myMenu:
if (fMacTutorCurDoc != nil) {
switch ( menuItem ) {
case item1:
fMacTutorCurDoc->
SetDisplayString(\pC++);
break;
case item2:
fMacTutorCurDoc->
SetDisplayString(\pSample);
break;
case item3:
fMacTutorCurDoc->
SetDisplayString(\pApplication);
break;
case item5:
fMacTutorCurDoc->SetDisplayString(\pHave Fun);
x = 4.567;
fMacTutorCurDoc->FtnCall(&menuItem,3456,&x);
break;
}
fMacTutorCurDoc->SetItemSelected(menuItem);
InvalRect(&(window->portRect));
fMacTutorCurDoc->DoUpdate();
}
break;
}
HiliteMenu(0);
} // DoMenuCommand