ARSC T3E Users' Newsletter 170, June 4, 1999

Passing Fortran CHARACTER arrays to C functions

One of our J90 users ran into trouble this week when calling a C function from Fortran. The troublesome parameter was a Fortran character array.

The solution is to include "fortran.h" in the C program, make string variables of type "_fcd" instead of "char*", and use SGI's access functions, _cptofcd, _fcdtocp, _fcdlen, _btol, and _ltob to access the string.

What follows is a longer version of this story. It goes into more depth concerning interlanguage communication as did a related article in newsletter #125:

/arsc/support/news/t3enews/t3enews125/index.xml

To get started, our J90 user knew the rule that Fortran is always pass-by-reference. He made the logical assumption that the C function parameters should be addresses, and in particular, that the string should be a character address. From the code:

FORTRAN:


      integer ierr
      character*50 command

      ierr = 0
      command = 'ls -l'//char(0)

      call simple(command,ierr)

      print *,'Back in Fortran: ierr=', ierr

C:

    void SIMPLE (command,ierr)
       char* command;
       int* ierr;
This sort of worked.

The Fortran syntax is correct. The line:


      command = 'ls -l'//char(0)
uses the Fortran character concatenation operator, "//", to append an ASCII NULL (i.e., the "char(0)") to the end of the desired string. This is for the benefit of C, which expects NULL terminated strings.

The line:


      call simple(command,ierr)
is also correct. On the Fortran side, you pass actual parameters as-is.

Things are more interesting on the C-side. For starters, the function declaration:


    void SIMPLE (command,ierr)
is correct. For the CF90 compiler to find the C function, the name MUST be shifted to uppercase. Fortran is a case-insensitive language, and CF90 implements this by shifting the entire Fortran code to uppercase. C and the linker, however, are sensitive to case and don't alter it.

Thus, in this example, the linker will expect an external function, SIMPLE (the CF90-shifted version of "simple"), and it will find it because the C code explicitly names the function "SIMPLE." (NOTE: the SGI MIPSpro7 Fortran 90 compiler for IRIX reverses this rule: it shifts everything to lower-case and appends a "_" to function names. Thus, in IRIX, the correct name for the C function would be "simple_". Confusing?)

Also correct is the "void" type given the C function. The function is treated on the Fortran side as a subroutine (not a function), and thus it shouldn't return a value.

Also correct is the declaration of the "ierr" dummy parameter:


       int* ierr;
Incorrect is the declaration of the string:

       char* command;
Here's the entire C function:

    void SIMPLE (command, ierr)
       char* command;
       int* ierr;
    {
      printf ("In C: value of command: \"%s\"\n", command);

      *ierr = system ( command );

      if (*ierr != 0) 
        perror ("In C: error from \"system\": ");
    }
The incorrect declaration produced different failures on the J90 and T3E. On either system, the initial "printf" worked. Here are two sample runs:

J90 RUN:


    chilkoot$ ./bad  
    In C: value of command: "ls -l"
    In C: error from "system": : Bad address
     Back in Fortran: ierr= 32512
    chilkoot$                                                     
T3E RUN:

    yukon$ ./bad
    In C: value of command: "ls -l"
    total 13344
    -rw-------   1 baring   staff        186 Jun  3 16:57 FCD
    -rw-------   1 baring   staff        143 Jun  3 14:39 Makefile
    -rwx------   1 baring   staff    1365232 Jun  4 11:37 a.out
    SIGNAL: Operand range error ( [32] memory management fault)

     Beginning of Traceback (PE 0):
      Interrupt at address 0x800001388 in routine 'SIMPLE'.
      Called from line 10 (address 0x8000014e8) in routine 'SIMPLEF'.
      Called from line 475 (address 0x800000c98) in routine '$START$'.
     End of Traceback.
    Operand range error(coredump)
    yukon$
EXPLANATION?

J90:

"printf" liked the address of "command", but "system" didn't.

"system" calls "fork" which calls "exec" which starts "/bin/sh" which is passed the original character address. I'm assuming that the Fortran address space on the J90 is outside the bounds for one of these calls.

"system" handled the error and returned, "perror" printed the message "Bad address," the C function returned, and the Fortran program was able to continue. (See "man perror" for more on this useful routine.)

T3E:

Both "printf" AND "system" liked the address of "command." As the output shows, "system" successfully executed "ls -l" and then the program crashed.

The operand range error occurred when "system" returned and the process tried to store the return value to *ierr. The problem is misalignment of the "ierr" variable and it's caused by the same programming mistake, declaring "char* command."

Given the C function declaration:


      void SIMPLE (command,ierr)
         char* command;
         int* ierr;
"command" is expected on byte 0 of the argument stack, and "ierr" is expected on byte 8.

On the J90, a Fortran character descriptor takes 8 bytes. Thus, given this call:


        call simple(command,ierr)
Fortran puts the address of ierr at byte 8 of the argument stack. This happens to coincide with the C function's view of the call stack.

On the T3E, however, a Fortran character descriptor takes 16 bytes (twice the size of the C character pointer). Thus, Fortran puts the address for ierr at byte 16. The C function treats the second byte of "command" as the address of ierr and, not surprisingly, it causes an ORE.

THE CORRECT WAY

From the "Cray C/C++ Reference Manual"


  Sec. 8.1.2.4 

    Logical and character data need special treatment for calls between
    C/C++ and Fortran. Fortran has a character descriptor that is
    incompatible with a character pointer in C/C++. 
The solution is to include "fortran.h" in the C program, declare "command" to be of type "_fcd", and use SGI's access functions. From "man cptofcd":

  NAME
     _cptofcd, _fcdtocp, _fcdlen, _btol, _ltob 

     Passes character strings and logical values between Standard C and
     Fortran
CORRECTED PROGRAM

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Fortran program: simplef.f
      program simplef

      integer ierr
      character*50 command

      ierr = 0
      command = 'ls -l'//char(0)

      call simple(command,ierr)

      print *,'Back in Fortran: ierr=', ierr

      end
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



/***********************************************************
 * C program: goodsimplec.c
 ***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <fortran.h>

void SIMPLE(command, ierr)
   _fcd command;
   int* ierr;
{
  printf ("In C: value of command: \"%s\"\n", _fcdtocp(command) );

  *ierr = system ( _fcdtocp(command) );

  if (*ierr != 0) 
    perror ("In C: error from \"system\": ");
}
/***********************************************************/
Compiling and Linking:

  yukon$ cc -c goodsimplec.c;
  yukon$ f90 simplef.f goodsimplec.o -o good
Running:

  yukon$ ./good
  In C: value of command: "ls -l"
  total 9248
  -rw-------   1 baring   staff        143 Jun  3 14:38 Makefile
  -rwx------   1 baring   staff    1474672 Jun  4 09:40 a.out
  -rwx------   1 baring   staff    1488352 Jun  4 11:50 bad
   Back in Fortran: ierr= 0
Final Note:

The Fortran function, ISHELL, lets Fortran programmers run shell commands from within a program. Our J90 user didn't need a wrapper to the C "system" routine: he could just use ISHELL.

New (to us) Qstat Modes


[ Thanks to Alan Wallcraft of NRL for this tip. ]
The "-m" option can be given to qstat in combination with other options, such as "-a" or "-c". It adds columns detailing resource requests, limits, and usage. It's a fast way to see how much time your request has used (for lots of detail, use "qstat -f <IDENTIFIER>").

yukon 1> qstat -cm
-------------------------------------------
NQS 3.3.0.5 QUEUE COMPLEX MPP LIMITS: yukon      
-------------------------------------------
QUEUE COMPLEX     RUN         PE'S
--------------- --- ---  ------ ------
cgcp              2/1       256/128    
cnormal          60/3       256/120    


yukon 2> qstat -am
-------------------------------------
NQS 3.3.0.5 BATCH REQUEST MPP SUMMARY
-------------------------------------
IDENTIFIER    NAME    USER        PE'S     REQUEST-TIME  P-TIME  ST
                                LIM USED     LIM   USED     LIM
------------- ------- -------  ---- ----  ------ ------  ------  ---
1891.yukon    myjob   smithw    128  128   28800   2109  <28800>  R03
1880.yukon    myproj  wsmith    128          300          <1800>  Qge
1881.yukon    mywk_1  jones     128          300          <1800>  Qge
1895.yukon    mywk_2  jones      70   70    1800    671   <1800>  R04
1892.yukon    mytask  johns      18   18   28800   6352  <28800>  R03
1885.yukon    myprog  johnsj     32   32   28800  16933   28800   R04
1868.yukon    myprj   jonhsj    100        14400         <14400>  Qge
1897.yukon    mywd    joans      64        14400          14400   Qge
no pipe queue entries

SGI On-line Technical Publications

http://techpubs.sgi.com/ is SGI's new server for its library of technical publications. It has the search function on page one and seems fast. I used "techpubs" a lot for the above article, searching on "interlanguage" in both the "Unicos/mk" and "IRIX 6.5" libraries to started.

Quick-Tip Q & A



A:{{ What's the best restaurant in Minneapolis and why? }}


  Thanks to those who responded.  

    Buca's - Italian family  restaurants - huge portions nice real food.
      Decent price also.  Minneapolis - downtown

    Vintage - Great wines by the bottle, excellent food, live music Sunday
      - Tuesday.  Price is about $20.00/plate St. Paul

    Mud Pie - Vegetarian place, good food, good prices Minneapolis,
      'Uptown' area.

    Da Afgan - excellent middle eastern food, good prices - Near the
      airport

    La Cucaracha - Downtown.  You wouldn't expect great Mexican food so far
      from the border, 'less you were from Fairbanks.  Decent chile
      relleno.

    Chiang Mai Thai - Uptown.  Spiciness index: "mild, medium, hot, or
      crazy hot".



Q: C header files always end in ".h".  Why the different conventions
   for Fortran?  For instance:

      include 'mpif.h'
      include 'VT.inc'
      include "mpp/shmem.fh"

[ Answers, questions, and tips graciously accepted. ]


Current Editors:
Ed Kornkven ARSC HPC Specialist ph: 907-450-8669
Kate Hedstrom ARSC Oceanographic Specialist ph: 907-450-8678
Arctic Region Supercomputing Center
University of Alaska Fairbanks
PO Box 756020
Fairbanks AK 99775-6020
E-mail Subscriptions: Archives:
    Back issues of the ASCII e-mail edition of the ARSC T3D/T3E/HPC Users' Newsletter are available by request. Please contact the editors.
Back to Top