- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I am in the process of upgrading a large mixed language (C++/Fortran) solution from Visual Studio & Fortran 2013 to Visual Studio 2022 (17.1.6) and Fortran 2022 (2022.1.0.139).
I have many examples of trivial getter/setter subroutines in Fortran for marshalling data from C++, for example:
SUBROUTINE SET_SOMENUMBER(SOMENUMBER)
USE MSOMEMODULE, ONLY: C_SOMENUMBER
IMPLICIT NONE
INTEGER*4, VALUE :: SOMENUMBER
C_SOMENUMBER = SOMENUMBER
END
Calling code from C++:
extern "C"
{
void __stdcall set_somenumber(int SOMENUMBER);
}
...
const int numbertopass = 1;
set_somenumber(numbertopass);
In the 2013 solution this all works fine.
Following the upgrade, the debugger breaks in the Fortran subroutine with a read access violation exception on the line where the assignment occurs. The watch window reports "Undefined address" on the incoming value, suggesting it may be being erroneously interpreted as an address (0x1) and not a value - which would of course be expected to blow up.
If I change the code to pass by reference, it works fine, for example:
SUBROUTINE SET_SOMENUMBER(SOMENUMBER)
USE MSOMEMODULE, ONLY: C_SOMENUMBER
IMPLICIT NONE
INTEGER*4 :: SOMENUMBER
C_SOMENUMBER = SOMENUMBER
END
Calling code:
extern "C"
{
void __stdcall set_somenumber(const int& SOMENUMBER);
}
...
const int numbertopass = 1;
set_somenumber(numbertopass);
My Fortran compiler command is similar to:
"C:\Program Files (x86)\Intel\oneAPI\compiler\2022.1.0\windows\bin\intel64_ia32\ifort.exe" /nologo /debug:full /warn:interfaces /iface:cvf /names:lowercase /iface:nomixed_str_len_arg /module:"C:\someproject\int\debug\" /object:"C:\someproject\bin\debug\somecode.obj" /Fd"C:\someproject\int\debug\vc143.pdb" /traceback /check:bounds /check:stack /libs:static /threads /dbglibs /Qvc14.3 /Qlocation,link,"C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.31.31103\bin\Hostx86\x86" /c C:\someproject\somecode.for
Can anyone shed light on this? Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
See this thread circa 2013 and the comments by @Steve_Lionel regarding changes starting what was then version 14.0 of Intel Fortran compiler:
The above might be relevant to you but Intel team and/or @Steve_Lionel may best be able to explain to you the situation re: VALUE attribute in the absence of BIND(C,..) which is what you appear to have in your code.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
See this thread circa 2013 and the comments by @Steve_Lionel regarding changes starting what was then version 14.0 of Intel Fortran compiler:
The above might be relevant to you but Intel team and/or @Steve_Lionel may best be able to explain to you the situation re: VALUE attribute in the absence of BIND(C,..) which is what you appear to have in your code.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, the link explains the behavior I am seeing here.
Adding BIND(C) has done the trick. I also needed to change the calling convention of the C++ function export to avoid a name decoration (leading underscore) mismatch:
extern "C"
{
void __cdecl set_somenumber(int SOMENUMBER);
}
SUBROUTINE SET_SOMENUMBER(SOMENUMBER) BIND(C)
USE MSOMEMODULE, ONLY: C_SOMENUMBER
IMPLICIT NONE
INTEGER*4, VALUE :: SOMENUMBER
C_SOMENUMBER = SOMENUMBER
END
I need to now decide which of changing my code everywhere to BIND(C) or using pass by reference would be less disruptive.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You could use bind(C, name =..... ) to avoid changing the call convention maybe. "Integer(C_INT), value" would be nicer also
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I don't see how BIND(C, name=...) would avoid changing the calling convention - it forces the use of cdecl doesn't it? Presumably I would also need !DIR$ ATTRIBUTES STDCALL to go back to stdcall.
I agree about the the use of C types. We do that in a few places but not consistently.
For now, I've settled on passing const references from the calling side. Seems to work.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
if set_somenumber(numbertopass) on the C side expects a decorated name then you can name the routine of the Fortran side if you wanted to I would expect. Maybe I am misunderstanding something.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Re: calling convention
Intel Fortran uses the C convention unless you explicitly override it. (On x64, there is only one calling convention.) You can use !DIR$ ATTRIBUTES STDCALL with BIND(C) if needed.
As is said earlier, VALUE without BIND(C) means something different from what you wanted - it passes an anonymous, writable copy of the argument by reference. BIND(C) also prevents hidden arguments and should be used when calling other languages where possible. And if you're using C++, definitely use 'extern "C"' on the C++ side.
For naming, BIND(C) downcases the name and applies whatever name decoration the "companion C processor" would. Note that it doesn't say C++ - if you are using C++ you'll get a mangled name without 'extern "C"'.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the info. I retained __stdcall and changed the C++ callee to accept const references, which works fine. After discussing with a colleague who has some Fortran experience, I also replaced VALUE with INTENT(IN).
I am now stuck on a possibly related marshalling issue. As with the problem above, the code runs correctly with Composer 2013.
This time it concerns passing arrays to a C++ routine. Here is a simplified example:
Fortran:
SUBROUTINE TEST()
IMPLICIT NONE
DOUBLE PRECISION array_1d(3)
DOUBLE PRECISION array_2d(2,2)
array_1d(1) = 1.D0/3.D0
array_1d(2) = 1.D0/7.D0
array_1d(3) = 1.D0/13.D0
array_2d(1,1) = 2.D0/3.D0
array_2d(1,2) = 2.D0/7.D0
array_2d(2,1) = 2.D0/11.D0
array_2d(2,2) = 2.D0/13.D0
CALL LOGARRAY([array_1d], SHAPE(array_1d))
CALL LOGARRAY([array_2d], SHAPE(array_2d))
RETURN
END SUBROUTINE TEST
SUBROUTINE LOGARRAY(elements, shape)
USE ISO_C_BINDING
IMPLICIT NONE
DOUBLE PRECISION, DIMENSION(*), INTENT(IN), TARGET :: elements
INTEGER, INTENT(IN), TARGET :: shape(:)
CALL logcpp(C_LOC(elements), SIZEOF(elements(1)), C_LOC(shape), SIZE(shape))
RETURN
END SUBROUTINE LOGARRAY
C++:
extern "C"
{
void __stdcall logcpp(const char *elements, const int& element_size, const int *shape, const int& rank);
}
void __stdcall logcpp(const char *elements, const int& element_size, const int *shape, const int& rank)
{
if (rank == 0) // Scalar
{
log_data(&elements[0]); // log shows &elements[0] has wrong value
}
else if (rank == 1) // Rank-1 array
{
int nElem = shape[0]; // shape[0] is pointing to wrong memory (huge number)
for (int i = 0; i < nElem; ++i)
{
log_data(&elements[i*element_size]);
}
}
Thank you.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You DO want VALUE (and BIND(C), which I don't see in this example.) You DON'T want C_LOC. INTENT(IN) doesn't change how anything is passed. What you have now is passing an address by reference, an extra set of indirection.
What do you think the square brackets in [array_1d], etc., are doing for you? I suppose it makes this passing an expression rather than a variable, but I don't see the point.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks Steve, you hit the nail on the head with C_LOC. I don't know what the intention of the square brackets was I'm afraid (I'm not very experienced with Fortran, and the person who wrote this left).
I removed VALUE (example from the original post) because I changed the C++ interface in the trivial setter functions to accept const references. They seem to work OK.
Here's the working code following your suggestions (key diffs in bold):
Fortran:
SUBROUTINE TEST()
IMPLICIT NONE
DOUBLE PRECISION array_1d(3)
DOUBLE PRECISION array_2d(2,2)
array_1d(1) = 1.D0/3.D0
array_1d(2) = 1.D0/7.D0
array_1d(3) = 1.D0/13.D0
array_2d(1,1) = 2.D0/3.D0
array_2d(1,2) = 2.D0/7.D0
array_2d(2,1) = 2.D0/11.D0
array_2d(2,2) = 2.D0/13.D0
CALL LOGARRAY(array_1d, SHAPE(array_1d))
CALL LOGARRAY(array_2d, SHAPE(array_2d))
RETURN
END SUBROUTINE TEST
SUBROUTINE LOGARRAY(elements, shape)
USE ISO_C_BINDING
IMPLICIT NONE
DOUBLE PRECISION, DIMENSION(*), INTENT(IN), TARGET :: elements
INTEGER, INTENT(IN), TARGET :: shape(:)
INTERFACE
SUBROUTINE Log(elements, sizeOfElement, shape, sizeOfShape) BIND(C,NAME='logcpp')
USE ISO_C_BINDING
DOUBLE PRECISION elements
INTEGER(KIND=C_SIZE_T), VALUE :: sizeOfElement
INTEGER shape
INTEGER, VALUE :: sizeOfShape
END SUBROUTINE Log
END INTERFACE
CALL Log(elements(1), SIZEOF(elements(1)), shape(1), SIZE(shape))
RETURN
END SUBROUTINE LOGARRAY
C++:
extern "C" void __cdecl logcpp(const char* elements, const int elementSize, const int* shape, const int rank);
I actually got this cut-down example working without the interface and BIND(C). However, my real code also passes a string and a function pointer (C_FUNLOC), and with these extra parameters, suddenly it doesn't like elements(1) and shape(1):
error #6633: The type of the actual argument differs from the type of the dummy argument. [ELEMENTS].
error #6633: The type of the actual argument differs from the type of the dummy argument. [SHAPE].
So perhaps the only way to get this working reliably (esp. when marshalling different C types in the same call) is to use an interface block with BIND(C), even though more trivial examples apparently don't always require this.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
My understanding is that "const" is like INTENT(IN) and doesn't change how the argument is received. I can't help with the errors because you haven't shown the source that generates them - the code you posted doesn't. I am a bit puzzled that on the C++ side elements is a char array but you're passing a double-precision array element to it. What is the intention here?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"My understanding is that "const" is like INTENT(IN) and doesn't change how the argument is received"
Yes, const is simply a compile-time check.
The char array is just a way of allowing Fortran to pass data of any type to the C++, along with a callback to the log routine appropriate to the type. In a pure C++ app you would just use a method template. The logcpp function can accept underlying INTEGER, DOUBLE PRECISION, REAL etc. data.
I tried to create a minimal example that reproduces the problem (including the string and function pointer) but it compiles without any problem. The real code is too big to post but the example has all the key elements so I'm not sure quite what was going on with the error. In any case, the code is stable with the interface - I'm going to run with that, just pleased to have this working at last! Thanks again.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
OK - in the future, you can declare the argument that accepts any type as TYPE(*). This is essentially the same as C's void and will match any type.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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