Adrian
Adrian

Reputation: 335

Problems with passing data between C++ and F90

Update: I changed psubstuff into a c_ptr as recommended and used a c_f_pointer to access bstuff. This was successful. However I still need a good t_stuff structure with the second member as a t_bstuff (for further code in the real world), so I created a copy of the t_stuff (the bind(c) version I called t_stuff_c), and am trying to copy the contents of the t_stuff_c structure into t_stuff. But I am getting a compilation error:

error #6285: There is no matching specific subroutine for this generic subroutine call. [C_F_POINTER]


I have a situation where a C++ main program is calling functions in a Fortran DLL. This comes from a very large project, so I have trimmed it down into a simple example which reproduces the problem I am having, which is:

The main C++ program creates a variable "stuff" of type "tstuff". "tstuff" contains just one element "substuff" which is of type "void*" (it has to be this way as in practice substuff is extremely large and complex on the F90 side and I can't and indeed don't need to reproduce it on the C++ side)

It then calls crfl() in the Fortran code which creates "substuff" on the Fortran side.

Then it calls the function stfl in the Fortran code to send the character "TEST" into the member substuff%clb%corf on the Fortran side. I step through the debugger in the Fortran code and all is good up to this point and "TEST" does indeed appear in the variable on the Fortran side.

Now it goes strange. The C++ code then calls the Fortran routine test2 passing the variable stuff. When I step into the F90 code, stuff%substuff%clb%corf contains garbage. The pointer to stuff is correct in the Fortran code as the text "ACF-C" is in the name variable. But substuff is garbage. It is important that the variable stuff%substuff is correct at this point as this variable is used further in the real code.

C Code:

#include <iostream>
#include <cstddef>
#include <vector>

using namespace std;

struct t_stuff {
   char  name[256];
   void *substuff;
};
//Fortran subroutine definitions
extern "C" {
   void test2(t_stuff *stuff);
}

extern "C" 
{
   void * crfl();
   int    stfl(void * cstuff, char * name);
}

int main()
{
    int ierr;
    t_stuff *stuff;

    stuff = new t_stuff;
    strcpy_s((*stuff).name, sizeof((*stuff).name), "ACF-C");
    (*stuff).substuff = crfl();
    ierr = stfl((*stuff).substuff, "TEST");
    test2(stuff);
}

Fortran code:

module ftncode_mod use, intrinsic :: iso_c_binding implicit none

   type, public :: t_clb
     character(8) :: corf
   end type

   type, public :: t_bstuff
     type (t_clb) :: clb
   end type t_bstuff

   type, public, bind(C) :: t_stuff_c
    character(1) :: name(256)
    type (c_ptr) :: psubstuff
   end type t_stuff_c

   type, public :: t_stuff
     character       :: name(256) = ' '
     type (t_bstuff) :: psubstuff
   end type t_stuff

   contains

      subroutine c2fstr(cptr,fstr)
        type(c_ptr), value, intent(in ) :: cptr
        character(*),       intent(out) :: fstr
        character(256),     pointer     :: lfstr
        integer                         :: id
        call c_f_pointer(cptr,lfstr)
        id = index(lfstr,c_null_char)-1
        if(id.le.0) id=len(lfstr)
        fstr = lfstr(1:id)
      end subroutine

      function crfl() result(cp) bind(C)
      !DEC$ ATTRIBUTES DLLEXPORT :: crfl
        type(c_ptr     )          :: cp
        type(t_bstuff), pointer :: fp
        allocate(fp)
        cp = c_loc(fp)
      end function

      function stfl(cstuff,cne) result(ierr) bind(C)
      !DEC$ Attributes dllexport :: stfl
        type(c_ptr), value, intent(in)     :: cstuff
        type(c_ptr), value, intent(in)     :: cne 
        integer                            :: ierr  
        type(t_bstuff)    ,  pointer       :: fstuff
        character(24)                      :: cwk 
        ierr = 0  
        call c_f_pointer(cstuff,fstuff)
        call c2fstr(cne,cwk)
        fstuff%clb%corf = trim(cwk)
      end function

      subroutine test2(stuff) bind(C)
      !DEC$ATTRIBUTES DLLEXPORT :: test2
        use iso_c_binding
         type(t_stuff_c), target, intent(in) :: stuff
         type(t_stuff) :: fstuff
         type(t_bstuff), pointer :: bstuff
         integer :: i

         call c_f_pointer(stuff%psubstuff,bstuff)
         print *, "stuff%substuff%clb%corf: ", bstuff%clb%corf  ! correct
 ! now need to populate the derived Fortran structure, fstuff
         do i = 1, 256
           if(stuff%name(i) == c_null_char) exit
           fstuff%name(i:i) = stuff%name(i)
         enddo
 ! and the pointer
         call c_f_pointer(stuff,fstuff)
         print *, "fstuff%substuff%clb%corf: ", fstuff%psubstuff%clb%corf
      end subroutine test2
end module

Upvotes: 0

Views: 208

Answers (0)

Related Questions