1

I have the following C library (in particular the function I am debugging now is pass_by_reference):

#include <stdlib.h>

double multiply_numbers(double a, double b);
double *get_array(double a);
void pass_by_reference(int *a);

double multiply_numbers(double a, double b) {
    return a*b;
}

double *get_array(double a) {
    double *retval;
    int i;

    retval = malloc(100*sizeof(a));

    for(i=0; i<100; i++) {
        retval[i] = i*a;
    }

    return retval;
}

void pass_by_reference(int *a) {
    *a = 8; 
}

And I am trying to get this wrapped by a Fortran subroutine named pass_by_reference (to be latter called via f2py in python):

module test_c_lib

        use iso_c_binding
        implicit none
        
contains

        subroutine multiply(x, y, z)

                use iso_c_binding

                real(8), intent(in)        :: x
                real(8), intent(in)        :: y
                real(8), intent(out)       :: z
        
                ! Interface to C function
                interface                        
                        real(c_double) function c_multiply_numbers(a, b) bind(C, name="multiply_numbers")
                                import
                                real(c_double), value :: a,b
                        end function
                end interface

                ! Call C function               
                z = c_multiply_numbers(x,y)                

        end subroutine
        
        subroutine get_array(x, z)

                use iso_c_binding

                real(8), intent(in)        :: x
                real(8), intent(out)       :: z(100)
                
                type(c_ptr)   :: ret_c_ptr
                real(8), pointer :: f_ptr(:)
        
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_get_array(a) bind(C, name="get_array")
                                import
                                real(c_double), value :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_get_array(x)                
                call c_f_pointer(ret_c_ptr, f_ptr, [100])
                               
                z = f_ptr

        end subroutine
        
        subroutine pass_by_reference(z)

                use iso_c_binding

                integer, intent(out)       :: z
                
                ! Interface to C function
                interface                        
                        type(c_null_ptr) function c_pass_by_reference(a) bind(C, name="pass_by_reference")
                                import
                                type(c_int), value :: a
                        end function
                end interface

                ! Call C function                
                c_pass_by_reference(z)

        
        end subroutine


end module

And the corresponding 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

When trying to get this compiled with f2py I get:

   57 |                 use iso_c_binding
      |                    2                    
......
   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C, name="pass_by_reference")
      |                                        1
Error: Type name 'c_null_ptr' at (1) conflicts with previously declared entity at (2), which has the same name
f_mod.f90:65:43:

   57 |                 use iso_c_binding
      |                    2                       
......
   65 |                                 type(c_int), value :: a
      |                                           1
Error: Type name 'c_int' at (1) conflicts with previously declared entity at (2), which has the same name
f_mod.f90:63:24:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C, name="pass_by_reference")
      |                        1
Error: The type for function 'c_pass_by_reference' at (1) is not accessible
f_mod.f90:63:24: Warning: Implicitly declared BIND(C) variable 'c_pass_by_reference' at (1) may not be C interoperable [-Wc-binding-type]
f_mod.f90:70:35:

   70 |                 c_pass_by_reference(z)
      |                                   1
Error: 'c_pass_by_reference' at (1) is not a variable
f_mod.f90:63:24-71:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C, name="pass_by_reference")
      |                        2                                              1
Warning: Implicitly declared variable 'a' at (1) may not be C interoperable but it is a dummy argument to the BIND(C) procedure 'c_pass_by_reference' at (2) [-Wc-binding-type]
f_mod.f90:63:24:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C, name="pass_by_reference")
      |                        1
Warning: Implicitly declared BIND(C) function 'c_pass_by_reference' at (1) may not be C interoperable [-Wc-binding-type]
error: Command "/usr/local/bin/gfortran9 -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops -I/tmp/tmpk7tn1bgi/src.freebsd-12.2-RC1-amd64-3.7 -I/usr/local/lib/python3.7/site-packages/numpy/core/include -I/usr/local/include/python3.7m -c -fPIC f_mod.f90 -o /tmp/tmpk7tn1bgi/f_mod.o -J/tmp/tmpk7tn1bgi/ -I/tmp/tmpk7tn1bgi/" failed with exit status 1
*** Error code 1

I have two questions:

  • When iso_c_binding is used, and in this particular example, is the variable a passed to pass_by_reference function actually passed by reference or do I have to specify something different in the interface?
  • Why f2py is reporting Type name 'c_int' at (1) conflicts with previously declared entity at (2), which has the same name?
  • How do I specify that the C function does not return anything (void)? I have tried to use type(c_null_ptr)
7
  • 2
    You have several distinct questions here, so it may be better to split them out. However, question 2 is just a boring typo on line 65: type(c_int) should be integer(c_int). Commented Jul 13, 2021 at 15:21
  • 2
    Similarly, c_null_ptr is a constant of type c_ptr not a type itself. (C functions with void return map to Fortran subroutines.) Commented Jul 13, 2021 at 15:23
  • 2
    And to some extent, the value attribute specification for a is a good hint that it is passed by value, not by reference. Commented Jul 13, 2021 at 15:35
  • Thanks for the indications. With that all errors are fixed except one: f_mod.f90:70:35: 70 | c_pass_by_reference(z) Error: 'c_pass_by_reference' at (1) is not a variable Commented Jul 14, 2021 at 0:25
  • 2
    You missed the hint "C functions with void return map to Fortran subroutines". Commented Jul 14, 2021 at 11:38

1 Answer 1

1

After implementing all the corrections pointed out in the comments this is the encapsulation that works:

module test_c_lib

        use iso_c_binding
        implicit none
        
contains

        subroutine multiply(x, y, z)

                use iso_c_binding

                real(8), intent(in)        :: x
                real(8), intent(in)        :: y
                real(8), intent(out)       :: z
        
                ! Interface to C function
                interface                        
                        real(c_double) function c_multiply_numbers(a, b) bind(C, name="multiply_numbers")
                                import
                                real(c_double), value :: a,b
                        end function
                end interface

                ! Call C function               
                z = c_multiply_numbers(x,y)                

        end subroutine
        
        subroutine get_array(x, z)

                use iso_c_binding

                real(8), intent(in)        :: x
                real(8), intent(out)       :: z(100)
                
                type(c_ptr)   :: ret_c_ptr
                real(8), pointer :: f_ptr(:)
        
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_get_array(a) bind(C, name="get_array")
                                import
                                real(c_double), value :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_get_array(x)                
                call c_f_pointer(ret_c_ptr, f_ptr, [100])
                               
                z = f_ptr

        end subroutine
        
        subroutine pass_by_reference(z)

                use iso_c_binding

                integer, intent(out)            :: z
                
                ! Interface to C function
                interface                        
                        subroutine c_pass_by_reference(a) bind(C, name="pass_by_reference")
                                import
                                integer(c_int) :: a
                        end subroutine
                end interface

                ! Call C function                
                call c_pass_by_reference(z)

        
        end subroutine


end module

JupyterNotebookOutput

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.