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

How to pass data/pointer between C/C++ and Fortran code?

Zhanghong_T_
Novice
3,367 Views

Dear all,

I am trying to compile some code that can work on Linux system to Windows 7 64bit + VS2013 + Intel Fortran. I have successfully compiled the code but the data are not passed correctly. For example, the following are Fortran code and C code:

Fortran:

      pointer (pptr,prolog), (eptr,epilog)
      integer prolog(*), epilog(*)
 
      include 'mm2000.h'
 
      integer array_bytes, block_bytes
      integer index, j, iptrsize
...
      pptr = util_malloc(block_bytes)

C:

void* UTIL_MALLOC(long long nbytes)
{ /* util_malloc  */
	void* iptout = NULL;

  if(!(iptout= malloc(nbytes))) {
    printf("UTIL_MALLOC: Out of memory, malloc return: %p \n",iptout);
    printf("   Requested value: %f = %4d bit unsigned int \n",(float)nbytes,sizeof(nbytes));
    return NULL;

  } /* if */

  return iptout;
} /* util_malloc */

 

Now the data are pass from Fortran to C, In Fortran side block_bytes=416, but in C the nbytes=1625144.

Another C example:

int_ptrsize UTIL_SUM_ADDR(long long* a, long long* b)
{ /* util_sum_addr */
  return *a + *b;
} /* util_sum_addr */

void* UTIL_REALLOC(void** iptr, long long* nwords)
{ /* util_realloc */
  void* iptout = NULL;
  long long nbytes = 0;

  nbytes = (*nwords);

  if (!(iptout= realloc(*iptr,nbytes))) {
    printf("UTIL_REALLOC: Reallocation error - aborting\n");
    printf("   Requested value: %f = %4d bit unsigned int \n",(float)nbytes,sizeof(nbytes));

    /*  exit(1); */
    return NULL;

  } /* if */

  return iptout;
} /* util_realloc */

 

In Fortran code, the function is as follows:

      pointer (pptr,prolog), (eptr,epilog)
      integer prolog(*), epilog(*)
       integer array_bytes, block_bytes, iptrsize
...
      pptr = util_realloc(pptr, block_bytes)
      eptr = util_sum_addr(aptr, array_bytes)

 

 

How to modify C/Fortran code to let the function be executed correctly?

Thanks,

Tang Laoya

0 Kudos
29 Replies
TimP
Honored Contributor III
2,802 Views
A c pointer is equivalent to what is referred to in ifort as integer pointer and in other Fortran compilers as Cray pointer. Your ifort is too old to support the f2008 portability features but you do need to look up earlier facilities like bind(c) and c_f_pointer. The latter intrinsic makes a Fortran pointer from the properly sized integer pointer.
0 Kudos
JVanB
Valued Contributor II
2,802 Views

I betcha mm2000.h has an interface block that looks like this:

interface
   function UTIL_MALLOC(nbytes)
      use ISO_C_BINDING
      implicit none
      integer(C_LONG_LONG), value :: nbytes
      integer(C_INTPTR_T) UTIL_MALLOC
   end function UTIL_MALLOC
end interface

gfortran, for example, would pass nbytes by value in this context, but current ifort does not. You need to specify the BIND property for the function if you want ifort to pass truly by value:

interface
   function UTIL_MALLOC(nbytes) bind(C,name='UTIL_MALLOC')
      use ISO_C_BINDING
      implicit none
      integer(C_LONG_LONG), value :: nbytes
      integer(C_INTPTR_T) UTIL_MALLOC
   end function UTIL_MALLOC
end interface

See if that helps. Also, I would recommend getting rid of the Cray pointers and replacing them with type(C_PTR) to communicate with C and Fortran pointers to dereference them on the Fortran side. If all you are trying to achieve is dynamic memory allocation, then you might be able to just do away with the C function to do that entirely and use allocatables in Fortran.

 

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Repeat Offender,

Thank you very much for your kindly reply. I will study the interface you provided. Could you also give me some hints on how to write interface for the function UTIL_SUM_ADDR?

Yes, the Fortran code is just from mm2000.F, and the C code is from mmsc.c, I guess that the author is use the C++ to manage memory for Fortran code. I am trying to modify the code to let it work in Windows and Intel Fortran.

Thanks,

Tang Laoya

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Tim,

Thank you very much for your kindly reply. Do you mean that if the compiler support Fortran 2008, the interface would be much simpiler? I noticed that IVF 2018 support Fortran 2008, I will try it latter.

Another question: if I build the above code by IVF 2018 as a static library, Can I link the library to the executabe file compiled by IVF 2016?

Thanks,

Tang Laoya

0 Kudos
TimP
Honored Contributor III
2,802 Views
No, f2008 provides access to the details of Fortran pointer, but that may not simplify your task.
0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views

When Tim says "f2008" here he really means Fortran 2018 (formerly Fortran 2015), or the "Additional Interoperability of Fortran with C" Technical Specification 29113, which Intel Fortran 16 and later supports. I agree with Tim that these features, while quite powerful, are far beyond what you need here and add complexity to the C implementation.

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

Thank you very much for your kindly reply. It seems that I still need to provide interface for these C functions. How to write interface for this kind of function?

void* UTIL_REALLOC(void** iptr, long long* nwords);

Thanks,

Tang Laoya

0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views
interface
  use, intrinsic :: iso_c_binding
  subroutine UTL_REALLOC (iptr, nwords) bind(C,NAME="UTL_REALLOC")
    type(C_PTR), intent(INOUT) :: iptr
    integer(C_LONG), intent(INOUT) :: nwords
  end subroutine UTL_REALLOC
end interface

iptr is a pointer passed by reference - presumably it gets updated by the function. nwords is also passed by reference but I don't know if the function changes it.

You can then use C_F_POINTER from module ISO_C_BINDING to convert iptr to a Fortran pointer. (And use C_LOC to go the other way.)

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

Thank you very much for your kindly reply. I am trying to rewrite the code. The original Fortran code is as follows:

      pointer (aptr,char_array)
      character*32 char_array(*)
      pointer (pptr,prolog), (eptr,epilog)
      integer prolog(*), epilog(*)
...
      aptr =  util_sum_addr(pptr, PROLOG_BYTES)
      eptr =  util_sum_addr(aptr, array_bytes)

 

The original C code is as follows:

long long UTIL_SUM_ADDR(long long* a, long long* b)
{ /* util_sum_addr */
  return *a + *b;
} /* util_sum_addr */

 

I added the following interface in the Fortran code:

      interface
	  function UTIL_SUM_ADDR(a,b) bind(C,name='UTIL_SUM_ADDR')
	  use ISO_C_BINDING
	  implicit none
	  integer(C_LONG), intent(INOUT) :: a,b
	  integer(C_LONG_LONG) :: UTIL_SUM_ADDR
	  end function UTIL_SUM_ADDR
      end interface      

 

When compiling, the following error displayed:

Error    4     error #6633: The type of the actual argument differs from the type of the dummy argument.   [APTR]    
Error    2     error #6633: The type of the actual argument differs from the type of the dummy argument.   [PPTR]   
Error    3     error #6638: An actual argument is an expression or constant; this is not valid since the associated dummy argument has the explicit INTENT(OUT) or INTENT(INOUT) attribute.

 

What else should I modify in the Fortran code?

Thanks,

Tang Laoya

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views

You don't need pointers at all in this latest example - just pass the variables by reference like you would to a Fortran routine. Your interface is wrong, though, in that you declare the arguments C_LONG instead of C_LONG_LONG. Also it appears you are passing a constant where you declared the dummy argument INTENT(INOUT). In this case you may want to omit INTENT.

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

Thank you very much for your kindly reply. As you suggested, after removed INTENT(INOUT), the compiling was successed, but the following warning displayed:

warning #6075: The data type of the actual argument does not match the definition.   [ARRAY_BYTES]

I don't know whether data can be passed correctly.

What do you mean "You don't need pointers at all in this latest example"? For example, the variable "aptr", which pointed to a character, how to declare that variable without pointers?

Thanks,

Tang Laoya

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

In the Fortran code, the code is as follows:

      pointer (pptr,prolog), (eptr,epilog)
      integer prolog(*), epilog(*)
...
      pptr = util_realloc(pptr, block_bytes)

The C code is as follows:

void* UTIL_REALLOC(void** iptr, long long* nwords)
{ /* util_realloc */
  void* iptout = NULL;
  long long nbytes = 0;

  nbytes = (*nwords);

  if (!(iptout= realloc(*iptr,nbytes))) {
    printf("UTIL_REALLOC: Reallocation error - aborting\n");
    printf("   Requested value: %f = %4d bit unsigned int \n",(float)nbytes,sizeof(nbytes));

    /*  exit(1); */
    return NULL;

  } /* if */

  return iptout;
} /* util_realloc */

I added the interface for 'UTIL_REALLOC' as you suggested:

	  function UTIL_REALLOC(iptr,nwords) bind(C,name='UTIL_REALLOC')
	  use ISO_C_BINDING
	  implicit none
	  type(C_PTR), intent(INOUT) :: iptr
	  integer(C_LONG_LONG) :: nwords
	  integer(C_INTPTR_T) UTIL_REALLOC
	  end function UTIL_REALLOC

 

The following compiling error displayed:

error #6633: The type of the actual argument differs from the type of the dummy argument.   [PPTR]

What should I modify in Fortran code to let it work?

Thanks,

Tang Laoya

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views

You are not showing all the declarations nor the part of the code that causes the error.

My comment that you didn't need the pointers for UTIL_SUM_ADDR was that this function simply takes two "long long" variables by reference and adds them. From the Fortran perspective, this is just like passing integer variables normally. You didn't show all the code (nor all the declarations), so I can't be sure what the purpose of this function really is. If you had other reasons to pass pointers, then you could declare the arguments to UTIL_SUM_ADDR to be integer(C_INTPTR_T) and also include the VALUE attribute. This would get the right level of indirection on the C side and would allow you to keep using integer pointers.

Now that I see more of your use of UTIL_REALLOC, I'd suggest changing the iptr dummy argument in the Fortran interface from type(C_PTR) to integer(C_INTPTR_T). You shouldn't need other changes.

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

I am sorry that I have not shown all related code. Now I attached the related source files, could you please help me to take a look at it? To let it work and to keep original code, I used macro 'ivfwin'.

 

Thanks,

Tang Laoya

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

In fact, I am trying to build LaGriT (https://github.com/lanl/LaGriT) in Visual Studio 2013 + IVF 2016 environment. I created two projects-one is C++ static library and another is Fortran console project. The C++ static library include all c code in \src and \ lg_util/src folders and the Fortran console project include all Fortran code, and when link to executable file the C++ static library is included. By simply build these two projects, hundreds of errors will display. I modified many Fortran files to let the build succeed.

But now there are some data pass problems between C and Fortran code. Could you please help me to take a look at it?

Thanks,

Tang Laoya

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views

Sorry, but I don't have the time to analyze your large program in such detail.

0 Kudos
Zhanghong_T_
Novice
2,802 Views

Dear Steve,

I am sorry for late reply. I simplified the original project to a small one and attached the whole simplified solution under VS2013+Intel Parallel Studio 2016. The C project generate a static library which would be linked by the Fortran project. The following error displayed if I build the solution:

Error    4     error #6633: The type of the actual argument differs from the type of the dummy argument.   [ADAPTR]    .\testmem.f90    75    
Error    2     error #6633: The type of the actual argument differs from the type of the dummy argument.   [ADNPTR]    .\testmem.f90    74    
Error    6     error #6633: The type of the actual argument differs from the type of the dummy argument.   [LPTR]    .\testmem.f90    76    

Could you please help me to take a look at the problem?

In addition: after commented out the lines that 'util_realloc' was called, the following errors displayed:

Error    1     error #11018: Cannot open mem_c.lib    ipo    
Error    2     fatal error LNK1181: cannot open input file 'mem_c.lib'    LINK    

The build only success when I put a copy of mem_c.lib to '.\testmem' directory (that is to say, two mem_c.lib should be existed in '.\Debug' and '.\testmem' at the same time). Could you please tell me what lead to this problem?

Thanks,

Tang Laoya

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,802 Views

The Fortran error messages are because you have the iptr argument in the interface to UTL_REALLOC declared as type(C_PTR), but it should instead be integer(C_INTPTR_T) as you are passing an integer pointer.

Another error is that the nwords argument is declared as C_LONG_LONG) but in the program you pass a default integer argument. If you declare max_ad as integer(C_LONG_LONG) it will fix that. (You will need to add "use, intrinsic :: ISO_C_BINDING" to the main program.) I am concerned also about the declaration of ad_addr as the comment says "pointer-sized integer" but it is declared as default integer. You don't use that in this smaller example so I'm not sure how this is used.

When I build the solution with these changes, the compile errors go away (I get a conversion warning from C++ that I am ignoring, but I then get a link error for the printf reference in the C code. I'm not a C/C++ expert - doing some web searching tells me that Microsoft changed the way printf was implemented in VS2015 and later (I have 2017), but as best as I can tell you're properly including stdio.h so I don't know what else is the problem. You could ask in a Microsoft C++ forum for help with that. I can see that a workaround is to add legacy_stdio_definitions.lib to "Linker > Additional Dependencies" in the Fortran project - that allows the program to link successfully.

I suspect that the IPO error you're seeing for mem_c.lib is because you have it listed as an additional dependency in the Fortran project. Since the C++ project is a dependent of the Fortran project, mem_c.lib gets linked in automatically. Since you don't tell the linker where to look for additional libraries, it can't find it. Just delete the mem_c.lib from there and let the build system take care of it through project dependencies. (Note that while this automatic linking works for C++ static libraries referenced from Fortran, Microsot changes prevent it from working for DLL libraries as well as any non-C++ libraries referenced from the C++ project.)

0 Kudos
FortranFan
Honored Contributor II
2,802 Views

Zhanghong T. wrote:

.. Could you please help me to take a look at the problem? .. 

@Tang Laoya,

What exactly you're trying to do!?

It's very surprising no one has thus far pointed out to you assumed-size array declarations (e.g., character*32 ad_name(2,*)) are only allowed as dummy arguments whereas you are trying to declare them as such for a local variable as in your testmem program.

It would also appear you're trying to write your own wrapper routines around standard C library functions such as malloc, realloc, etc. or trying to use someone else's wrappers for the same.  Note you don't need to do such a thing, you can directly use them from Fortran.  I suggest if you are interested in learning better about Fortran, you review sources referenced in this blog by Steve Lionel: https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world

In the meantime, note you can do the following:

mem-c.c file: a simple file that shows a C function to printf some memory:

#include <stdio.h>

void Cout(void *str) {
   printf("%s\n", (char *)str);
}

Fortran module defining interface to C stdlib function(s) and the above one:

module mem2000

   use, intrinsic :: iso_c_binding, only : c_char, c_ptr, c_size_t

   implicit none

   private

   interface

      function realloc( ptr, new_size ) result(ret) bind(C,name="realloc")
      ! Defined in C header <stdlib.h>
      ! void *realloc( void *ptr, size_t new_size );

         import :: c_ptr, c_size_t
         
         implicit none
         
         ! Argument list
         type(c_ptr), intent(in), value :: ptr
         integer(c_size_t), intent(in), value :: new_size
         ! Function result
         type(c_ptr) :: ret
         
      end function realloc

      subroutine Cout( vptr ) bind(C, name="Cout")
      
         import :: c_ptr
         
         implicit none
         
         ! Argument list
         type(c_ptr), intent(in), value :: vptr
         
      end subroutine Cout
      
      subroutine free( vptr ) bind(C, name="free")
      ! Defined in header <stdlib.h>
      ! void free( void* ptr );
      
         import :: c_ptr
         
         implicit none
         
         ! Argument list
         type(c_ptr), intent(in), value :: vptr
         
      end subroutine free
      
   end interface

   public :: realloc
   public :: Cout
   public :: free
   
end module mem2000

And a main program in Fortran: 

!****************************************************************************
!
!  PROGRAM: testmem
!
!  PURPOSE:  Entry point for the console application.
!
!****************************************************************************
program testmem

   use, intrinsic :: iso_c_binding, only : c_size_t, c_char, c_null_char, c_ptr, c_null_ptr,        &
                                           c_f_pointer
   use mem2000, only : realloc, Cout, free

   implicit none

   integer(c_size_t), parameter :: NUM_AD_START = 1
   integer(c_size_t), parameter :: SIZE_1 = 2
   integer(c_size_t), parameter :: BYTES_PER_CHAR = 32

   type(c_ptr) :: adnptr
   integer(c_size_t) :: nbytes
   character(kind=c_char,len=BYTES_PER_CHAR), pointer :: ad_name(:,:)

   adnptr = c_null_ptr
   nbytes = NUM_AD_START*SIZE_1*BYTES_PER_CHAR
   adnptr = realloc(adnptr, nbytes) ! Allocate the C pointer
   
   ! Setup a Fortran pointer to point to the C pointer
   call c_f_pointer( cptr=adnptr, fptr=ad_name, shape=[SIZE_1, NUM_AD_START] )
   
   ! Load some data into allocated memory
   ad_name(1,1) = repeat(string=c_char_"x", ncopies=BYTES_PER_CHAR-1) // new_line(c_char_"")
   ad_name(2,1) = c_char_"Hello World! Testing char *" // c_null_char
   
   ! Output the memory using a C function
   call Cout( adnptr )
   
   ! Nullify the Fortran pointer
   ad_name => null()
   
   call free( adnptr ) ! Free the C pointer

   stop

end program testmem

If you manage to compile and link the above either from a suitable command prompt or using a Visual Studio solution, say with Fortran console and C static library projects, you should get output along the following lines:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Hello World! Testing char *

Attached find a zip file with an example Visual Studio solution with a Fortran console and C static library projects.

0 Kudos
FortranFan
Honored Contributor II
2,061 Views

@Tang Laoya,

Also note given the dynamic memory allocation facilities in Fortran, which with the ALLOCATABLE attribute prove superior to facilities in other compiler-based languages, it's unclear why you would even want to use C functions of malloc/realloc (or your UTIL_REALLOC, etc.)  in your Fortran code:

https://software.intel.com/en-us/fortran-compiler-18.0-developer-guide-and-reference-allocatable

0 Kudos
Reply