# Presentation: 735448

## Mixed Language Programming on Seaborg

- Mark Durst
- NERSC User Services

## Overview

- Matching variable types
- Mixing C and FORTRAN
- Mixing C++ and FORTRAN
- Mixing C and FORTRAN modules
- Character arguments
- Mixed-case names
- Other issues
- Mixed-language library use
- Further References

## Type Correspondences

| FORTRAN | C/C++ | Bytes |
| --- | --- | --- |
| REAL, REAL*4, REAL(KIND=4) | float | 4 |
| REAL*8, REAL(KIND=8), DOUBLE PRECISION | double | 8 |
| REAL*16, REAL(KIND=16) | long double (with
-qlongdouble) | 16 |
| INTEGER*4, INTEGER(KIND=4) | int, long int
 | 4 |
| INTEGER*8, INTEGER(KIND=8) | long long int (may require 
–qlonglong) | 8 |
| INTEGER*2 | short int | 2 |
| COMPLEX, COMPLEX*4 | structure of two floats | 8 |
| COMPLEX*8 | structure of two doubles | 16 |
| DOUBLE COMPLEX, COMPLEX*16 | structure of two long doubles | 32 |
| CHARACTER | Here be dragons... |  |

## Calling C from FORTRAN

- Calling by reference expected by FORTRAN
- Lowercase C routine name easiest (since Fortran by default not case-sensitive)
*No change* in routine name
- –qextname option to xlf and –brename option to ld provide other options

## Calling C from FORTRAN (example)

- !FILENAME: f_main.f
- !
- PROGRAM f_calls_c
-  IMPLICIT NONE
-  REAL :: p1_x, p1_y, p2_x, p2_y
-  EXTERNAL calc_dist
-  p1_x = 0.0
-  p1_y = 0.0
-  p2_x = 3.0
-  p2_y = 4.0
-  CALL calc_dist(p1_x,p1_y,p2_x,p2_y)
- END PROGRAM f_calls_c

- /* FILENAME: calc_c.c
- */
- #include <math.h>
- void calc_dist(float *x1, float *y1, float *x2, float *y2)
- {
-   float dxsq, dysq, distance;
-   dxsq = (*x2-*x1)*(*x2-*x1);
-   dysq = (*y2-*y1)*(*y2-*y1);
-   distance = sqrt( dxsq + dysq );
-   printf("The distance between the points is %13.5e\n",
- 	distance);
- }

## Calling C from FORTRAN (running)

- > cc -c calc_c.c
- > xlf90  -o f_calls_c f_main.f calc_c.o
- ** f_calls_c   === End of Compilation 1 ===
- 1501-510  Compilation successful for file f_main.f.
- > ./f_calls_c
- The distance between the points is   5.00000e+00

## Calling FORTRAN from C

- Again, calling by reference most straightforward
- Must specify –lxlf90 –lm in cc command
- Or use xlf90 to load

## Calling FORTRAN from C (example)

- /* FILENAME: c_main.c
-    Shows how to call a FORTRAN routine from C
- */
- extern void calc_dist(float*, float*, float*, float*);
- void main()
- {
-   float p1_x,p1_y,p2_x,p2_y;
-   p1_x = 0.0;
-   p1_y = 0.0;
-   p2_x = 3.0;
-   p2_y = 4.0;
-  calc_dist(&p1_x,&p1_y,&p2_x,&p2_y);
- }

- ! FILENAME: calc_f.f
- !
- SUBROUTINE calc_dist(x1,y1,x2,y2)
-    IMPLICIT NONE
-    REAL :: x1,y1,x2,y2
-    REAL :: distance
-    distance = SQRT( (x2-x1)**2 + (y2-y1)**2 )
-    WRITE(6,1) distance
- 1  FORMAT("The distance between the points is ", 1pe13.5)
- END SUBROUTINE calc_dist

## Calling FORTRAN from C (running)

- > xlf90 -c calc_f.f
- ** calc_dist   === End of Compilation 1 ===
- 1501-510  Compilation successful for file calc_f.f.
- > cc -o c_calls_f c_main.c calc_f.o -lxlf90 -lm
- > ./c_calls_f
- The distance between the points is   5.00000E+00

## Calling C++ from FORTRAN

- Must have C wrapper function
- C++ compiler mangles routine names
- C wrapper name handled just like C
- Also any C++ that is “just like” C
- xlC* *command must be used to link

## Calling C++ from FORTRAN (C++ code)

- /* FILENAME: calc_c++.h
- 	This is the C++ routine.
- */
-  #include <iostream.h>
-  #include <math.h>
- template<class T> class calc {
- public:
- calc() {cout <<"Inside C++ constructor"<<endl;}
- ~calc() {cout <<"Inside C++ destructor"<<endl;}

- void calc_dist(T *x1, T *y1, T *x2, T *y2)
-  {
-          T dxsq, dysq, distance;
-          dxsq = (*x2-*x1)*(*x2-*x1);
-          dysq = (*y2-*y1)*(*y2-*y1);
-          distance = sqrt( dxsq + dysq );
-          cout <<"The distance between the points is "
- 		<<distance<<" \n"<<endl;
-  }
- };

## Calling C++ from FORTRAN (C wrapper)

- /* Filename: calc_cwrapper.C */
- #include <stdio.h>
- #include "calc_c++.h"
- extern "C" void calc_dist(float *x,float *y,float *X,float *Y);
- void calc_dist(float *x,float *y,float *X,float *Y) {
-         printf("Inside C function, creating C++ object\n");
-         calc<float>* c=new calc<float>();
-         c->calc_dist(x,y,X,Y);
-         printf("Back in C function, will delete C++ object\n");
-         delete c;
- }

## Calling C++ from FORTRAN (running)

- > xlf90 -c f_main.f
- ** f_calls_c   === End of Compilation 1 ===
- 1501-510  Compilation successful for file f_main.f.
- > xlC -c calc_cwrapper.C
- > xlC -o f_calls_c++ calc_cwrapper.o f_main.o -lm -lxlf90
- > ./f_calls_c++
- Inside C function, creating C++ object
- Inside C++ constructor
- The distance between the points is 5
- Back in C function, will delete C++ object
- Inside C++ destructor

## Calling FORTRAN from C++

- Can simply declare FORTRAN subroutine like C:
- extern "C" void *fortran_routine_name*
- Or with FORTRAN function:
- extern "C" *return_type* *fortran_routine_name*

## Calling F90 module routines from C

- Refer to FORTRAN routine as __*modulename*_MOD_*routinename*
- There is apparently a way for C to read prototype info out of .mod files* *

## Calling F90 module routines from C (module code)

- ! FILENAME: calc_mod_f.f
- !
- MODULE CALCULATION
-       CONTAINS
-         SUBROUTINE CALC_DIST(x1,y1,x2,y2)
-         IMPLICIT NONE
-         REAL :: x1,y1,x2,y2
-         REAL :: distance
-         distance = SQRT( (x2-x1)**2 + (y2-y1)**2 )
-         WRITE(6,1) distance
- 1       FORMAT("The distance between the points is ", 1pe13.5)
-         END SUBROUTINE CALC_DIST
- END MODULE CALCULATION

## Calling F90 module routines from C (C code)

- /* FILENAME: c_main_mod.c
-    Shows how to call a FORTRAN routine from C
-    In this case the FORTRAN routine, named CALC_DIST,
-         is in the FORTRAN module named calculation
- */
- extern void __calculation_MOD_calc_dist(float *, float *,
- 				float *, float *);
- void main()
- {
-         float p1_x,p1_y,p2_x,p2_y;
-         p1_x = 0.0;
-         p1_y = 0.0;
-         p2_x = 3.0;
-         p2_y = 4.0;
-         __calculation_MOD_calc_dist(&p1_x,&p1_y,&p2_x,&p2_y);
- }

## Calling F90 module routines from C (running)

- > xlf90 -c calc_mod_f.f
- ** calculation   === End of Compilation 1 ===
- 1501-510  Compilation successful for file calc_mod_f.f.
- > cc -o c_calls_f_mod c_main_mod.c calc_mod_f.o -lxlf90 -lm
- > ./c_calls_f_mod
- The distance between the points is   5.00000E+00

## Handling character arguments

- FORTRAN passes (by value) an extra argument with the length of the string
- C assumes strings are a null-terminated sequence of bytes
- Can process this argument at the C end
- Can null-terminate strings at the FORTRAN end
- CALL TAKE_CHARS(b // CHAR(0))
- may also want %REF

## Mixed case names

- -U option in xlf
- All functions, variables, and names become case-sensitive
- @PROCESS MIXED compiler directive
- on a specific compiler unit

## Design Issues

- Porting an existing code
- make and version control recommended
- Designing a new code
- FORTRAN for numbers
- C for strings
- (or avoid entirely)
- Maintaining portability
- Wrappers/shims useful
- make (but try to avoid “code schism”)
- Maybe -qextname

## Miscellaneous Issues

- Compiler options are frequent pitfall (or helper)
- including: -qalign, -q32, -q64, -qautodbl, -qintsize, -qnullterm, -qrealsize
- Name conflicts can occur (e.g., main)
- -qextname can help (but with added complexity...)
- Ditto -brename
- %VAL and %REF built-in functions

## Miscellaneous Issues (cont’d)

- -qextchk can help find errors; ditto –qsigtrap, xl__trbk and -g
- “Funny” FORTRAN function return values not supported
- pointer, array, or character of nonconstant length
- (not that I can make this work even with simple examples)
- Can share data structures C  FORTRAN
- Cross-language I/O: Just say no!

## Cross-language library calls

- No particular tricks; as described above
- For C, function prototypes may be a challenge
- Can simply roll your own
- Load with xlf90 for FORTRAN libraries
- 64-bit or 32-bit uniformity; ditto thread-safe
- Column-major (FORTRAN) vs. row-major (C) array order

## Cross-language library call (example)

- !FILENAME: wrap_s01baf.f
- !
- ! Gives an example of wrapping a
- ! Fortran function into a
- ! subroutine that can be called
- ! by even the most incompetent
- ! cross-language programmer, viz.
- !              - Mark Durst
- subroutine wrap_s01baf(x,y,i)
-   double precision :: x, y
-   integer :: i
-   y = s01baf(x,i)
- end subroutine wrap_s01baf

- /* FILENAME: c_nag.c
-    Shows how to call a FORTRAN wrapper
-    for a NAG routine from C
- */
- #include <stdio.h>
- #include <math.h>
- extern void wrap_s01baf(double*,double*, int*);
- main(int argc, char *argv[]){
-   double x, y;
-   int ifail;
-   x =1.0;
-   y = 0.0;
-   wrap_s01baf(&x,&y, &ifail);
-   printf("%f\n",y);
- }

## Cross-language library call (running)

- > xlf90 -c wrap_s01baf.f
- ** wrap_s01baf   === End of Compilation 1 ===
- 1501-510  Compilation successful for file wrap_s01baf.f.
- > cc -c c_nag.c
- > module load nag
- > xlf90 -o c_nag c_nag.o wrap_s01baf.o $NAG
- > ./c_nag
- 0.693147

## Further References

- Examples and most content are from
- _[http://www.nersc.gov/nusers/resources/software/ibm/c_and_f.php](http://www.nersc.gov/nusers/resources/software/ibm/c_and_f.php)_
- More details in *XL Fortran User’s Guide*, Ch. 10
- _[http://www.nersc.gov/vendor_docs/ibm/xlf/html](http://www.nersc.gov/vendor_docs/ibm/xlf/html)_
- HPC Users Seaborg  Software  Compilers  xlf vendor  *User’s Guide*