C++ classes can call Mumps functions. See the following example:
^MaxHct(ptid) if $data(^pat(ptid)) quit "0"
set i=-1
set max=0
for do
. set i=$next(^ptHct(ptid))
. if i<0 break
. if i>max set max=i
quit max
^MinHct(ptid) if $data(^pat(ptid)) quit "0"
set i=$next(^ptHct(ptid,-1))
if i<0 quit 0
set min=i
for do
. set i=$next(^ptHct(ptid))
. if i<0 break
. if i |
#include <mumpsc/libmpscpp.h>
char * MaxHct(struct SV *, char*, const char*);
char * MinHct(struct SV *, char*, const char*);
char * AvgHct(struct SV *, char*, const char*);
char * CountHct(struct SV *, char*, const char*);
char * HctValue(struct SV *, char*, const char*, const char*);
char * findPatient(struct SV *, char*, const char*);
class PatientHct {
public:
PatientHct();
double Max();
double Min();
double Avg();
bool Exists(char*);
long Count();
double Value();
private:
char ptid[32];
char current[32];
char hctVal[32];
struct SV * svPtr;
};
PatientHct::PatientHct() {
strcpy(ptid,"");
strcpy(current,"-1");
svPtr=AllocSV();
}
double PatientHct::Max()
{ return atof(MaxHct(svPtr,"",ptid)); }
double PatientHct::Min()
{ return atof(MinHct(svPtr,"",ptid)); }
double PatientHct::Avg()
{ return atof(AvgHct(svPtr,"",ptid)); }
long PatientHct::Count()
{ return atol(CountHct(svPtr,"",ptid)); }
double PatientHct::Value()
{ strcpy(current,HctValue(svPtr,"",ptid,current));
return atof(current); }
bool PatientHct::Exists(char *ptnbr){
if (findPatient(svPtr,"",ptnbr)=="1") {
strcpy( ptid, ptnbr);
return true;
}
strcpy (ptid, "");
return false;
}
int main() {
double val;
char ptnbr[32];
PatientHct pHct;
while (1) {
cout << "enter ptid ";
cin.getline(ptnbr,32);
if (!pHct.Exists(ptnbr)) {
cout << ptnbr << " not found. Exiting." << endl;
break;
}
if ( pHct.Count() > 0 )
while ( (val = pHct.Value()) > 0) cout << val << endl;
else cout << "Sorry, not Hct's for " << ptnbr << endl;
}
return 0;
}
|
To compile and link the above, use commands of the form:
mumps2c hct.mps
g++ -c hct.cpp
g++ patient.cpp hct.o -lmpscpp -lmumps -lmpsglobal_native -lpcre
Creating and Linking Multiple Mumps Object Files Together
Mumps functions that are separate C++ functions (i.e., their names begin with two circumflexes), may be separately compiled and linked together from object code modules. For example, if you have a main Mumps function in a file named main.mps that calls upon a function contained in the file test.mps, you may first translate the functions to C++ and then compile them to object code, then link them as follows:
mumps2c test.mps
g++ -c test.cpp
mumpsc main.mps test.o
This assumes that the main function is in main.mps Likewise, test.cpp contains the subroutine also created by the mumps compiler. Only one zmain command is permitted in any final binary executable.
Be careful not to use the underscore character in function names. In Mumps this means concatenate. Be careful not to use the names of functions already known to the C++ environment, including those names found in "bifs.c" in the distribution.
The compiler supports both call by value and call by reference forms of parameter passing for compiled code. This option is not presently supported in the interpreter.
Arguments are passed by reference if their names are preceded in the calling routine by a decimal point. Otherwise, they are called by value.
Call by reference variables that are altered in a called routine are altered in the calling routine whereas call by value variables are not affected by changes made to them in subroutines. The decimal point preceds variable names to be called by reference only in the calling routine.
Example:
^tst(a,b,c)
set a=100
set b=200
set c=300
quit
zmain
set a=0
set b=0
set c=0
do ^tst(.a,b,.c)
write a," ",b," ",c,!
halt
The above will write "100 0 300".
|
If a calling parameter other than the first or last is omitted, a zero will be inserted in its place. For example:
^abc(a,b,c,d) write a,b,c,d,! quit
zmain
do ^abc(1,,,,4)
will be understood as though you had written:
do ^a(1,0,0,4)
|
You may not omit the first or final parameter.
In Mumps, there is basically one internal program data type: string. There are no declarations. String variables are created when they appear on the left hand side of an assignment operator or in "read" statements. Variables may be destroy with the "kill" command. Arithmetic may be performed on variables if the contents are numeric. The result is converted to string. The basic external data type is the global array. It is an undeclared external hierarchical data type that can contain strings. In a program, global arrays look like array references and they may be used anywhere an ordinary, internal variable may be used. Global arrays may have either numeric or string subscripts.
In the MDH, there are two main classes to represent the Mumps data types: global and mstring. They mimic the behavior of the corresponding Mumps data types and many of the builtin Mumps functions to manipulate instances of these data types are available.
The MDH also contains a large set of functions that are not part of Mumps. Many of these functions are very useful for certain applications such as Information Storage and Retrieval. It is possible, however, for a Mumps program to invoke these functions (and, correspondingly, for a C++ program to invoke Mumps functions).
Before a Mumps program can access a C++ function operating on a global array or a string variable, certain declarations must be established that provide the interface between the environments. Basically, you will either write a multi-line, self-contained C++ function and invoke it from Mumps or you will embed C++ code in your Mumps program.
In the examples above, C++ code was always shown as multi-line, self-contained modules and data was exchanged by formal parameters or return values. None of the C++ programs directly operated on global arrays or Mumps string variables.
As noted above, if a line compiled by the Mumps Compiler begins with a "+" sign, the line is interpreted to be a line of C++ code. If a line begins with a "#" sign, the line is a comment. In Mumps programs with embedded C++ code, there will be several lines of C++ mixed with the Mumps code.
If a Mumps program wants to use a C++ builtin function to perform an operation on a global array, the Mumps program must, on a C++ line, create a C++ object that corresponds to the global in use in the Mumps program. Similarly, if you want to be able to manipulate a variable in both the C++ and Mumps environment, you need to create a C++ object that corresponds to the Mumps variable name. For example, if you have a global array named "^parts()" and you want to invoke the builtin C++ function named "Count()" to determine how many elements there are in "^parts()", you would do the following:
zmain
+ global parts("parts");
+ mstring count("count");
+ count=parts().Count();
write "There are ",count," parts",!
halt
|
In this example, a C++ object named "parts" is created to interface to the global array "^parts()" and a C++ object named "count" is created to interface to the Mumps variable named "count". The function "Count()" is a member of the class "global" and, when applied to an instance of "global", counts the number of elements.
In both cases of creating the C++ interface objects, the quoted string gives the name of the object in the Mumps environment. In nearly all cases, this should be the same name as the object.
Changes made in either the C++ or Mumps environment are also changes to the corresponding object in the other environment. for example:
zmain
+ mstring x("x");
+ x="Hello World";
write x,!
set x="Goodbye World"
+ cout << x << endl;
halt
writes:
Hello World
Goodbye World
zmain
kill ^tmp()
+ global tmp("tmp");
+ mstring x("x");
+ for (x=1; x<100; x++) tmp(x)=tmp;
for i=1:1:99 write ^tmp(i),!
halt
writes 1 through 99 on successive lines.
|
Examples of mstring and global Mumps/C++ interfaces
Mumps variables are stored in a run time symbol table while most C++ variables are either bound at compile time or dynamically allocated during execution and bound to pointers. It is, however, possible to link Mumps and C++ variables. The MDH and Mumps Compiler provide for mstring variables in the C++ program that can directly be accessed by both Mumps programs and C++ programs.
Global Array Interface Example
First, consider the following hybrid C++/Mumps program in which a C++ function calls a Mumps function. In this example, the Mumps and C++ program share access to a common global array:
^Avg(test)
set total=0,count=0
set id=""
for do
. set id=$order(^Labs(id))
. if id="" break
. set name=""
. for do
.. set name=$order(^Labs(id,name))
.. if name="" break
.. set date=""
.. for do
... set date=$order(^Labs(id,name,date))
... if date="" break
... set total=total+^Labs(id,name,date)
... set count=count+1
set avg=-1
if count'=0 set avg=total/count
quit avg
zexit
+ int main() {
+ mstring name;
+ mstring id;
+ mstring date;
+ mstring test;
+ mstring result;
+ mstring avg;
+ global Labs("Labs");
+
+ while (1) {
+ cin >> id; // read id
+ if (id<0) break; // end of data
+ cin >> name >> date >> test >> result; // read data
+ Labs(id,name,test,date)=result; // store data
+ }
+ cin >> test; // read a test name
+ avg = Avg(svPtr, "", test); // call Mumps program
+ cout << "average for test " << test << " is " << avg << endl;
+ return 0;
+ }
|
The program consists of a C++ main() function and a Mumps subroutine named Avg(). Important points:
In this example, mstring is an MDH data type that mimics the typeless variables in Mumps. Instances of class "global" are interfaces to global arrays which can be accessed by either Mumps or C++ routines.
In the example above, a C++ main program reads in a set of patient ids, names, dates, test names and results and stores the result in the "Labs" global array indexed by id, name, test and date. When all the data have been read and stored, the program then reads in a test name and invokes the Mumps function "Avg()" which calculates the average value for all tests of that name and returns the result.
In the example, both the Mumps program and the C++ program have access to the same global array (^Labs()). While global arrays are never declared in Mumps, they must be instantiated as objects of the global class in C++. That is done in the C++ statement:
+ global Labs("Labs");
In this statement an object named "Labs" is created and it references the global array named "Labs". Note: the name in the parentheses is not required to match the name of the object. An object of a different name could be used but the actual global array referenced is determined by the name in the parentheses. This makes it possible to write a function where the name of the actual global is passed as a character string parameter. Note that the global array name does not use the circumflex (^) character in the C++ program but the circumflex is required in the Mumps program.
Additionally, the above could have been expressed entirely in C++ using the MDH "look and feel" functions as follows:
#include <mumpsc/libmpscpp.h>
global Labs("Labs");
double Avg(mstring test) {
double total=0.0,avg=-1.0;
int count=0;
mstring id,name,date;
id="";
while (1) {
id=$order(Labs(id),1);
if (id == "") break;
name="";
while (1) {
name=$order(Labs(id,name),1);
if (name == "") break;
date="";
while (1) {
date=$order(Labs(id,name,date),1);
if (date == "") break;
total=total+Labs(id,name,date);
count=count+1;
}
}
}
if (count != 0) avg=total/count;
return avg;
}
int main() {
mstring name;
mstring id;
mstring date;
mstring test;
mstring result;
mstring avg;
while (1) {
cin >> id;
if (cin.eof()) break;
cin >> name >> date >> date >> test >> result;
Labs(id,name,test,date)=result;
}
cin >> test;
avg = Avg(test);
cout << "average for test " << test << " is " << avg << endl;
return 0;
}
|
mstring Interface Example
As noted above, it is possible to share variables between Mumps and C++ programs. This is possible by declaring mstring variables in the C++ program in a manner that causes them to be linked to a similarly named variable in the Mumps run time symbol table. At present, the mstring variables must be declared first.
Assume the data base from the above example and consider the following:
^FindNext(test,max)
for do
. set id=$order(^Labs(id))
. if id="" break
. set name=""
. for do
.. set name=$order(^Labs(id,name))
.. if name="" break
.. set date=""
.. for do
... set date=$order(^Labs(id,name,date))
... if date="" break;
... if ^Labs(id,name,date)>max goto exit
quit 0
export name,id;
exit quit 1
zexit
.
+ int main() {
+ mstring name("name");
+ mstring id("id");
+ mstring date;
+ mstring test;
+ mstring result;
+ mstring avg;
+ mstring max;
+ global Labs("Labs");
+
+ cin >> test >> max;
+ id="";
+ while (1) {
+ result=FindNext(svPtr, "", test, max);
+ if (result == 1) cout << id << " " << name << endl;
+ }
+ return 0;
+ }
|
Note that "mstring" variables "name" and "id" in the C++ program are declared with string constants. These are the Mumps run time symbol table names for these variables.
When the Mumps function is run, these variables are availablavailable to the Mumps program. The "export" command is similar to the "export" shell command in Linux: it causes the named variables to be exported from the current level of the symbol table to the outer layers. Otherwise, all local variables would be lost on exit. On entry to the Mumps routine, any variable ("id" in this case) not found in the current symbol table layer is sought in outer layers.
In the above example, the calling C++ program defined the mstring variables and the called Mumps program used/modified the variables. In those cases where you want to invoke a function from the MDH, inline execution of the MDH functions is preferred.
The following is a simple example of this. The example is a Mumps program that incorporates embedded inline C++ code (lines beginning with "+"). The global array objects "a" and "b" are instantiated in the C++ environement as are "mstring" objects "r", "x" and "z". When the "mstring" objects are created, they are also recorded in the Mumps runtime symbol table along with the location of the C++ data area for each (in the form of a C++ "string" class object). Modifications by either the C++ or the Mumps environment to one of these variables is visible to both environments. Once again, the C++ declaration of the mstring variables must preced the Mumps usage.
In the example, Mumps code is used to build the global array "^a()". A C++ method, Avg() is then applied (averages the values of all elements below the referenced element) and the result (numeric) is captured by the "mstring" object "x" which is then printed from the Mumps environment. Similarly, the Count() method is applied to the entire global array and to just one node of the tree. Note that globals are not preceded by the circumflex in C++. Next, a small matrix is constructed in the Mumps environment and then the Transpose() method is applied and the resulting matrix printed. Finally, two strings are initialized in the Mumps environment and then processed by the sw() method. The sw() method performs a Smith-Waterman pattern match on the two strings and returns a total score. Note: invocation parameters request intermediate output which is shown in the output section. The total score is captured by the "mstring" object and the result is printed from the Mumps side. Note that the Mumps variables "x" and "y" are passed as parameters to the sw() function!
There is a large and growing number of such functions aimed mainly at applications to information storage and retrieval, bioinformatics and cheminformatics. Our experience indicates that some things are easier and quicker to do on the C++ side while others are easier on the Mumps side. With versions 8.00 and higher, the Mumps Compiler attempt to seamlessly integrate both environments and provide complete interoperability between Mumps and the C/C++ environment.
zmain
+ global a("a");
+ global b("b");
+ mstring r("r");
+ mstring x("x");
+ mstring y("y");
# Build a 100 by 5 global array whose contents are 1,2,3, ...
set k=1
for i=1:1:100 for j=1:1:5 set ^a(i,j)=k set k=k+1
# Use an MDH function to calculate the average of the values in ^a()
+ x=a().Avg();
write x,!
# Use an MDH function to count the number of cells in ^a()
+ x=a().Count();
write x,!
+ x=a("1").Count();
write x,!
# Delete the global and rebuild
kill ^a
set ^a("1","1")=2
set ^a("1","2")=3
set ^a("2","1")=4
set ^a("2","2")=0
# Use an MDH function to put the transpose of ^a() into ^b()
+ a().Transpose(b());
write "the transposed matrix (by row):",!
# use an MDH function to print ^b()
+ b().TreePrint();
# Use an MDH function to do a Smith-Waterman alignment - note blanks at
# start of each string - these are required.
set x=" now is the time for all good men to come to the aid of the party"
set y=" time for good men"
# call the MDH Smith-Waterman function
+ r=sw(x,y,1,0,-1,-1,3);
write "The best alignment score is ",r,!
----------------------------------------------------------------
output:
250.5
500
5
1
1=2
2=4
2
1=3
2=0
S-W Alignments for:
64 now is the time for all good men to come to the aid of the party
22 time for good men
29 men 32
::::
19 men 22
score=12
29 - men 32
::::
18 men 22
score=11
23 l good-- men 32
::::: ::::
11 good men 22
score=24
22 ll good-- men 32
::::: ::::
11 - good men 22
score=23
16 for all good-- men 32
::::: ::::: ::::
6 for -- good men 22
score=37
12 time- for all good-- men 32
:::: ::::: ::::: ::::
1 time for -- good men 22
score=48
The best alignment score is 48
|
To see the collection of MDH functions, see mdh.html