- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have a situation where a C++ main program is calling functions in a F90 DLL. This comes from a very large project, so I have trimmed it down 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 do reproduce it on the C++ side)
It then calls crfl() on the F90 side which populates "substuff" on the F90 side.
Then it calls the function stfl to send the character "TEST" into the member substuff%clb%corf on the F90 side. I step through the debugger and all is good up to this point and "TEST" does indeed appear in the variable on the F90 side.
Now it goes strange. The C++ code then calls the F90 routine test2 passing the variable stuff. When I step into the F90 code, stuff%substuff%clb%corf contains garbage.
It won't let me attach the CPP file for some reason, so I list it below:
#include <iostream>
#include <cstddef>
#include <vector>
using namespace std;
struct t_stuff {
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;
(*stuff).substuff = crfl();
ierr = stfl((*stuff).substuff, "TEST");
test2(stuff);
}
Fortran code is attached.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@ferrad1 ,
First, please try to refer to the language as Fortran and not F90. You will note a F90 reference is meaningless, especially as you are using features from Fortran 2003 standard revision.
Then please note the following:
- As you have hinted, there is no interoperability between C++ and Fortran and therefore, toward the interoperation your C++ code will be safer enclosing all the constructs (and instructions) within the extern C clause,
- An opaque pointer such as `void *` under extern C only interoperates with `type(c_ptr)` on the Fortran side.
Given above, give this a try:
#include <iostream>
#include <cstddef>
#include <vector>
using namespace std;
//Fortran subroutine definitions
extern "C" {
struct t_stuff {
void *substuff;
};
void test2(t_stuff *stuff);
void * crfl();
int stfl(void *, char *);
}
int main()
{
int ierr;
t_stuff *stuff;
stuff = new t_stuff;
(*stuff).substuff = crfl();
ierr = stfl((*stuff).substuff, "TEST");
test2(stuff);
}
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
type(c_ptr) :: psubstuff
end type t_stuff
contains
subroutine c2fstr(cptr,fstr) !<-- eventually replace this with C_F_STRPOINTER from Fortran 2023 standard
type(c_ptr), value, intent(in ) :: cptr
character(kind=c_char,len=*), intent(out) :: fstr !<-- Technically this works with c_char KIND only
character(kind=c_char,len=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), target, intent(in) :: stuff
type(t_bstuff), pointer :: fstuff
call c_f_pointer(stuff%psubstuff,fstuff)
print *, "stuff%substuff%clb%corf: ", fstuff%clb%corf
fstuff => null() !<-- Not a must, just good practice
end subroutine test2
end module
- Build and execute on Windows with Intel Fortran and Microsoft C++ toolchain:
C:\temp>ifort /c /standard-semantics ftncode.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.8.0 Build 20221119_000000
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.
C:\temp>cl /c /W3 /EHsc c++.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31937 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
c++.cpp
C:\temp>link c++.obj ftncode.obj /subsystem:console /out:c++.exe
Microsoft (R) Incremental Linker Version 14.34.31937.0
Copyright (C) Microsoft Corporation. All rights reserved.
Creating library c++.lib and object c++.exp
C:\temp>c++.exe
stuff%substuff%clb%corf: TEST
Last but not the least: note the above verifies your expected result in a console test. Do such verification first because you may face issues with Intel Fortran integration with Visual Studio if that is the debugger you are using whereby depending on your version(s) and installation aspects, the debugger may fail to render the objects accurately in the Fortran subprogram instance on the call stack. That is a separate problem.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
(Reiterating on this reply!)
Thanks. Yes I'm so used to calling the Fortran code F90 due to the extension...
I have taken a look at the code changes and see the main change is the void <> c_ptr connectivity.
The main problem still exists however. In test2, I need to operate on stuff not fstuff. stuff is the structure which needs to be passed on to subsequent routines so stuff%substuff must be correct. It is still garbage.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Re: "One question I have is, by changing the type of substuff to c_ptr I have problems in the rest of the code which uses it. Is there a way to avoid this or must I make changes in all the code that uses it?"
- it points to nonconforming aspects (some would call them bugs) somewhere in your code. You can either spend the time to thoroughly review and fix your code wherever needed or try to kluge your way through such that you somehow get it to work with both the debugger and the compiler version today. With the latter, you also run the risk the code stops working in a future compiler version with stricter conformance requirements or one that does not work with any other compiler from another vendor or an open source effort out there (presently gfortran but there will be others like it).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I updated my reply later with a more specific issue:
The main problem still exists however. In test2, I need to operate on stuff not fstuff. stuff is the structure which needs to be passed on to subsequent routines so stuff%substuff must be correct. It is still garbage.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Toward the C_F_STRPOINTER option from the Fortran 2023 revision, see this comment.
For the code in your original post at least, here is a variant to consider. Note the `bind(C)` on type `t_stuff` - look into that in your actual code.
module cstring_m
use, intrinsic :: iso_c_binding, only : c_char, c_size_t, c_ptr, c_f_pointer
generic :: c_f_strpointer => c_f_strpointer_cstrptr !<-- Toward the eventual Fortran 2023 implementation
contains
subroutine c_f_strpointer_cstrptr( cstrptr, fstrptr, nchars )
! Argument list
type(c_ptr), intent(in) :: cstrptr
character(kind=c_char, len=:), pointer, intent(out) :: fstrptr
integer(c_size_t), intent(in) :: nchars
if ( nchars >= 0 ) then
block
character(kind=c_char, len=nchars), pointer :: sptr
call c_f_pointer( cstrptr, sptr )
fstrptr => sptr
sptr => null()
end block
end if
return
end subroutine
end module
module ftncode_mod
use, intrinsic :: iso_c_binding
use cstring_m, only : c_f_strpointer
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
type(c_ptr) :: psubstuff
end type t_stuff
type(t_bstuff), allocatable, target, save :: fp
contains
function crfl() result(cp) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: crfl
type(c_ptr) :: cp
if (.not. allocated(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
! Function result
integer :: ierr
! Local variables
type(t_bstuff), pointer :: fstuff
character(kind=c_char,len=:), pointer :: cwk
ierr = 0
call c_f_pointer(cstuff,fstuff)
call c_f_strpointer( cne, cwk, len(fstuff%clb%corf,kind=c_size_t) )
fstuff%clb%corf = cwk
end function
subroutine test2(stuff) bind(C)
!DEC$ATTRIBUTES DLLEXPORT :: test2
type(t_stuff), target, intent(in) :: stuff
type(t_bstuff), pointer :: fstuff
call c_f_pointer(stuff%psubstuff,fstuff)
print *, "stuff%substuff%clb%corf: ", fstuff%clb%corf
fstuff => null() !<-- Not a must, just good practice
end subroutine test2
end module
C:\temp>c++.exe
stuff%substuff%clb%corf: TEST
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Will look at it in full later, but I still see in your example:
print *, "stuff%substuff%clb%corf: ", fstuff%clb%corf
I need to see:
print *, "stuff%substuff%clb%corf: ", stuff%substuff%clb%corf
This still fails for me.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Re: "I need to see: .. stuff%substuff%clb%corf"
Ultimately that will only be feasible in a standard-conforming and portable manner if the type corresponding to object `substuff` is fully interoperable with the C companion processor. But that will also call for refactoring your code if what is shown in the original post reflects closely your actual code. Now, refactoring should help simplify the code if done properly. Your code in the original post has all the elements of hacking away at it until something works and then complaining about problems in the debugger. Keeping it simple and structured can help avoid problems in the first place.
With the code in the original post, one quick option at restructuring it to get to "I need to see: stuff%substuff%clb%corf" in the Fortran side will be like so:
module ftncode_mod
use, intrinsic :: iso_c_binding
implicit none
integer, parameter :: SLEN = 8
type, public, bind(C) :: t_clb
character(kind=c_char,len=1) :: corf(SLEN)
end type
type, public, bind(C) :: t_bstuff
type(t_clb) :: clb
end type t_bstuff
type, public, bind(C) :: t_stuff
type(t_bstuff) :: substuff
end type t_stuff
contains
function stfl(bstuff,cne) result(ierr) bind(C)
!DEC$ Attributes dllexport :: stfl
type(t_bstuff), intent(inout) :: bstuff
type(c_ptr), value, intent(in) :: cne
! Function result
integer :: ierr
! Local variables
character(kind=c_char,len=1), pointer :: cwk(:)
ierr = 0
call c_f_pointer( cne, cwk, shape=[ SLEN ] )
bstuff%clb%corf = cwk
end function
subroutine test2(stuff) bind(C)
!DEC$ATTRIBUTES DLLEXPORT :: test2
type(t_stuff), target, intent(in) :: stuff
print *, "stuff%substuff%clb%corf: ", stuff%substuff%clb%corf
end subroutine test2
end module
#include <iostream>
#include <cstddef>
#include <vector>
using namespace std;
//Fortran subroutine definitions
extern "C" {
enum SLEN { SLEN = 8 };
typedef struct {
char corf[SLEN];
} t_clb;
typedef struct {
t_clb clb;
} t_bstuff;
typedef struct {
t_bstuff substuff;
} t_stuff;
void test2(t_stuff *stuff);
int stfl(t_bstuff *, char *);
}
int main()
{
int ierr;
t_stuff *stuff;
stuff = new t_stuff;
ierr = stfl(&(*stuff).substuff, "TEST");
test2(stuff);
}
C:\temp>ifort /c /standard-semantics ftncode.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.8.0 Build 20221119_000000
Copyright (C) 1985-2022 Intel Corporation. All rights reserved.
C:\temp>cl /c /W3 /EHsc c++.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31937 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
c++.cpp
C:\temp>link c++.obj ftncode.obj /subsystem:console /out:c++.exe
Microsoft (R) Incremental Linker Version 14.34.31937.0
Copyright (C) Microsoft Corporation. All rights reserved.
Creating library c++.lib and object c++.exp
C:\temp>c++.exe
stuff%substuff%clb%corf: TEST
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This won't work as substuff has to be a void on the C++ side. This is because t_bstuff is in practice enormous and complex and can't be represented on the C++ side. substuff is only used on the Fortran side which needs to manipulate the C++ void pointer to look like a Fortran pointer to a substuff structure which the Fortran will populate.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@ferrad1 ,
Hopefully others can show how you can have your cake and eat it too .. or you can try other compiler-specific hacks and stuff and also search far and wide .. comp.lang.fortran, StackOverflow, and https://fortran-lang.discourse.group/ are other places to ask!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I guess you're saying what I'm asking is not possible?

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page