Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28446 Discussions

How to call a fortran in vc++ and callback c function

yan__san-chi
Beginner
941 Views

Hi!  I create a fortran dll that it can pass an array to sum its elements and call a c function below:

module callctest
        
    use, intrinsic :: iso_c_binding
    
    implicit none
    private
    public callcfun
        
    contains    
        
    subroutine callcfun(a,b) bind(C,name = "callcfun")
    !DEC$ ATTRIBUTES DLLEXPORT :: callcfun 
        implicit none
        
        real(c_double), dimension(*):: a !receive an array from c
        type(c_funptr), value :: b ! receive a c function 
        
        real,  parameter::pi=3.14159
        integer :: i
        real :: sum = 0.0
        
        do i = 1,3          ! sum the array elements
            sum = sum+a(i)
        end do 
        write(*,*) 'total',sum
        return
    
    end subroutine callcfun
    
    end module callctest 

And c code that it call fortran with passing c function that it print words and an input integer:

#include "stdafx.h"
    #include <iostream>
    
    using namespace std;
    
    extern "C" {
    
    	void callcfun( double[], void(f)(int)); //
    
    }
    
    void sayhello(int a){
    
    
    	cout << "Hi! fortran " <<a<< endl;
    }
    int main(){
    
    	double a[] = { 10.0, 50, 3.0 };
    
    	callcfun(a, sayhello(50));
    	
    	system("pause");
    	return 0;
    
    }

But the problem in function sayhello(50). Error list shows: 

Error    1    error C2664: 'void callcfun(double [],void (__cdecl *)(int))' : cannot convert argument 2 from 'void' to 'void (__cdecl *)(int)'   

 

IntelliSense: argument of type "void" is incompatible with parameter of type "void (*)(int b)" 

 How to solve it? Thanks!

0 Kudos
8 Replies
mecej4
Honored Contributor III
941 Views

I think that there is a problem in your C code. In fact, the error message is from the C compiler. The second actual argument to "callcfun" is allowed in C only if is an expression that can be evaluated (including a pointer value). However, your "callcfun" is of type void, so its invocation does not result in a value that can be used as an argument.

It is not clear what you want to accomplish, but you have to conform to the restrictions of C in your C code and the restrictions of Fortran in your Fortran code.

0 Kudos
yan__san-chi
Beginner
941 Views

In c program, function "sayhello" print words "Hi! fortran" and an integer, I want to call sayhello function

by calling  "callcfun( array, function )" which is written by fortran.

It works when I call "callcfun( array, function )" in c program if function

like "sayhello" only print "Hi! fortran". But I add an int argument for sayhello function 

so that it print words "Hi! fortran" and an  integer argument. Function "callcfun" doesn't execute successfully.  

0 Kudos
mecej4
Honored Contributor III
941 Views

Although you did not say so in so many words, you appear to want a function reference used as an actual argument in the invocation of another function reference to be deferred. There is no provision in either Fortran or C to do such a thing. Since you claim that you can make your idea in  pure C, show us the code that does so.

More specifically, how does the C language allow you to distinguish between immediate and deferred evaluation of a function? I don't think this is allowed, but I am willing to be persuaded otherwise.

Here is an example, using the function printf from the standard C library. Given the statement

printf("sqrt(x) = %8.4f",sqrt(x));

where does the sqrt() function get called? Just before the call to printf(), or from deep within printf() itself?  

0 Kudos
jimdempseyatthecove
Honored Contributor III
941 Views

You have problems on your C++ side.

In the Fortran program you have: type(c_funptr), value :: b ! receive a c function

Which declares b as having a C function pointer (.NOT . C++ function pointer)

In the C++ program you have: void sayhello(int a){...}

Which declares sayhello as a C++ function (.NOT. C function (you forgot the extern "C" on this))

In the C++ program you issue: callcfun(a, sayhello(50));

Which "says" evaluate sayhello(50) and use the return (void) as the second argument to callcfun.

My assumption is you need to have callcfun pass:

1) a - the array pointer
2) sayhello - address of sayhello(int) function
3) 50 - the argument you intend to use within the Fortran program (unless you intend to use a Fortran generated integer)

If the above is what you need then the Fortran program needs to add the integer argument (value or reference) and then use

callcfunc(a, sayhello, 50);

Note, depending on how you work the C++ side, you may need to create a temporary function pointer in the event that C++ cannot disambiguate sayhello to be the address of the function. Sometimes use of & helps:

callcfunc(a, &sayhello, 50);

Jim Dempsey

 

 

0 Kudos
yan__san-chi
Beginner
941 Views

Hi! jimdempseyatthecove. After I think for a while, I should adjust my code. Now I call fortran subroutine "callcfun", it includes a function written in C, then in fortran, call "sayhello" that prints words and a number(double format).

My fortran code:
module callctest
    
use, intrinsic :: iso_c_binding
implicit none
interface
    subroutine cfun(x)
        use, intrinsic :: iso_c_binding
        real (kind = c_double), value :: x
    end subroutine cfun
end interface
private
public callcfun
    
contains    
    
subroutine callcfun(b) bind(C,name = "callcfun")
!DEC$ ATTRIBUTES DLLEXPORT :: callcfun
use, intrinsic :: iso_c_binding
    implicit none


    type(c_funptr), value :: b
    
    real,  parameter::pi=3.14159
    procedure(cfun), pointer :: f
  
    call c_f_procpointer(b, f)  
    call f(18.12_c_double)
    

end subroutine callcfun
end module callctest

And C code:

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
using namespace std;

extern "C" {

	void callcfun(void(*)(double));

}

void sayhello(double x){
	
	printf("Hi fortran and x is %f.\n",x);
}

int main(){




	cout << endl;
	callcfun(&sayhello);

	system("pause");
	return 0;

}

But the output is:

Hi fortran and x is -92559592302995111000000000000000000000000000000000000000000000.000000.

Where is my problem? Though I set double format argument in both side, I still cannot find bug.

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
941 Views

The problem is (I think) is in the Fortran listing in #6 you declare the interface cfun as being a Fortran function (not C function) taking a "value" of kind(c_double).

Be aware that "value" Fortran functions create a (stack) temporary, copy the value to there, then pass the reference to the value, older Fortran compilers passed an actual value but this precluded them from having an optional value. The newer method protects the content of the original value in the event you somehow manage to write to it.

You need to declare the interface to cfun with BIND(C,...) such that the "value" passes a value on the stack as opposed to reference to value holder. (Note, pass on stack may be supplemented with pass in register)

Jim Dempsey

0 Kudos
IanH
Honored Contributor II
941 Views

You are missing BIND(C) on the interface body for `cfun`.

0 Kudos
yan__san-chi
Beginner
941 Views

Right! I missed BIND(C) in cfun(x). Thanks for you guys help! 

0 Kudos
Reply