Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
103 Views

Mapping Shared Memory from C++

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

 

 

 

0 Kudos
23 Replies
Highlighted
86 Views

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

 

 

0 Kudos
Highlighted
Beginner
86 Views

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 

0 Kudos
Highlighted
86 Views

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

0 Kudos
Highlighted
Beginner
86 Views

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

 

 

 

 

 

 

0 Kudos
Highlighted
Beginner
86 Views

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

0 Kudos
Highlighted
Black Belt Retired Employee
86 Views

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.

0 Kudos
Highlighted
Beginner
86 Views

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])

  

 

0 Kudos
Highlighted
Beginner
86 Views

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. 

0 Kudos
Highlighted
86 Views

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

0 Kudos
Highlighted
Beginner
86 Views

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.

0 Kudos
Highlighted
86 Views

Which statement causes the crash?

What is the C_LOC of F0 following its C_F_POINTER (both cases)

Jim Dempsey

0 Kudos
Highlighted
Beginner
86 Views

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.

 

 

 

 

0 Kudos
Highlighted
Black Belt Retired Employee
86 Views

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?

0 Kudos
Highlighted
Beginner
86 Views

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. 

0 Kudos
Highlighted
Black Belt Retired Employee
86 Views

Which compiler version are you using? Would you please include a short but complete program that demonstrates the problem?

0 Kudos
Highlighted
Beginner
86 Views

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

 

 

 

 

0 Kudos
Highlighted
Black Belt Retired Employee
86 Views

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.

0 Kudos
Highlighted
Beginner
86 Views

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"

 

0 Kudos
Highlighted
Beginner
86 Views

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.

0 Kudos