- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm creating a shared memory file mapping in a DLL in C++ and assign it to SM01
extern "C" {
__declspec (dllexport) char *SM01 = NULL;
}
I can use this in another C++ DLL by doing this
extern "C" __declspec(dllimport) char *SM01;
#define abc1 (*(int *)(SM01 + 4))
I can assign abc1 to a value in shared memory.
I would like to do the equivalent in a FORTRAN DLL but it's not working. Something like this.
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
TYPE(C_PTR), BIND(C) :: SM01
SUBROUTINE FORTRAN_LIBRARY()
INTEGER abc1
EQUIVALENCE (abc1,SM01(4))
abc1 = 555
END
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
https://software.intel.com/en-us/node/678433 provides an example on how to interoperate with C pointer.
For your case:
USE, INTRINSIC :: ISO_C_BINDING
CHARACTER,POINTER::ar(:)=> NULL()
INTEGER,PARAMETER :: n_elements = 4
!DIR$ ATTRIBUTES DLLIMPORT :: SM01
type(c_ptr) :: SM01
call C_F_POINTER (sm01,ar,[n_elements])
ar(4) = 'c'
Thanks,
Xiaoping Duan
Intel Customer Support
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm still having 2 problems.
1. I get this even though I'm linking in the library that defines the symbol. I'm already linking the library for the C part.
error LNK2001: unresolved external symbol __imp_SM01
2. I'm trying to map the whole block of 2000 characters (as allocated in C) and assign different variable types in Fortran. I would also like to see the same values I assigned in C++ in Fortran. As shown by the following, I'm trying to read the value set in C++ which was assigned at position 4. I guessing in Fortran it will be position 5.
INTEGER abc1
EQUIVALENCE (abc1,SM01(5))
Thanks
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
1. Please check if your C++ DLL correctly exports the symbol by command "dumpbin/exports <dllname>.lib".
2. EQUIVALENCE can't be used for object with DLLIMPORT attribute. The default lower bound of fortran array is 1 and of C++ is 0 so it is 5 in fortran.
Thanks,
Xiaoping Duan
Intel Customer Support
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Number 1 is fixed using your solution to look at the dump. The buffer is exported as _SM01 so now the code looks like this.
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
CHARACTER,POINTER::ar(:)=> NULL()
INTEGER,PARAMETER :: n_elements = 4
!DIR$ ATTRIBUTES DLLIMPORT :: _SM01
TYPE(C_PTR) :: SM01
call C_F_POINTER (SM01,ar,[n_elements])
ar(4) = 'c'
But this crashed when setting ar(4) = 'c' In the debugger I see SM%PTR is 0 and ar(4) as undefined address.
Furthermore, is there a way to map the memory of 2000 bytes to different types? I don't want to map the whole memory, just a portion. Let say:
C Map ar(1:4)
INTEGER A
C Map ar(5:8)
INTEGER B
C Map ar(9:88)
INTEGER C(1:20)
C skip to the 200th byte to Map a real8 (201:208)
REAL*8 D
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have the solution but one last question. Below is the code. Is there a way to not create module mymod2 and just express the DLL import in the subroutine? When I move it, I get the following error: If any bind-entity in a bind-stmt is an entity-name, the bind-stmt shall appear in the specification part of a module
module mymod2
use, intrinsic :: ISO_C_BINDING
IMPLICIT NONE
CHARACTER,POINTER::ARR01(:)=> NULL()
type(C_PTR), bind(C) :: SM01
!DIR$ ATTRIBUTES DLLIMPORT :: SM01
END
SUBROUTINE FORTRAN_LIBRARY2()
USE, INTRINSIC :: ISO_C_BINDING
USE mymod2
IMPLICIT NONE
integer*4 ,pointer :: F0
integer*4 ,pointer :: F1
real*4, pointer :: F2
integer*4, pointer :: F3(:)
call C_F_POINTER (SM01, ARR01, [2000])
call C_F_POINTER(C_LOC(ARR01(1)), F0)
call C_F_POINTER(C_LOC(ARR01(5)), F1)
call C_F_POINTER(C_LOC(ARR01(9)), F2)
call C_F_POINTER(C_LOC(ARR01(13)), F3,[4])
F0 = 673
F1 = 544
f2 = 66.7
END
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I don't get any errors compiling this source in 17.0.2. But no, you can't declare a variable with BIND(C) except as a module variable. The DLLIMPORT is not the issue (though since you can't declare an "external" variable outside of a module, you can't DLLIMPORT it either. You can DLLIMPORT a COMMON, but using a BIND(C) variable is the better approach.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
OK, thanks. I'll stick with creating a module.
Another question. Does FORTRAN care how the memory is mapped with pointer as shown above? Is there an latency when an INTEGER or ARRAY is not bound on 4 or 8 bytes.
Here is an example where F0 isn't padded to keep the memory bound to 4 bytes:
LOGICAL*1, POINTER :: F0
INTEGER*4, POINTER :: F1
REAL*8, POINTER :: F2
INTEGER*4, POINTER :: F3(:)
call C_F_POINTER (SM02, SMARR02, [2000])
call C_F_POINTER(C_LOC(SMARR02(1)), F0)
call C_F_POINTER(C_LOC(SMARR02(2)), F1)
call C_F_POINTER(C_LOC(SMARR02(6)), F2)
call C_F_POINTER(C_LOC(SMARR02(14)), F3,[10])
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'll answer my own question. Memory must be aligned to obtain the fastest possible read. A CPU always reads word size therefore when accessing misaligned data, multiple reads may occur.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The default array indexing for Fortran is 1-based
SMARR02(1), is the 1st element (when default allocated) there is no 0'th element
You could specify
integer(1) :: SMARR02(0:1999)
if you want a 0-based array of 2000 bytes
>>an example where F0 isn't padded to keep the memory bound to 4 bytes
F0 is a pointer to s logical(1), its (the pointer) size is the size of the machine word (4 or 8 bytes), what it points to would be arbitrarily byte aligned
F3 is different in that it is an array descriptor than can point to an arbitrarily sized array (blob) of 4-byte integers (which could potentially be arbitrarily byte aligned, though you wouldn't normally want to do so).
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Is quad precision working with Fortran?
REAL*16 , POINTER :: F0
call C_F_POINTER (SM02, SMARR02, [2000])
call C_F_POINTER(C_LOC(SMARR02(1)), F0)
F0 = 11.22
Visual Studio crashes.
REAL*8 works fine.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Which statement causes the crash?
What is the C_LOC of F0 following its C_F_POINTER (both cases)
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
F0 = 11.22 is causing the crash. F0 crashes VS due to trying to get its value.
C_LOC(SMARR02(1)) is pointing to a shared memory created in C++
Do you mean loc(F0)? This changes every time since the memory is allocated on the fly.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
When you say "crashes VS", do you mean when attempting to see the value in the debugger? I recall there were debugger issues with REAL(16) in the past. Does the program run without debugging?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yes, the crash is only in the debugger. In release, it does run but it changes the value to 0. I had previously set it to 25.6 using a "long double" on the C++ side.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Which compiler version are you using? Would you please include a short but complete program that demonstrates the problem?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm using VS 2015 integrated with Intel® Parallel Studio XE 2016 building in Release 64bit
CExecutable is the executable that runs. This is setting the initial values and printing them, Calls Fortran, and then prints values again.
FortranLibrary is library linked into CExecutable. FortranLibrary.f sets new values.
PSIM is a C++ that creates the Shared Memory
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
When I run this, the call to CreateFileMapping in PSIM's DllMain fails, and GetLastError returns 5 (Access Violation). I am not familiar with this API and my study of the MSDN description doesn't suggest why this fails. I did puzzle over your explicitly specifying a handle value instead of the constant INVALID_HANDLE_VALUE, though the effect appears to be the same. This is as far as I got.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm guessing you need admin privileges to create global memory. Try changing
L"Global\\SM01" to L"Local\\SM01"
and
L"Global\\SM02" to L"Local\\SM02"
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Of possible (but remote) interest, here is an easy method of creating a shared memory buffer using file mapping, without need for a DLL. You can share across separate processes (you'll need your own synchronization method, not too hard to do), threads, DLLs, even separate machines with only minor tweaks and additional effort. This was written in ~1999 for DVF or CVF and may need some tweaks for IVF but that should be fairly minor. Overhead will average in the nanoseconds as this prevents page swapping as-is (can be easily changed).
http://www.fortranlib.com/ShareBufferWin32.f90
I could provide an update (IVF compatibility, modernize, etc.) if there's any interest.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ok, I changed the Global to Local. Got this:
Values: 0 0 0 0 Values: 65 5345 55.5 25.6 Values: 673 544 23.43 0
The zero is because you declared this item REAL*16 in Fortran but "long double" in C. These are not the same type - at least in MSVC, long double is the same as double. MSDN says:
Previous 16-bit versions of Microsoft C/C++ and Microsoft Visual C++ supported the long double, 80-bit precision data type. In Win32 programming, however, the long double data type maps to the double, 64-bit precision data type. The Microsoft run-time library provides long double versions of the math functions only for backward compatibility. The long double function prototypes are identical to the prototypes for their double counterparts, except that the long double data type replaces the double data type. The long double versions of these functions should not be used in new code.

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