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

Using the 'value' attribute

Simon_Geard
New Contributor I
1,166 Views
module mymod
    implicit none
    type my_t
	integer :: a = 3
    end type my_t
	
contains
    subroutine by_value(i,m) bind(c,name='BY_VAL')
	use iso_c_binding
	integer(c_int), intent(in), value :: i
	type(my_t), intent(out) :: m
	m%a = i
    end subroutine by_value
		
end module mymod

program test
	use mymod
	implicit none
	integer :: i
	type(my_t) :: m

	i = 5
	
	call by_val(i,m)
	print *, m%a
	
	call by_value(i,m)
	print *, m%a
	
end program test

 

> ifort tc.f90
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.2.180 Build 20160204
Copyright (C) 1985-2016 Intel Corporation.  All rights reserved.

 

Microsoft (R) Incremental Linker Version 11.00.60610.1
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:tc.exe
-subsystem:console
tc.obj

> tc
  1060107240
           5

The above code shows the inconsistency. If I remove the output argument 'm' in the calls then both output values are '5' as I'd expect. I get the same result with Version 14.0.1.139 Build 20131008.

 

0 Kudos
22 Replies
Arjen_Markus
Honored Contributor I
1,011 Views

I do not think there is an inconsistency here: the routine BY_VAL has no explicit interface, so the compiler assumes the FORTRAN 77 rules. The fact that there is an alternative, by_value, makes no difference. The compiler has to apply the old rules, so both arguments are passed by reference.

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

Thanks, I understand that. The corollary is that to have one routine that is callable from both C and Fortran (and uses the 'value' attribute) the routine name and bind(c) name need to be different so that the Fortran calls use the non-bind(c) name and therefore get the correct interface.

 

0 Kudos
Arjen_Markus
Honored Contributor I
1,011 Views

Not quite - if you specify bind(c) and do not give a name, a C routine with exactly that name is simply hidden and the Fortran interface is used instead (if you may express it like that)

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

Have just tried it but it causes linkage problems. A typical example is

module x

contains

    subroutine get_panel_geometry(ind, pgeom) bind(c)
        use iso_c_binding
        implicit none
        integer(c_int), intent(in), value    :: ind
        type(SrLRKPanelGeoData), intent(out) :: pgeom
    end subroutine get_panel_geometry

end module x

Without an explicit name the dll build fails with unresolved external symbol GET_PANEL_GEOMETRY - another hole world of pain!

0 Kudos
Steven_L_Intel1
Employee
1,011 Views

The error in the first case was that you called the external name rather than the Fortran name. If you change anything about the routine from the Fortran defaults, you need to have the explicit interface visible to the caller. There's no problem having a routine callable from both Fortran and C if you do it consistently.

In post 5 it would seem that you're not USEing the module when you call the routine. This is needed not only for the name but for pass-by-value. Why are you deliberately trying to hide the interface from the caller?

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

I'm not meaning to mislead, I'm just trying to extract the salient features from a large code-base. When the procedure 'get_panel_geometry' is called in the Fortran code there is a 'use x' so that it gets the right interface, except that it doesn't.

If I undestand Arjen correctly this is because the bind(c,name='GET_PANEL_GEOMETRY') decoration causes the compiler to use the Fortran77 rules so that Fortran code calling 'get_panel_geometry' effectively has the interace (provided by 'use x') masked. The result is that at runtime the value of 'ind' is incorrect when the routine is called from Fortran but correct when it is called from C++.

If I change the Fortran name to, for example, get_panel_geometry_f and leave the bind(c) as in the above paragraph, then change the fortran calls to get_panel_geometry_f then the code behaves as expected. If I try just using bind(c) then the build fails because GET_PANEL_GEOMETRY (which is in the .def file) can no longer be found.
 

0 Kudos
FortranFan
Honored Contributor II
1,011 Views

Simon Geard wrote:

I'm not meaning to mislead, ..

Not sure what exactly the issue is: why not have the Fortran calling procedures use the Fortran name (by_value in your original post) while exposing the interface as Steve pointing (i.e.,  "use"ing the module that has the procedure interface on the Fortran calling side); and have the C functions use the name specified in the binding (BY_VAL per your original post).

What doesn't make sense in your original post is the 'call by_val(i,m)' statement your Fortran main program.

By the way, if you're going to use some C interoperability features, then why not go the full distance?  That is, apply bind(C) on all interoperation entities; for the code in the original post, it would mean bind(C) attribute on the derived type (my_t) as well.  For simple types with integer components, you may feel this adds little but it will help greatly in the case of more complicated data structures; plus bind(C) should help with data alignment consistency with the companion C processor as well.

0 Kudos
Steven_L_Intel1
Employee
1,011 Views

When you use NAME= with an uppercase name, what you do is give the routine the same global name the compiler would normally give it on Windows (and by "the compiler" I mean Intel Fortran here - other compilers might have different defaults.)  Fortran 77 has nothing to do with it. But because you've changed the interface to the routine, that change isn't visible when you call without the interface, so you get wrong results. It's really the same issue as any of the other cases where Fortran requires an explicit interface.

The bottom line is that if you change a routine's name or interface or calling convention from the compiler default, you have to make sure that all the callers of that routine know about the changes. The recommended method is the explicit interface, and since you're putting these routines in a module, adding a USE of the module is the way to do it.

0 Kudos
FortranFan
Honored Contributor II
1,011 Views

Steve Lionel (Intel) wrote:

When you use NAME= with an uppercase name, ..

The bottom line is that if you change a routine's name or interface or calling convention from the compiler default, you have to make sure that all the callers of that routine know about the changes. The recommended method is the explicit interface, and since you're putting these routines in a module, adding a USE of the module is the way to do it.

Oh, is that what the confusion is for OP?

The instructions in the second paragraph should clear things up:

module mymod

   use iso_c_binding, only : c_int

   implicit none

   type, bind(c) :: my_t
      integer(c_int) :: a = 3
   end type my_t

contains

   subroutine by_val(i,m) bind(C, name='BY_VAL')

      integer(c_int), intent(in), value :: i
      type(my_t), intent(out) :: m

      m%a = i

      return

   end subroutine by_val

end module mymod
program test

   use mymod, only : my_t, by_val ! Fortran names are case insensitive, so by_val, BY_VAL
                                  ! makes no difference

   implicit none

   integer :: i
   type(my_t) :: m

   i = 5

   call by_val(i, m)
   print *, "m%a = ", m%a

   stop

end program test
 m%a =  5
Press any key to continue . . .
#include <stdio.h>

typedef struct {
   int a;
} c_t;

extern void BY_VAL(int, c_t *);

int main()
{

   int i;
   c_t m;

   i = 5;
   BY_VAL(i, &m);
   printf("i = %d, m.a = %d", i, m.a);

   return(0);
}

Upon execution,

i = 5, m.a = 5

 

0 Kudos
Steven_L_Intel1
Employee
1,011 Views

I would recommend against using NAME= with an uppercase name - that makes it too easy to use the routine without the interface.

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

All types that are interoperable in our code do have the bind(c) attribute.

The reason I raised this was that I was trying to use an existing procedure and it failed. The existing code snippet:

module x

contains

    subroutine get_panel_geometry(ind, pgeom) bind(c,name='GET_PANEL_GEOMETRY')
        use iso_c_binding
        use pdbtype
        implicit none
        integer(c_int), intent(in), value    :: ind
        type(SrLRKPanelGeoData), intent(out) :: pgeom
    end subroutine get_panel_geometry

end module x

Then there is the corresponding C prototype:

    void GET_PANEL_GEOMETRY(int, SrLRKPanelGeoData*);

and some calls, e.g.

	SrLRKPanelGeoData lgeom;
	GET_PANEL_GEOMETRY(modelIdx, &lgeom);

Calls to this procedure made from the C++ code work as expected.

Now I want to call the same code from Fortran, so I add a 'use x' to the calling code and some calls, e.g.

use x
...
call get_panel_geometry(panelID, pgeom)

but when I run it it doesn't work - as illustrated in post#1. Thus I can't use the same name for both the C and Fortran code and a solution is to change the Fortran name to be different from the C name. If it is possible to use the same name for both C and Fortran calls I'd like to know how because none of the replies in this thread so far have explained that to me.

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

Hmm. Unfortunately our house style is to make all C++ callable Fortran have uppercase names so that it is more obvious in the C++ code that we're calling into the Fortran subsystem (our C++ procedures are never all uppercase).

0 Kudos
Steven_L_Intel1
Employee
1,011 Views

What you show should work, as long as the interface is visible. What you did in post 1 was call a different name.

0 Kudos
FortranFan
Honored Contributor II
1,011 Views

Simon Geard wrote:

.. but when I run it it doesn't work - as illustrated in post#1... If it is possible to use the same name for both C and Fortran calls I'd like to know how because none of the replies in this thread so far have explained that to me.

Re: "none of the replies in this thread so far have explained that to me" - did you see Message #10?

What you're indicating in Message #12 is not the same as in the original post; in Message #12 you say Fortran name and bind(C, name=..) are the same but that is not how it is in the original post.  Show a reproducible example that is exactly consistent with your scenario.  You can take the code below (a minor variant of Message #10, here C binding name is not upper case). note it has no issues  even though it uses " the same name for both C and Fortran calls", so modify it as needed to reproduce the issue you face and post it on this thread: 

module mymod

   use iso_c_binding, only : c_int

   implicit none

   type, bind(c) :: my_t
      integer(c_int) :: a = 3
   end type my_t

contains

   subroutine by_val(i,m) bind(C, name='by_val')

      integer(c_int), intent(in), value :: i
      type(my_t), intent(out) :: m

      m%a = i

      return

   end subroutine by_val

end module mymod
program test

   use mymod, only : my_t, by_val

   implicit none

   integer :: i
   type(my_t) :: m

   i = 5

   call by_val(i, m)
   print *, "From Fortran: i = ", i, ", m%a = ", m%a

   stop

end program test

Upon execution with Intel Fortran,

 From Fortran: i =  5 , m%a =  5
Press any key to continue . . .
#include <stdio.h>

typedef struct {
   int a;
} c_t;

extern void by_val(int, c_t *);

int main()
{

   int i;
   c_t m;

   i = 5;
   by_val(i, &m);
   printf("From C: i = %d, m.a = %d\n", i, m.a);

   return(0);
}

Upon execution,

From C: i = 5, m.a = 5
Press any key to continue . . .

 

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

In my original post I just tried to illustrate the problem I'm having which I stated quite clearly in post #12 - using a different name is the only way I've found so far of making it work. The actual code base is extensive and I couldn't send to Premier Support even if I wanted to.

When I posted #12 your post #10 had not appeared so tomorrow I'll try running it. I did get some wierd picture about the site being lost on the internet so there seems to have been a latency issue. On the face of it your example should fail in exactly the same way as our code does since the structure and use look identical to what I originally tried, but if it does work it should provide a reference to work out why our code doesn't work so TIA.

It may well be that the way we build our dlls is a complication (post #7) but the fact is that what I described in post #12 is what is happening and I have to find either a work around (post #1) or a fix (probably derived from your example).

 

0 Kudos
Steven_L_Intel1
Employee
1,011 Views

Upload a ZIP of a complete example. My guess is there's something you've not shown us that is important.

0 Kudos
Lorri_M_Intel
Employee
1,011 Views

I know you're sending real code to Steve, but let me try and make this even more complicated (sometimes, I can't help myself)

In the very original program, the one in post 1 where the interface is declared to be this:

    subroutine by_value(i,m) bind(c,name='BY_VAL')

The compiler always knows this routine by the name "by_value".   So, when you created the call to by_value, it knew that you meant *this* one, and knew the calling convention was to pass integer arguments by value.

The name "BY_VAL" is the name given to the linker, and is not recognized (if you will) by the compiler as being *this* routine.  

Therefore, when you created the call to BY_VAL, the compiler thought it was some random external Fortran routine, used the rules that apply to calling any external Fortran routine (such as passing arguments by reference), and told the linker it was looking for a routine named "BY_VAL".

At link time, these two things hooked up and that's why you saw the results you saw in post #1.

I also want to second Steve's statement that it should work to have the same name for both C and Fortran, so there is hope.

             --Lorri

0 Kudos
Simon_Geard
New Contributor I
1,011 Views

As promised this morning I added the code provided by FortranFan, ran the program and it worked. So I removed his/her code, changed my code back to the way it was when it wasn't working and hey-presto it's working now. The only thing I can think is that it was some sort of build issue, but none of the code exhibiting the problem is new so I don't really understand that either.

Now it looks as if I've been chasing shadows but they were very real yesterday when the program was crashing. Thanks for your help.

BTW, I had some real problems accessing this topic yesterday and today, screenshots attached.

intel_dev_zone.pngintel_dev_zone_2.png

0 Kudos
andrew_4619
Honored Contributor II
1,011 Views

The Site has had several periods of flakeyness for me in the last couple of weeks.....

0 Kudos
Steven_L_Intel1
Employee
859 Views

Glad to hear it's all sorted out. And yes, the aliens have put us back where we were before - at least that's what they want us to think.

0 Kudos
Reply