- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have a C struct and a corresponding Fortran derived type which is defined with bind(c), and have successfully been passing instances back and forth using the syntax (in C):
myFortranRoutine(&myInteroperableStruct);
...and in Fortran:
subroutine myFortranRoutine(c_myInteroperableStruct) bind(c, name='myFortranRoutine') type(c_ptr), value :: c_myInteroperableStruct type(interoperableStruct), pointer :: myInteroperableStruct call c_f_pointer(c_myInteroperableStruct, myInteroperableStruct) call doInterestingStuff(myInteroperableStruct) end subroutine myFortranRoutine
For a variety of reasons it would be convenient to change the C call from passing a pointer to passing-by-value in cases where I don't require the struct to be changed by the Fortran routine. In other words, in C I'd like to write:
myFortranRoutine(myInteroperableStruct);
...and on the Fortran side I'd put:
subroutine myFortranRoutine(myInteroperableStruct) bind(c, name='myFortranRoutine') type(interoperableStruct), value:: myInteroperableStruct call doInterestingStuff(myInteroperableStruct) ! Obviously, any changes to myInteroperableStruct won't ! be returned to the C application end subroutine myFortranRoutine
The trouble is that, when I do this, the application compiles without any errors but at runtime there is a 24-byte offset in the structs: that is to say, in the Fortran code, the first 24 bytes of the struct contain apparent garbage, and the rest of the struct contains exactly what it should but with a fixed offset of 24 bytes.
Is this because I've got the syntax wrong, or simply because I'm doing something which isn't supported?
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Please come up with a buildable and runnable example that demonstrates the problem. You've left out too much that could be important, especially the declaration of interoperableStruct. My experience is that paraphrases and snippets usually obscure the real problem.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If you pass the C struct by value then it will have to be pushed onto the stack. Why not on the C++ side, declare the function prototype as taking reference and on the Fortran side declare the argument with type you have (not as c_ptr). This should work.... since both are references. Give it a try.
This should work with user defined type but not with arrays that require a descriptor.
You will still have to work out issues between languages with regard to packing of structs.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you both. I haven't tried Jim's suggestion yet, but here's what I did in response to Steve's...
Firstly, the C code:
#include "stdafx.h" struct interoperableStruct { int myVariables[10]; }; extern "C" __declspec(dllimport) void __cdecl myFortranRoutine(interoperableStruct myStruct); int _tmain(int argc, _TCHAR* argv[]) { interoperableStruct myInteroperableStruct; for (int i = 0; i < 10; ++i) { myInteroperableStruct.myVariables = i; } myFortranRoutine(myInteroperableStruct); return 0; }
Then the Fortran, which I wrote as a DLL to reflect the usage in my own application:
module myFortranLibrary use, intrinsic :: iso_c_binding implicit none type, bind(c) :: interoperableStruct integer(c_int), dimension(10) :: myVariables end type interoperableStruct contains subroutine myFortranRoutine(myInteroperableStruct) bind(c, name='myFortranRoutine') !DEC$ ATTRIBUTES DLLEXPORT :: myFortranRoutine type(interoperableStruct), value :: myInteroperableStruct integer :: i do i=1,10 print *, myInteroperableStruct%myVariables(i) end do end subroutine myFortranRoutine end module myFortranLibrary
Here’s the funny thing. When I compiled this in 32-bit mode, everything worked absolutely fine, as it ought to have done. However, when I recompiled exactly the same source code in 64-bit mode (as my own application is 64-bit), it failed completely: that is to say, there were no compilation or runtime errors, but on the Fortran side every field of the struct was filled with #CCCCCCCC.
I double-checked that I had the same alignment in each case (compiling the C++ code with /Zp8 and the Fortran with /align:rec8byte rather than relying on the defaults), but this made no difference.
So I went back to passing the struct by reference. The changes to the C code were trivial, and the revised Fortran code was now:
module myFortranLibrary use, intrinsic :: iso_c_binding implicit none type, bind(c) :: interoperableStruct integer(c_int), dimension(10) :: myVariables end type interoperableStruct contains subroutine myFortranRoutine(c_myInteroperableStruct) bind(c, name='myFortranRoutine') !DEC$ ATTRIBUTES DLLEXPORT :: myFortranRoutine type(c_ptr), value :: c_myInteroperableStruct type(interoperableStruct), pointer :: myInteroperableStruct integer :: i call c_f_pointer(c_myInteroperableStruct, myInteroperableStruct) do i=1,10 print *, myInteroperableStruct%myVariables(i) end do end subroutine myFortranRoutine end module myFortranLibrary
Just as in my own application, this worked absolutely fine in 32 and 64-bit mode.
So I’m none the wiser. It seems that there is definitely a problem when passing structs by value in 64-bit mode, though not in 32-bit mode, but even in 64-bit mode I don’t see quite the same failure in the test application as I do in my own application. I’d really appreciate any further suggestions.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Which version? I had a similar issue with a user-defined type argument with the value attribute not being passed correctly in XE 2013 SP1 update 1. I was going to create a premier support issue for this, but noticed that it had been fixed in update 2. My suspicion is this was fixed by DPD200249357 (VALUE attribute inconsistently applied in 14.0 with structure), could be the case for your problem too.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm running SP1 Update 3, installed about a fortnight ago.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I can see something wrong is happening, but I am not exactly sure what. What I do see is that both the C and Fortran code is making a copy of your struct - C is passing a copy and Fortran is making a copy of what is passed in. Fortran doesn't seem to be copying the right thing, though. We will investigate.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Correction - Fortran is not making a copy. But it still gets the address wrong. An all-Fortran example that ought to behave identically is working ok, so I need to dig deeper.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you Steve; I appreciate your attention!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As I needed to bolt down my API and move on, I've done this a different way.
My reason for wanting to pass my structs by value was to make it absolutely obvious when a Fortran call was likely to change a function argument, so that in a C function call like:
myFortranFunction(&firstArg, secondArg);
it would be clear that firstArg might be modified but secondArg wouldn't.
Of course, this only holds in C anyway, since in C++ there'd be nothing to stop someone from rewriting myFortranFunction in C++ and passing secondArg as a reference, in which case it absolutely could be modified. Anyway, I can achieve my objective just by making the second argument a const pointer, so in the C header:
void myFortranFunction(interoperableStruct *firstArg, const interoperableStruct *secondArg);
...and in the program:
myFortranProgram(&firstArg, &secondArg);
...all of which is slightly less aesthetically pleasing, but just as precise.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You could write multiple same named shell functions with different argument signatures. Those without pointer to object get copied on the C++ side, than call made with pointer version to temporary copy. This way you can use the & for modifiable arguments and non-& arg for non-modifiable.
I might add that you can also use the make copy to consolidate a C++ multi-dimension array (pointer to pointers) that is non-contiguous. This will simplify construction the array descriptor on the Fortran side.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This is very strange. The other day when I took your ZIP and rebuilt it, I could swear that I saw the printed output wrong. But when I try it today, with a fresh copy of your ZIP, the output is correct. But what is consistently wrong is the view of the variable in the debugger. The debugger thinks the dummy argument is allocated at a different location than where it is - the distance between the two is not consistent.
Is this what you're seeing?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks Jim for your earlier comment.
And thanks Steve, yes this is what I see. When I raised the issue I wasn't even paying much attention to what got printed out in the console, as I was mainly looking at the debugger which was definitely awry. I repeated the experiment myself today and sure enough, the debugger output is wrong but the console output is right.
By the way, I've been working on something vaguely related in an all-C project today, and I've noticed some comparable behaviour, though not just affecting the debugger. If I pass a struct by value, then the subroutine receives the struct as intended and runs properly, but on returning to the main routine the struct which was passed (and which should, if I understand what's meant to happen under the hood, have been copied onto the stack without being affected itself in any way) becomes corrupted. If I pass the same struct using a const pointer or (in the C++ parts of the code) a const reference, then everything is absolutely fine and works without errors. I found a couple of cases where the Intel C compiler failed but the Microsoft one didn't, and yet there were also plenty of cases where the Microsoft one did as well.
All told, while my understanding of the relevant language standards is that passing structs by value should work (so long as they're small enough not to cause a stack overflow, and none of mine are bigger than 100 bytes), in practice it's just causing me needless trouble. I'm probably just doing it wrong, but as there's a simple enough workaround I think I should just stick with that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm sure the Intel C++ developers would be interested in seeing an example that failed.
I will report the Fortran debugging issue to the (Fortran) developers.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks Steve. I'll send a report to the Premier Support people.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As a matter of fact, there is nothing to report; the error was mine.
In the all-C case I mentioned yesterday, the object being passed by value was actually a class with a set of constructors and a destructor. When the copy onto the stack was made, a syntax error on my part caused the wrong constructor to be called, so that some internal data was not copied correctly. This left my new object on the stack with a pointer which was still pointing to a memory buffer in my old object. At the end of the subroutine, the right destructor was called, freeing the memory buffer in the old object so that when control returned from the subroutine my old object which had been copied had nevertheless become corrupted!
I guess the reason for the difference in behaviour between Intel and Microsoft C++ was that, in a case which was basically undefined behaviour because I'd failed to specify a correct constructor, the different compilers took different guesses as to which alternative to use.
This is a reason why I love Fortran - it saves you from this kind of pain! It's also a salutary lesson why passing C structs and classes by value is a bad and dangerous thing to do (unless there is a really good reason such as the copy-and-swap idiom, but such nasties have no place in a civilised Fortran forum).
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page