fortrancharacterf2pyfortran-iso-c-binding

How do I concatenate a NULL character to a character array in Fortran to call a c function?


I have the following test function:

#include <stdlib.h>
#include <stdio.h>

void print_string(char *text);

void print_string(char *text) {
    printf("---\n%s\n---\n", text);
}

And the following module which encapsulates the call with a Fortran subroutine using iso c bindings:

module test_c_lib

        use iso_c_binding
        implicit none
                
contains
        
    subroutine test(text)
    
            use iso_c_binding
            
            character,intent(in)       :: text(:)
            
            ! Interface to C function
            interface
                    subroutine c_print_string(t) bind(C, name="print_string")
                            import
                            character(kind=c_char) :: t(:)
                    end subroutine
            end interface
            
            ! Call C function
            print *,"AAAAA"
            print *,text
            print *,"AAAAA"         
            
            call c_print_string(text // C_NULL_CHAR)
            
    end subroutine
    
end module

The way to define the subroutine in Fortran using ISO C BINDINGS is extracted from this documentation link. I further encapsulate it a bit as f2py does not support iso c bindings.

I compile everything via makefile:

$ cat makefile 
f_mod.so:       f_mod.f90 c_lib.o
                f2py -c f_mod.f90 c_lib.o -m f_mod

c_lib.o:        c_lib.c
                gcc -c -fpic c_lib.c -o c_lib.o

It compiles but:

  1. I get the following warning:
      150 |                 call c_print_string(text // C_NULL_CHAR)
          |                                    1
    Warning: Character length mismatch (2/1) between actual argument and assumed-shape dummy argument 't' at (1) [-Wargument-mismatch]
  1. I get the following output when invoking via import fmod; f_mod.test_c_lib.test("Foo"):
     AAAAA
     Foo
     AAAAA
    ---
    8v$
    ---

So f2py is working but when passing text // C_NULL_CHAR as parameter it does not seem to work as I get garbage from the C function output.


Solution

  • You have two mistakes:

    To make the character dummy argument interoperable with the char * C parameter, t should be an assumed size array:

    character(kind=c_char) :: t(*)   ! Assumed size, not assumed shape
    

    You can also use the CFI_cdesc_t C type to have t an assumed shape array, or an assumed length scalar, but that's a lot more advanced.

    Even making t assumed size, you don't have a working procedure, because of the next problem: the elemental nature of //.

    As text is an (assumed shape) array the concatenation text // C_NULL_CHAR is done elementally, giving the length-2 array1 with each element of text concatenated with the C null char. The C function then sees input looking like [text(1), C_NULL_CHAR, text(2), C_NULL_CHAR, ...].

    To have a length-1 character array with C_NULL_CHAR appended, you need to use an array constructor:

    call c_print_string([text,C_NULL_CHAR])
    

    1 That the argument is of length-2 is the reason for the warning about a "character length mismatch".