- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am attempting to pass a C++ array of pointers into a Fortran subroutine.
The C++ array is of a structure, which exactly conforms to a Fortran derived type.
So in C++ I have pArray* c_arr[DIM1][DIM2];
Then I call a Fortran sub, passing in the C++ array of pointers like fortsub(c_arr[0][0])
In Fortran I have Subroutine fortsub (fort_arr) where I need fort_arr to be a container for the array of pointers.
I currently do not know how to make this work, I have something like:
Type (type1), Dimension(2,*), Target :: fort_arr where type1 is the same type as pArray.
Basically, my problem is I don't know how to pass a double dimensioned array of C++ structure pointers into a Fortran subroutine.
I know that he concept of "array of pointers" does not exist in Fortran and the way you have to do it is have an array of derived type with a pointer component, but I cannot seem to get the data passing on the argument list correct.
Anyone have some advice, perhaps with a specific example? I would appreciate it greatly. Thanks.
Link Copied
- « Previous
-
- 1
- 2
- Next »
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As I noted in post #12, I ended up using an array of derived type with a pointer component: https://software.intel.com/en-us/forums/topic/280765. This technique worked very well, the passing of data between C++ and Fortran was seamless, and I do not think any copies were being made.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am getting more confused on this. When I run #10 code with Intel compiler 2016 in Windows 10, I get an output as shown in the attached output-win10.png, where the addresses are different (seems the data is copied on the Fortran side). When the same code is run on a different computer with intel 2016 installed onto Windows 7, the output (output-win7.png) looks good (same address, no copy is made). If #10 is doing the correct thing (without copying data in Fortran), it will be what I am in favor of as it looks much simpler. Any suggestions?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The printf() and PRINT statements are both wrong. printf() is being told to print out an address with %d format, so it only prints out the low 32 bits of the address. I don't know C all that well, but I think maybe %zd would print out all bits. Also, I don't think it's allowed to print out a type(C_PTR) with list-directed formatting, so I think you might expect more reasonable results if you printed out TRANSFER(add_foo_i,0_C_INTPTR_T). Careful examination of your outputs shows that the C printf() statements are printing out the low 32 bits of the full address seen in your Fortran outputs. I usually prefer to print out the C pointers with %p format in C and z0 format in Fortran because I am just more used to seeing these things in hex.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I ended up with something along the lines of the following code.
Note: The following is just a skeleton, but the technique worked perfectly for my real-world application.
// C++ //Declarations: // C++ structure extern "C" struct MyStructureC { int iData; }; // C++ Container for MyStructureC pointers extern "C" struct MyContainerC { MyStructureC* pData; }; // Fortran subroutine declaration extern "C" void FORTLINK MyFortranSub (MyArray* pMyArray); // In local C++ file: // Declare local array vector<MyContainerC> MyCArrayVariable; // Fill up the C++ array with pointers MyCArrayVariable.resize(1) // Size of one is simply for example // Load in your C pointer(s) here, loop as needed MyCArrayVariable[0].pData = [fill in a pointer to some desired MyStructureC variable here, i.e., *pMyStructureC]; // Now call the Fortran subroutine MyFortranSub (&MyCArrayVariable[0]);
--------------------------------------------------------------
--------------------------------------------------------------
Now for the Fortran side:
! Declarations: Type MyStructureFortran Sequence Integer :: iData End Type MyStructureFortran Type MyContainerFortran Sequence Type (MyStructureFortran), Pointer :: pStructure End Type MyContainerFortran ! Fortran Subroutine Code called from C++: Subroutine MyFortranSub (var) Type (MyContainerFortran), Dimension(*), Target :: var Integer :: var2 ! Example Usage Do i = 1, n var2 = var(i)%pStructure%iData End Do Return End Subroutine MyFortranSub
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
qi d. wrote:
.. When I run #10 code with Intel compiler 2016 in Windows 10, .. the addresses are different (seems the data is copied on the Fortran side). When the same code is run on .. Windows 7, the output .. looks good (same address, no copy is made). ..
@qi d.,
Perhaps you want to open up another thread (you can reference this thread in it) to discuss the issue where you are seeing different behavior on Windows 10 versus 7 with the same code and (apparently) the same compiler. This way the issue will get noticed better and someone might be able to help you out. You may want to post the relevant files (actual code, Visual Studio project files if you are using VS, compiler output logs, etc.), not just screen images and comments, to have better success in resolving the issue. In the new thread, you may also want to follow up on comments by Steve Lionel (Intel) as to how the " the code in #10 is faulty" is related to different behavior with the same Intel compiler on Windows 10 versus Windows 7. And the same with RO on how the comment in Message #24 of "The printf() and PRINT statements are both wrong" is pertinent to Windows 10.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@F**2,
The code in Quote #10 is faulty for the 2 reasons I pointed out. You are just going to have to learn to live with that reality.
Recall that in C, printf() is a function, not a statement like PRINT is in Fortran, thus printf() doesn't really have any context to know the effective KINDs of its actual arguments, so it must muddle through as best as possible from the information you provide in the format string. Your format for the address was %d, so what printf() is going to do is to look at the next argument and convert its low 32 bits to decimal and print that to standard output. In Window 7 it came out as 00000000001CFE08 and the low 32 bits were 001CFE08 which prints as 1900040. In Windows 10, the address was 000000C55837F828, the low 32 bits was 5837F828, so it printed out 1480063016. There may be some security feature (ASLR) active in the Windows 10 context but not in Windows 7 that makes the difference.
Fortran can use the context of the PRINT statement to figure out the size of the next data item to be printed. gfortran, with -std=f2003 rejects your Fortran code with
sub.f90:30:57: print *, " address of foo%(",i,") = ", add_foo_i 1 Error: GNU Extension: Data transfer element at (1) cannot have PRIVATE component s
But it will accept transfer(add_foo_i,0_C_INTPTR_T), provided that your USE statement is expanded appropriately.
In C, you need a format that prints out 32 bits in a 32-bit context and 64 bits in a 64-bit context. It would be nice if %ld or %lld would be the right thing, but C doesn't have a nice KIND system like Fortran does, so neither of these is guaranteed to be right. But since C_INTPTR_T seems to be the same as C_SIZE_T on Windows at least, %zd should work. Just checking on gcc, it doesn't, however, so you need some sort of preprocessor noise to figure out whether you need %ld or %lld. Of course if you printed out in %p and z0 formats there would be no problems.
Print from C main: address of foo[0] = 000000000023FDF0 address of foo[1] = 000000000023FDF4 address of foo[2] = 000000000023FDF8 Print from C function: foo[0].i = 0 foo[1].i = 1 foo[2].i = 2 address of foo[0] = 000000000023FDF0 address of foo[1] = 000000000023FDF4 address of foo[2] = 000000000023FDF8 Print from Fortran subroutine: foo%( 1 ) = 0 foo%( 2 ) = 1 foo%( 3 ) = 2 address of foo%( 1) = 23FDF0 address of foo%( 2) = 23FDF4 address of foo%( 3) = 23FDF8
Edit: Oh yes, I forgot to mention that your example passed a 1-d array and not a 2-d array. Passing a 2-d array between C and Fortran seems to me to be a problem in that at least older versions of C didn't have anything really like a 2-d array, just an array of pointers, each pointing effectively to a row of the array. How do you get C and Fortran to agree that what gets passed from one to the other is the address of a contiguous block of memory that contains the whole 2-d array, not the address to an array of pointers to rows?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I finally made the counterexample to Quote #10 work on gcc/gfortran. Compile them with:
C:\>gfortran -c sub0.f90 C:\>gcc main0.c sub0.o -omain0 -lgfortran -Wl,--dynamicbase, --export-all-symbols
Now the addresses are different every time:
C:\>main0 Print from C main: address of foo[0] = 17563104 address of foo[1] = 17563108 address of foo[2] = 17563112 Print from C function: foo[0].i = 0 foo[1].i = 1 foo[2].i = 2 address of foo[0] = 17563104 address of foo[1] = 17563108 address of foo[2] = 17563112 Print from Fortran subroutine: foo%( 1 ) = 0 foo%( 2 ) = 1 foo%( 3 ) = 2 address of foo%( 1 ) = 17563104 address of foo%( 2 ) = 17563108 address of foo%( 3 ) = 17563112 C:\>main0 Print from C main: address of foo[0] = 14089040 address of foo[1] = 14089044 address of foo[2] = 14089048 Print from C function: foo[0].i = 0 foo[1].i = 1 foo[2].i = 2 address of foo[0] = 14089040 address of foo[1] = 14089044 address of foo[2] = 14089048 Print from Fortran subroutine: foo%( 1 ) = 0 foo%( 2 ) = 1 foo%( 3 ) = 2 address of foo%( 1 ) = 14089040 address of foo%( 2 ) = 14089044 address of foo%( 3 ) = 14089048
But unfortunately the addresses are still 32 bit size so the bug in Quote #10 is not evident. We can fix this with:
C:\>editbin /HIGHENTROPYVA main0.exe Microsoft (R) COFF/PE Editor Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved.
Now the addresses really are 64 bits and the bug shows itself:
C:\>main0 Print from C main: address of foo[0] = -361826704 address of foo[1] = -361826700 address of foo[2] = -361826696 Print from C function: foo[0].i = 0 foo[1].i = 1 foo[2].i = 2 address of foo[0] = -361826704 address of foo[1] = -361826700 address of foo[2] = -361826696 Print from Fortran subroutine: foo%( 1 ) = 0 foo%( 2 ) = 1 foo%( 3 ) = 2 address of foo%( 1 ) = 386185229936 address of foo%( 2 ) = 386185229940 address of foo%( 3 ) = 386185229944
So the difference may be that on Windows 10 /HIGHENTROPYVA is the default.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Seems that C99 VLAs pass 2-d arrays smoothly.
module m use, intrinsic :: iso_c_binding, only : c_int, c_loc, c_ptr, c_intptr_t implicit none type, bind(c) :: foo_f integer(c_int) :: i end type foo_f contains subroutine somesub(size_i, size_j, foo) bind(c, name="F_printf") !.. Argument list integer(c_int), intent(in), value :: size_i, size_j type(foo_f), intent(inout), target :: foo(size_j, size_i) !.. Local variables integer :: i, j type(c_ptr) :: add_foo_i !.. print *, " Print from Fortran subroutine:" do i = 1, size_i do j = 1, size_j print '(1x,a,i0,a,i0,a,i0.2)', " foo%(",j,",",i,") = ", foo(j,i)%i end do end do do i = 1, size_i do j = 1, size_j add_foo_i = c_loc(foo(j,i)) print '(1x,a,i0,a,i0,a,z0)', " address of foo%(",j,",",i,") = ", & transfer(add_foo_i,0_C_INTPTR_T) end do end do !.. return end subroutine somesub end module m
#include <stdio.h> #include <malloc.h> typedef struct { int i; } foo_c; void C_printf(int M, int N, foo_c
C:\>gfortran sub1.f90 -c C:\>gcc main1.c -omain1 sub1.o -lgfortran -Wl,--dynamicbase, --export-all-symbols -Wl,--high-entropy-va C:\>main1 Print from C main: address of foo[0][0] = 00000017B1406AE0 address of foo[0][1] = 00000017B1406AE4 address of foo[1][0] = 00000017B1406AE8 address of foo[1][1] = 00000017B1406AEC address of foo[2][0] = 00000017B1406AF0 address of foo[2][1] = 00000017B1406AF4 Print from C function: foo[0][0].i = 00 foo[0][1].i = 01 foo[1][0].i = 10 foo[1][1].i = 11 foo[2][0].i = 20 foo[2][1].i = 21 address of foo[0][0] = 00000017B1406AE0 address of foo[0][1] = 00000017B1406AE4 address of foo[1][0] = 00000017B1406AE8 address of foo[1][1] = 00000017B1406AEC address of foo[2][0] = 00000017B1406AF0 address of foo[2][1] = 00000017B1406AF4 Print from Fortran subroutine: foo%(1,1) = 00 foo%(2,1) = 01 foo%(1,2) = 10 foo%(2,2) = 11 foo%(1,3) = 20 foo%(2,3) = 21 address of foo%(1,1) = 17B1406AE0 address of foo%(2,1) = 17B1406AE4 address of foo%(1,2) = 17B1406AE8 address of foo%(2,2) = 17B1406AEC address of foo%(1,3) = 17B1406AF0 address of foo%(2,3) = 17B1406AF4

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page
- « Previous
-
- 1
- 2
- Next »