How do I call C routines from Fortran?

Methods for calling C routine from Fortran are covered in some detail in the tutorial Fortran 95 and Beyond. There is a related tutorial the covers the transition from Frotran 77 to 90. Here we look at a more contrete example, calling a C routine to make a subdirectory from Fortran. The routine of interest is mkdir.

We are looking at the library routine mkdir called from C not the command for making a directory from a terminal window. One difference is that the library routine does not support recursive (multilevel) directory creations. The user for whom this example was orignaly created wanted to do a recursive mkdir so we replaced a simple mkdir call with a recursive version found at http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html

On the other hand, for simplicity, we assumed that people would want to make their directories read, write, execute/search by owner. So we hardcoded the flag in the mkdir call to set the appropriate protections.

Here is our Fortran code:

Fortran


module cfuncs
  use iso_c_bindingonlyC_CHARC_NULL_CHAR
  interface
    subroutine my_mkdir(stringbind(Cname="my_mkdir")
      use iso_c_bindingonlyc_char
      character(kind=c_char) :: string(*)
    end subroutine my_mkdir
  end interface
end module
    program mkdoor
      use cfuncs
      character (len=128)scratchPath,base
      character (len=32)subsub,sub
      base="scratch"
      sub="1234"
      subsub="5678"
      scratchPath=trim(adjustl(base))//"/" &
                                     //trim(adjustl(sub))//"/" &
                                     //trim(adjustl(subsub))//C_NULL_CHAR
      write(*,"(a128)")scratchPath
      call my_mkdir(scratchPath)
!      call my_mkdir("/u/pa/ru/tkaiser/scratch/9876/5432"//C_NULL_CHAR)
    end program

In our main program we start with a base directory, scratch. We will create a sub directory, 1234, and a subdirectory beneath that 5678. In the end we will have a directory scratch/1234/5678.

The variable scratchpath is a concatenation of scratch "/" 1234 "/" 5678. Note the Fortran string concatenation operator is //. Finally we add C_NULL_CHAR to the end of our string. C_NULL_CHAR is a C null character that is defined in the module iso_c_binding. We need to put this at the end of Fortran strings that are being pass to C routines to properly terminate it.

Once the path to the directory is made we create the directroy simply by calling the routine:

call my_mkdir(scratchPath)

There is an interface to the subroutine my_mkdir in the module cfuncs. This interface is included with the

use cfunc

Let's look at the module and the interface. The first thing we see is that we use portions of iso_c_binding. ISO_C_BINDING provides named constants, types and procedures that are useful in a mixed-language (C and Fortran) programing. Here we use C_CHAR and C_NULL_CHAR. the first is a data type matching the C char type. The second is the C null character used to terminate a string.

Links describing the ISO_C_BINDING module

The interface for our subroutine gives the name of the routine as called from Fortran "my_mkdir". The subroutine line also contains the extra bind(C, name="my_mkdir") statement. This says that our subroutine is actually written in C and the name of the C subroutine is also my_mkdir. Note that the Fortran and C names do not need to be the same. Finally, we say that the input argument for the C subroutine is a C char array.

Here is our C code.

C code


#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
// S_IRWXU read, write, execute/search by owner
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysstat.h.html

  void my_mkdir1(char *string/* equivalent: char string[]  */
  {
     printf("%s\n"string);
     mkdir(string,S_IRWXU);
  }
// does a recursive mkdir
//http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
 void my_mkdir(char *dir) {
        char tmp[256];
        char *p = NULL;
        size_t len;
//        printf("%s\n", dir);

        snprintf(tmpsizeof(tmp),"%s",dir);
        len = strlen(tmp);
        if(tmp[len - 1] == '/')
                tmp[len - 1] = 0;
        for(p = tmp + 1; *pp++)
                if(*p == '/') {
                        *p = 0;
                        mkdir(tmpS_IRWXU);
                        *p = '/';
                }
        mkdir(tmpS_IRWXU);
}

As discussed above we are hardwiring the call to the mkdir routine to create the directory read, write, execute/search by owner. This is done by using the flag S_IRWXU as the second argument.

The routine my_mkdir1 is not used here. It would create a single subdirectory directory but not multiple directories since the mkdir library call does not support such an operation.

The routine named my_mkdir maps to the name in the Fortran module bind(C, name="my_mkdir") statement and thus is the routine that is called from Fortran. As stated here and in the webpage given in the source this does a recursive directory creation, simulating the -p option in the mkdir command.

The makefile for this example is simple.

Makefile

FC=gfortran
CC=gcc

#IBM Blue Gene Q
#FC=bgxlf90_r
#CC=bgxlc_r

#IBM Power
#FC=xlf90_r
#CC=xlc_r

#intel
#FC=ifort
#CC=icc

#Portland Group both x86 and power
#FC=pgf90
#CC=pgcc


mymkdir: dodir.f90 mymkdir.o
	rm -f cfuncs.mod
	$(FC) dodir.f90 mymkdir.o -o mymkdir

mymkdir.o: mymkdir.c
	$(CC) -c mymkdir.c

clean:
	rm -f mymkdir.o mymkdir cfuncs.mod dodir.o

tar:
	tar -cf mkdir.tar dodir.f90 mymkdir.c index.html $(MAKEFILE_LIST)

It is included in the tar ball. This has been tested with indicated compilers.

Notes

  1. Environmental variables most likely will not work in as part of the string that is passed to the subroutine.
  2. WARNING: If you use ~ to stand for your home directory as the base of the directory path this also will most likely not work as intended. You may end up with a directory ~ somewhere outside of your home. This is a problem. If you try to delete ~ you may wipe our your home directory. You need to "quote" the "~" to reference this directory.

Files

777 Apr 20 16:10 dodir.f90
23591 Apr 20 16:27 index.html
431 Apr 20 16:14 makeit
30720 Apr 20 16:11 mkdir.tar
970 Apr 20 16:05 mymkdir.c