how to call c from fortran?

Dear All,

I want to call c subroutine from fortran, my idea is to create a c project as library file, and add this project into my fortran project then link c part as external lib file, but I failed with the following error:
error LNK2019: unresolved external symbol _F2C_FUNC@32 referenced in function MAIN.

I will attach the code below, hopefully someone can give me some clues to work it out.

      program prog
      implicit none
 		external f2c_func  

		logical*1 bool1
		character letter1
		integer*4 numint1, numint2
		real numfloat1
		double precision numdoub1
		integer*2 numshor1

		call f2c_func(bool1, letter1, numint1, numint2, numfloat1, numdoub1, numshor1)
		write( *, "(L2, A2, I5, I5, F6.1, F6.1, I5)") bool1, letter1, numint1, numint2, numfloat1,numdoub1, numshor1

      end program prog

[code]	#define TRUE 0xff
	#define FALSE 0
	void f2c_func_( bool1, letter1, numint1, numint2, numfloat1, numdoub1, numshor1)
	char *bool1, *letter1;
	int *numint1, *numint2;
	float *numfloat1;
	double *numdoub1;
	short *numshor1;
	{
	*bool1 = TRUE; *letter1 = 'v';
	*numint1 = 11; *numint2 = -44;
	*numfloat1 = 39.6 ;
	*numdoub1 = 39.2;
	*numshor1 = 981;
	}

Since the c part is library and added into my fortran main program, I link it in the following way:
Property--Linker--input--additional dependencies-- ..\Release\f2c_func.lib.

BTW, I am working on my program by Visual Studio 2008 with a 32 bit windows xp machine.

Any comments and advice will be greatly appreciated,

Zhangping

Hi Zhangping,

You have the right idea but just need to get the Win32 calling convention to match between the C and Fortran code. Please take a look at Chapter 15 of the PGI Visual Fortran User’s Guide (http://www.pgroup.com/doc/pvfug.pdf) for detail. In particular, review the section titled “Win32 Calling Conventions”.

Hope this helps,
Mat

Hey Mat,

I went through the chapter and changed the code according to some calling conventions, but the code still has the same error, right now my code is;

      program prog
      implicit none
		
		integer*4 numint1, numint2
		real numfloat1
			
		!DEC$ ATTRIBUTES C :: f2c_func			
		call _f2c_func (%val(numint1), %val(numint2), %val(numfloat1))
		
		write( *, "(I5, I5, F6.1)") numint1, numint2, numfloat1
		
      end program prog

And the c part is;

	#define TRUE 0xff
	#define FALSE 0

	void f2c_func_(numint1, numint2, numfloat1)

	int *numint1, *numint2;
	float *numfloat1;
	{
	*numint1 = 11; *numint2 = -44;
	*numfloat1 = 39.6 ;
	}

they are simplified for test, I wonder if you can do me a favor to test on your machine, or do you have some other advice?

Thanks a lot,

Zhangping

You have to follow the mixed-language calling conventions consistently and correctly.

The subroutine name in the DEC$ directive and the CALL statement do not match.

You call the subroutine with %val applied to each argument, but the subroutine is declared as having addresses passed to it.

The 32-bit PGI Fortran compiler defaults to the STDCALL calling convention. Use the -Munix option to use the CDECL convention.

Alternatively, use the ISO-C interoperability features of Fortran 2003.

I am sorry for those simple mistakes, I am new to mixing programming, and I appreciate your advice.

If I want to follow STDCALL conventions, based on my understanding, the code will be;

      program prog
      implicit none
		
		integer*4 numint1, numint2
		real numfloat1
			
		!DEC$ ATTRIBUTES STDCALL :: f2c_func			
		call f2c_func@12 (%addr(numint1), %addr(numint2), %addr(numfloat1))
		
		write( *, "(I5, I5, F6.1)") numint1, numint2, numfloat1
		
      end program prog



	#define TRUE 0xff
	#define FALSE 0

	void f2c_func_(numint1, numint2, numfloat1)

	int *numint1, *numint2;
	float *numfloat1;
	{
	*numint1 = 11; *numint2 = -44;
	*numfloat1 = 39.6 ;
	}

But I still have error like this;
warning W0025 : Illegal character (@) - ignored
error S0034 : Syntax error at or near integer constant 12

Can you give me some more help, thanks.

Zhangping

Hi Zhangping,

mecej4 was meaning the symbol names needed to match when using the C calling convention. i.e. “f2c_func_”. The “@12” is the size of your arguments, not the symbol name.

Though, I see where you got confused. The section in the User’s Guide which has the “_work@12(%addr(…” line, this is pseudo-code illustrating how the symbol will be decorated, not the syntax you need to use. Here’s an example of the correct syntax with STDCALL.

PGI$ cat testf.f90
      program prog
      implicit none

      integer*4 numint1, numint2
      real numfloat1

!DEC$ ATTRIBUTES STDCALL, REFERENCE :: f2c_func
      call f2c_func (numint1, numint2, numfloat1)
      write( *, "(I5, I5, F6.1)") numint1, numint2, numfloat1

      end program prog
PGI$ cat test.c
   #define TRUE 0xff
   #define FALSE 0

   void f2c_func_(numint1, numint2, numfloat1)

   int *numint1, *numint2;
   float *numfloat1;
   {
   *numint1 = 11;
   *numint2 = -44;
   *numfloat1 = 39.6 ;
   }

PGI$ cl -c test.c
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.30319.01 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

test.c
PGI$ pgf90 testf.f90 test.obj  -g
testf.f90:
PGI$ testf.exe
   11  -44  39.6

Now with “C”:

PGI$ cat testf.f90
      program prog
      implicit none

      integer*4 numint1, numint2
      real numfloat1

!DEC$ ATTRIBUTES C, REFERENCE :: f2c_func_
      call f2c_func_ (numint1, numint2, numfloat1)
      write( *, "(I5, I5, F6.1)") numint1, numint2, numfloat1

      end program prog
PGI$ pgf90 testf.f90 test.obj
testf.f90:
PGI$ testf.exe
   11  -44  39.6

Now using F2003 standard ISO C Binding:

PGI$ cat testf2.f90
      program prog
      use iso_c_binding
      implicit none

      interface
        subroutine f2c_func (a,b,c) BIND(C, name='f2c_func_')
          use iso_c_binding
          integer(C_INT) :: a,b
          real(C_FLOAT) :: c
        end subroutine f2c_func
      end interface

      integer*4 numint1, numint2
      real numfloat1

      call f2c_func (numint1, numint2, numfloat1)
      write( *, "(I5, I5, F6.1)") numint1, numint2, numfloat1

      end program prog
PGI$ pgf90 testf2.f90 test.obj
testf2.f90:
PGI$ testf2.exe
   11  -44  39.6

Hope this helps,
Mat

Hey Mat,

Thanks very much for your detailed explanation, the second one works in my machine, this confirms that fortran and c can communicate with each other. Right now I am working on my problem by modifying this code.

We have program written by fortran, and we use c to output results into files, previously, we have our code work well with Intel Visual Fortran, but after we switch to PGI, we found the code did not work any more, so I try to fix it.

Here is a simplified case of our application, array a and b are results from fortran program, we want to pass them with the output file defined name to the c function or subroutine, and then c code create the file and write the array into the file. Honestly, I do not have experience with c, and the following case is written based on my understanding and google search, the compilation shows some errors and warnings, can you give me some more help to work it out?

      program prog
      implicit none
      
		real*4 a(10)
		integer*4 b(10)
		integer i,n
		character*256, name
		
		name='file_output.dat'
		n=10	
			
		do i=1,n	
			a(i)=(i*10-5)/2
			b(i)=i+100
		enddo
				
		!DEC$ ATTRIBUTES C,REFERENCE :: f2c_func_				
		call f2c_func_(n, name, a, b)
		
      end program prog



	
	#include <stdio>
	void f2c_func_(n, name, a, b)
	float *a;
	int *b;
	int n;
	char * name;

	FILE * fp;
	{		
		fp = fopen(name,'w');
		for(int i = 1; i <= n; i++)
			{
			fwrite(a , sizeof(a) ,1 ,  fp );
			fwrite(b , sizeof(b) ,1 ,  fp );
			}
		fclose(fp);
	}

The errors and warnings are;
.\f2c_func.c(9) : error C2085: ‘fp’ : not in formal parameter list
.\f2c_func.c(11) : error C2065: ‘fp’ : undeclared identifier
.\f2c_func.c(11) : warning C4047: ‘function’ : ‘const char *’ differs in levels of indirection from ‘int’
.\f2c_func.c(11) : warning C4024: ‘fopen’ : different types for formal and actual parameter 2
.\f2c_func.c(11) : warning C4047: ‘=’ : ‘int’ differs in levels of indirection from ‘FILE *’
.\f2c_func.c(12) : error C2143: syntax error : missing ‘;’ before ‘type’
.\f2c_func.c(12) : error C2143: syntax error : missing ‘;’ before ‘type’
.\f2c_func.c(12) : error C2143: syntax error : missing ‘)’ before ‘type’
.\f2c_func.c(12) : error C2143: syntax error : missing ‘;’ before ‘type’
.\f2c_func.c(12) : error C2065: ‘i’ : undeclared identifier
.\f2c_func.c(12) : warning C4552: ‘<=’ : operator has no effect; expected operator with side-effect
.\f2c_func.c(12) : error C2065: ‘i’ : undeclared identifier
.\f2c_func.c(12) : error C2059: syntax error : ‘)’
.\f2c_func.c(13) : error C2143: syntax error : missing ‘;’ before ‘{’
.\f2c_func.c(14) : error C2065: ‘fp’ : undeclared identifier
.\f2c_func.c(14) : warning C4047: ‘function’ : ‘FILE *’ differs in levels of indirection from ‘int’
.\f2c_func.c(14) : warning C4024: ‘fwrite’ : different types for formal and actual parameter 4
.\f2c_func.c(15) : error C2065: ‘fp’ : undeclared identifier
.\f2c_func.c(15) : warning C4047: ‘function’ : ‘FILE *’ differs in levels of indirection from ‘int’
.\f2c_func.c(15) : warning C4024: ‘fwrite’ : different types for formal and actual parameter 4
.\f2c_func.c(17) : error C2065: ‘fp’ : undeclared identifier
.\f2c_func.c(17) : warning C4047: ‘function’ : ‘FILE *’ differs in levels of indirection from ‘int’
.\f2c_func.c(17) : warning C4024: ‘fclose’ : different types for formal and actual parameter 1

I think the c part is OK, but it just doesn’t work, any further advice?

Thanks a lot,

Zhangping

Hi Zhangping,

You just put the declaration of fp in the wrong spot. Since it’s a local variable, it needs to be declared inside the curly brackets.

Note since you have the “REFERENCE” clause, all variables are being passed by reference, hence n needs to be defined as a pointer. Also, the !DEC$ directive needs to be up in the declaration. Next, you should append Fortran strings with a NULL terminator when passing to C since since C strings are NULL terminated. Finally, fopen expects a string for the second argument, not a character, so you need to change ‘w’ to “w”.

Here’s the changes:

PGI$ cat test1.c
   #include <stdio>
   void f2c_func_(n, name, a, b)
   float *a;
   int *b;
   int *n;
   const char * name;
   {
      FILE * fp;
      printf("Opening %s %d %d %f\n",name,*n,*b,*a);
      fp = fopen(name,"w");
      for(int i = 1; i <= *n; i++)
         {
         fwrite(a , sizeof(a) ,1 ,  fp );
         fwrite(b , sizeof(b) ,1 ,  fp );
         }
      fclose(fp);
   }

PGI$ cat test2.f90
      program prog
      implicit none

      real*4 a(10)
      integer*4 b(10)
      integer i,n
      character*256, name
!DEC$ ATTRIBUTES C,REFERENCE :: f2c_func_

      name='file_output.dat'//char(0)
      n=10

      do i=1,n
         a(i)=(i*10-5)/2
         b(i)=i+100
      enddo

      call f2c_func_(n, name, a, b)

      end program prog
PGI$ pgf90 test1.c test2.f90
test1.c:
test2.f90:
PGI$ test1.exe
Opening file_output.dat 10 101 2.000000

Here’s the code again using ISO_C_BINDING. Besides being more portable, you can mix pass by reference and pass by value.

PGI$ cat test1a.c
   #include <stdio>
   void f2c_func_(n, name, a, b)
   float *a;
   int *b;
   int n;
   const char * name;
   {
      FILE * fp;
      printf("Opening %s %d %d %f\n",name,n,*b,*a);
      fp = fopen(name,"w");
      for(int i = 1; i <= n; i++)
         {
         fwrite(a , sizeof(a) ,1 ,  fp );
         fwrite(b , sizeof(b) ,1 ,  fp );
         }
      fclose(fp);
   }

PGI$ cat test2a.f90
      program prog
      use iso_c_binding
      implicit none

      real*4 a(10)
      integer*4 b(10)
      integer i,n
      character*256, name

      interface
        subroutine f2c_func (n,name,a,b) BIND(C,name='f2c_func_')
           use iso_c_binding
           integer(C_INT), value :: n
           character*256 ::  name
           real(C_FLOAT), dimension(:) :: a
           integer(C_INT), dimension(:) :: b
        end subroutine f2c_func
      end interface

      name='file_output.dat'//char(0)
      n=10

      do i=1,n
         a(i)=(i*10-5)/2
         b(i)=i+100
      enddo

      call f2c_func(n, name, a, b)

      end program prog
PGI$ pgf90 test1a.c test2a.f90
test1a.c:
test2a.f90:
PGI$ test1a.exe
Opening file_output.dat 10 101 2.000000
  • Mat