Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Mapping Common Block to C Memory

merik
Beginner
1,092 Views

Hi,

I have a C pointer that points to a memory buffer that I am trying to map to a common block in FORTRAN. As a simple example let assume I have this,

char buf[100];
__declspec (dllexport) char *xrftest = buf;

I am trying to mapa common block to the pointer. Let say this:

integer*4
& F2
&, F1
CHARACTER*1
& DUM(80)
COMMON /BOB /
& DUM, F1, F2

Is there a way to bring in the pointer and do an equivalence or something to map the memory to the common block?

Here is what I tried with no success:

!ms$attributes dllimport :: XRFTEST
!ms$attributes C, alias : '_xrftest' :: XRFTEST
EQUIVALENCE(BOB, XRFTEST)

Thanks

0 Kudos
12 Replies
TimP
Honored Contributor III
1,092 Views
Quoting - merik

Hi,

I have a C pointer that points to a memory buffer that I am trying to map to a common block in FORTRAN.

You'd have to follow the traditional examples, using the labeled COMMON name on the Fortran side as a global struct on the C side, Even if you don't care to use modern alternatives, you should read the "Interoperability with C" section of ifort documentation.

0 Kudos
merik
Beginner
1,092 Views
Quoting - tim18

You'd have to follow the traditional examples, using the labeled COMMON name on the Fortran side as a global struct on the C side, Even if you don't care to use modern alternatives, you should read the "Interoperability with C" section of ifort documentation.

Usually, this works if you access the buffer straight:

Something like,

integer*4
& F2
&, F1
CHARACTER*1
& DUM(80)
COMMON /XRFTEST/
& DUM, F1, F2

!ms$attributes dllimport :: XRFTEST
!ms$attributes C, alias : '_xrftest' :: XRFTEST

Where XRFTEST is now the buffer char XRFTEST[100];

however, this does not function with a pointer to bytes in memory.

0 Kudos
Steven_L_Intel1
Employee
1,092 Views

The tricky part here is that the C object is a pointer to a thing, not the thing itself. While this can be done with various extensions, let me show you how it's done in standard Fortran 2003:

[cpp]    module mymod
    use, intrinsic :: ISO_C_BINDING
    implicit none

    type, bind(C) :: xrftest_t
      character(80) :: DUM
      integer(C_INT) :: F1, F2
    end type xrftest_t
    
    type(C_PTR), bind(C) :: xrftest
    end module mymod
    
    program test
    use mymod
    type(xrftest_t), pointer :: BOB
    
    ! Convert C pointer to Fortran pointer
    call c_f_pointer (xrftest, BOB)
    print *, BOB%DUM

    end program test
[/cpp]

It is required that the declaration of the "interoperable variable" (the one you're sharing with C) be in a module, so I've put the structure declaration and the variable in a module. Note the bind(C) on the type and variable - these tell the compiler that these items are "interoperable".

The main program uses C_F_POINTER (inherited from ISO_C_BINDING) to convert the C pointer to a Fortran pointer (of type xrftest_t). Now you can reference the components of the structure.

0 Kudos
merik
Beginner
1,092 Views

The tricky part here is that the C object is a pointer to a thing, not the thing itself. While this can be done with various extensions, let me show you how it's done in standard Fortran 2003:

module mymod use, intrinsic :: ISO_C_BINDING implicit none type, bind(C) :: xrftest_t character(80) :: DUM integer(C_INT) :: F1, F2 end type xrftest_t type(C_PTR), bind(C) :: xrftest end module mymod program test use mymod type(xrftest_t), pointer :: BOB ! Convert C pointer to Fortran pointer call c_f_pointer (xrftest, BOB) print *, BOB%DUM end program test


It is required that the declaration of the "interoperable variable" (the one you're sharing with C) be in a module, so I've put the structure declaration and the variable in a module. Note the bind(C) on the type and variable - these tell the compiler that these items are "interoperable".

The main program uses C_F_POINTER (inherited from ISO_C_BINDING) to convert the C pointer to a Fortran pointer (of type xrftest_t). Now you can reference the components of the structure.

The problem I have is that I want to be able to access the variables DUM, F1, and F2 straight without pointers. This is because I don't want to change existing code that I have. If there a way not to use pointer notation?

0 Kudos
Steven_L_Intel1
Employee
1,092 Views

Well, you mean component notation - there's no pointer there. Here's an alternative. It still involves pointers but once you call init at the beginning of the program, the rest of your code can reference DUM, F1 and F2 directly. All you have to do is add the USE of the module to make them visible.

[cpp]    module mymod
    use, intrinsic :: ISO_C_BINDING
    implicit none

    type, bind(C):: xrftest_t
      character(80) :: DUM
      integer(C_INT) :: F1, F2
    end type xrftest_t
    
    type(C_PTR), bind(C) :: xrftest
    character(80), pointer, protected :: DUM
    integer(C_INT), pointer, protected :: F1, F2
    
    contains
    
    subroutine init
    type(xrftest_t), pointer :: p
    call c_f_pointer (xrftest, p)
    DUM => p%DUM
    F1 => p%F1
    F2 => p%f2
    end subroutine init
    end module mymod
    
    program test
    use mymod
 
    call init
    
    print *, F1

    end program test[/cpp]

0 Kudos
merik
Beginner
1,092 Views

Well, you mean component notation - there's no pointer there. Here's an alternative. It still involves pointers but once you call init at the beginning of the program, the rest of your code can reference DUM, F1 and F2 directly. All you have to do is add the USE of the module to make them visible.

module mymod use, intrinsic :: ISO_C_BINDING implicit none type, bind(C):: xrftest_t character(80) :: DUM integer(C_INT) :: F1, F2 end type xrftest_t type(C_PTR), bind(C) :: xrftest character(80), pointer, protected :: DUM integer(C_INT), pointer, protected :: F1, F2 contains subroutine init type(xrftest_t), pointer :: p call c_f_pointer (xrftest, p) DUM => p%DUM F1 => p%F1 F2 => p%f2 end subroutine init end module mymod program test use mymod call init print *, F1 end program test


I assume that all of this is only in 10.1. Do you have a equivalent for 9.1?

0 Kudos
Steven_L_Intel1
Employee
1,092 Views
Hmm. A bit harder that way. You need to define an integer variable of kind INT_PTR_KIND() in the COMMON which maps to xrftest. You'll need the !DEC$ ALIAS directive to change the name. Then use the integer POINTER extension to have that variable be a pointer to a variable of type xrftest_t. You're still left with the need to access the individual fields as variables on their own. I suppose you could use the integer POINTER extension with three pointers and assign each one loc(struct%component).

I highly recommend upgrading to 11.0.
0 Kudos
merik
Beginner
1,092 Views

The tricky part here is that the C object is a pointer to a thing, not the thing itself. While this can be done with various extensions, let me show you how it's done in standard Fortran 2003:

module mymod use, intrinsic :: ISO_C_BINDING implicit none type, bind(C) :: xrftest_t character(80) :: DUM integer(C_INT) :: F1, F2 end type xrftest_t type(C_PTR), bind(C) :: xrftest end module mymod program test use mymod type(xrftest_t), pointer :: BOB ! Convert C pointer to Fortran pointer call c_f_pointer (xrftest, BOB) print *, BOB%DUM end program test


It is required that the declaration of the "interoperable variable" (the one you're sharing with C) be in a module, so I've put the structure declaration and the variable in a module. Note the bind(C) on the type and variable - these tell the compiler that these items are "interoperable".

The main program uses C_F_POINTER (inherited from ISO_C_BINDING) to convert the C pointer to a Fortran pointer (of type xrftest_t). Now you can reference the components of the structure.


Is there a way to do the assignment "call c_f_pointer (xrftest, BOB)" in a global scope? The reason I ask is becuase all my declaration for the legacy code is inside an include file so as I am generating the include file, I have problems accessing the FORTRAN file.

I guess I need the equivalence of doing something like this in a C header?

int getValue() {return 1}

int a = getValue();

a will be assigned 1 when you load (global scope)

0 Kudos
Steven_L_Intel1
Employee
1,092 Views

No - Fortran doesn't have that sort of thing. What you can do is write an initialization procedure that does the conversion and call it at the start of the main program (or wherever is convenient.)

0 Kudos
merik
Beginner
1,092 Views

OK, here I go again trying to do the same thing but a different way. Basically, I am trying to create a filemappingand I am trying to map given variables to vars that already exist. I saw another approach that was like:

[cpp]      COMMON   /XRFTEST  / dummy(0:99)
!ms$attributes dllimport :: XRFTEST
!ms$attributes C, alias : '_xrftest' :: xrftest
      CHARACTER*1 dummy
      
      INTEGER*4 ABC
      Equivalence(ABC, dummy(80))

      ABC = 99[/cpp]
that seems to work fine when I had

__declspec (dllexport) char xrftest[100];

but because I am only getting a pointer back when I use the file mapping, this does not work with

__declspec (dllexport) char buf[100];
__declspec (dllexport) char *xrftest = buf;

Any idea how to map existing vars in the declaration area like the above? It would be nice if the import would force dummy to use the same memory as XRFTEST which is noe a pointer.

I'm not sure if the following is a clue to make it easier, but creating a common block pointing to a big chuck of memory and doing equivalence was much easier because I didn't have to do any assignments, just declarations. Also, I didn't require any code change to my legacy code. The worst case for me is to have to create a parser to format all of the legacy code that I have.

Perhaps there is an easier way to map shared memory in FORTRAN?
0 Kudos
anthonyrichards
New Contributor III
1,092 Views
I am trying to understand what you want the C code to do? Please correct me if I am wrong, but it appears to me that you have 'legacy code' which is set up to allow subprograms access to variables held in a common block COMMON /XRFTEST/BYTES(80). The memory required for the common block is allocated in the Fortran code (is it an an .EXE?).
You now want to provide/change the values held in that common block by manipulating a buffer in some C code (to be linked into the Fortran code via a .LIB created from the C code?)
Why not just declare XRFTEST as External in the C code and you can play with it there at your leisure?

#define BUFFLEN 80
extern "C" double XRFTEST_mp_BYTES[BUFFLEN];

0 Kudos
merik
Beginner
1,092 Views

The problem is we use to do shared memory in C using what you have stated above. Here is what we had

__declspec (dllexport) char xrftest[SEG0SIZE]

And then to create a shared memory we would do:

#pragma data_seg()
#pragma comment(linker, "-section:.CDB,rws")

This worked great for accessing variables in C++ using structures and in FORTRAN using common blocks and mapping using the above method.

The problem now is that Vista came along and you can no longer use the above method to create shared memory. This type of shared memory does not work across user sessions. Therfore, now I have to use CreateFileMapping with a security descriptor to create the shared memory.

Using this way to create the shared memory, xrftest is no longer an array. It is just a pointer I get back using MapViewOfFile.

In C++ you can do the following to access the same array

extern "C" __declspec (dllimport) char *xrftest;
struct cdb_xrftest {
unsigned char dum[80];
int ABC;
};
static struct cdb_xrftest &xrftest_ptr = (struct cdb_xrftest &)*xrftest;
#defineABC xrftest_ptr.ABC

Yuo can even do the following:

static struct cdb_xrftest *xrftest_ptr = (struct cdb_xrftest *)xrftest;
#define ABC xrftest_ptr->ABC


How can this be done in FORTRAN? Is there a way to map the xrftest pointer to the common block?


0 Kudos
Reply