Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Operator interface with integer(c_int) and integer(c_long)

109 views
Skip to first unread message

Thomas Koenig

unread,
Apr 17, 2022, 11:42:18 AM4/17/22
to
The following is slightly simplified.

I have a C functions I want to interface to, which takes a long
argument. I also want to have a wrapper function in Fortran
around it, and operator overloading for it which works for as many
kinds as possible.

So,

use, intrinsic :: iso_c_binding

interface operator(+)
module procedure int_add, long_add
end interface operator(+)

! Hopefully, this will be (almost) elided on a 64-bit system.

recursive function int_add (a, b) result (c)
type(something), intent(in) :: a
integer(c_int), intent(in) :: b
type(something) :: c
integer(c_long) :: long_b
long_b = b
c = long_add (a,long_b)
end function int_add

recursive function long_add (a,b) result(c)
type(something), intent(in) :: a
integer(c_int), intent(in) :: b
type(something) :: c
call some_c_function (a, b, c)
end function long_add

On a 64-bit system with 64-bit longs and 32-bit ints, all is fine.
However, if c_int = c_long, the code is wrong because of an
ambiguous interface for the operator(+).

For c_int == c_long, the additional function is not actually needed,
but I don't see a way to remove it short of all sorts of configure
hackery and/or preprocessing.

Any ideas?

FortranFan

unread,
Apr 17, 2022, 11:50:52 AM4/17/22
to
On Sunday, April 17, 2022 at 11:42:18 AM UTC-4, Thomas Koenig wrote:

> ..
> Any ideas?

The standard intrinsic module "ISO_C_BINDING" also includes the named constants of c_int32_t and c_int64_t (and also c_int16_t and c_int8_t).

Toward generic interfaces these named constants are to be preferred over c_int, c_long, etc. precisely because of the issue encountered in the original post.

Thomas Koenig

unread,
Apr 17, 2022, 11:51:55 AM4/17/22
to
FortranFan <pare...@gmail.com> schrieb:
... which does not help if the binding is specified as "long".

Ron Shepard

unread,
Apr 17, 2022, 12:24:53 PM4/17/22
to
Can you do something like

if ( c_int32_t .eq. c_long ) then
...
elseif ( c_int64_t .eq. c_long ) then
...
else
...

to straighten out the possibilities? The if statements themselves will
probably be evaluated at compile time and the dead code will be
eliminated, so there should be little or no performance penalty for
doing this.

The overloading on the fortran side depends on the number of integer
kinds that you want to support. If you want to support all integer
kinds, then that itself presents problems with portability. The
INTEGER_KINDS() array gives you the number and value of the kinds that
are supported, but then what? How do you write portable code with just
that information? A preprocessor is one approach, but the language
itself doesn't do much beyond that array to help.

$.02 -Ron Shepard

FortranFan

unread,
Apr 17, 2022, 12:27:30 PM4/17/22
to
On Sunday, April 17, 2022 at 11:51:55 AM UTC-4, Thomas Koenig wrote:
>..
> ... which does not help if the binding is specified as "long".

It will be better to illustrate the approach I recommend to teams I work with in industry with a fair bit of success using an minimal example.

Toward this, it will be useful to know about these existing C functions.

Since C does NOT permit function overloading, I presume they might along the following lines:

typedef struct t {
int x;
} T;
T foo(T*, int*);
T bar(T*, long*);

Is that a fair assumption?

FortranFan

unread,
Apr 17, 2022, 12:45:58 PM4/17/22
to
Shown below is what I mean: @Thomas Koening and anyone else interested can try out to see my recommendation:

1. C code, file c.c
#include <stdio.h>
typedef struct t {
int x;
} T;
T foo(T*, int*);
T bar(T*, long*);

T foo(T* a, int* b) {
T r;
r.x = a->x + *b;
printf("In foo: a, b, r = %d, %d, %d\n", a->x, *b, r.x);
return r;
}

T bar(T* a, long* b) {
T r;
r.x = a->x + *b;
printf("In bar: a, b, r = %d, %d, %d\n", a->x, *b, r.x);
return r;
}

2. Fortran code, file p.f90
module m
use, intrinsic :: iso_c_binding, only : c_int, c_int32_t, c_int64_t
type, bind(C) :: t
integer(c_int) :: x
end type

interface
function foo( a, b ) result(r) bind(C, name="foo")
import :: c_int32_t, t
! Argument list
type(t), intent(in) :: a
integer(c_int32_t), intent(in) :: b
! Function result
type(t) :: r
end function
function bar( a, b ) result(r) bind(C, name="bar")
import :: c_int64_t, t
! Argument list
type(t), intent(in) :: a
integer(c_int64_t), intent(in) :: b
! Function result
type(t) :: r
end function
end interface
interface operator(+)
procedure foo;
procedure bar;
end interface
end module
use m
use, intrinsic :: iso_c_binding, only : c_long
type(t) :: u
integer(c_long) :: v
u%x = 41 ; v = 1
u = u + v
print *, "u%x = ", u%x, "; expected is 42."
end

3. Program response using gfortran on 32-bit Windows OS:
c:\temp>gfortran -c c.c

C:\temp>gfortran -c p.f90

C:\temp>gfortran p.o c.o -o p.exe

C:\temp>p.exe
In foo: a, b, r = 41, 1, 42
u%x = 42 ; expected is 42.

Thomas Koenig

unread,
Apr 17, 2022, 1:16:23 PM4/17/22
to
FortranFan <pare...@gmail.com> schrieb:
> On Sunday, April 17, 2022 at 11:51:55 AM UTC-4, Thomas Koenig wrote:
>>..
>> ... which does not help if the binding is specified as "long".
>
> Toward this, it will be useful to know about these existing C functions.

The prototype is (paraphrased)

foo_add (foo_type *, foo_type * const, long);

where foo_add is from an external library I have no control over.

On the Fortran side, I would like to do

type(foo_type) :: a, b
b = a + 1

where addition is meaningful for foo_type, and I would also like
to be able to do have another kind of integer, so this can be used
using operators following normal Fortran rules (as far as possible).

Thomas Koenig

unread,
Apr 17, 2022, 1:17:58 PM4/17/22
to
Ron Shepard <nos...@nowhere.org> schrieb:
> On 4/17/22 10:51 AM, Thomas Koenig wrote:
>> FortranFan <pare...@gmail.com> schrieb:
>>> On Sunday, April 17, 2022 at 11:42:18 AM UTC-4, Thomas Koenig wrote:
>>>
>>>> ..
>>>> Any ideas?
>>>
>>> The standard intrinsic module "ISO_C_BINDING" also includes the named constants of c_int32_t and c_int64_t (and also c_int16_t and c_int8_t).
>>>
>>> Toward generic interfaces these named constants are to be preferred over c_int, c_long, etc. precisely because of the issue encountered in the original post.
>>
>> ... which does not help if the binding is specified as "long".
>
> Can you do something like
>
> if ( c_int32_t .eq. c_long ) then
> ...
> elseif ( c_int64_t .eq. c_long ) then
> ...
> else

Unfortunately, this does not work for defining an interface for
operator(+).

FortranFan

unread,
Apr 17, 2022, 1:28:48 PM4/17/22
to
On Sunday, April 17, 2022 at 1:16:23 PM UTC-4, Thomas Koenig wrote:
> FortranFan schrieb:
Perhaps you will see my minimal but fully working example upthread:
https://groups.google.com/g/comp.lang.fortran/c/JgeUMfTHIuA/m/so1jiycPEgAJ

Give it a try on 64-bit Linux where, if I understood correctly, GCC uses 64-bit integer for long.; I don't have access to Linux. Then compare the program response relative to what's shown below.

Also, the above example hopefully also shows how to address the need, "I would also like to be able to do have another kind of integer, so this can be used > using operators following normal Fortran rules (as far as possible)."

Thomas Koenig

unread,
Apr 17, 2022, 1:43:36 PM4/17/22
to
FortranFan <pare...@gmail.com> schrieb:
> On Sunday, April 17, 2022 at 1:16:23 PM UTC-4, Thomas Koenig wrote:
>> FortranFan schrieb:
>> > On Sunday, April 17, 2022 at 11:51:55 AM UTC-4, Thomas Koenig wrote:
>> >>..
>> >> ... which does not help if the binding is specified as "long".
>> >
>> > Toward this, it will be useful to know about these existing C functions.
>> The prototype is (paraphrased)
>>
>> foo_add (foo_type *, foo_type * const, long);
>>
>> where foo_add is from an external library I have no control over.
>>
>> On the Fortran side, I would like to do
>>
>> type(foo_type) :: a, b
>> b = a + 1
>>
>> where addition is meaningful for foo_type, and I would also like
>> to be able to do have another kind of integer, so this can be used
>> using operators following normal Fortran rules (as far as possible).
>
> Perhaps you will see my minimal but fully working example upthread:
> https://groups.google.com/g/comp.lang.fortran/c/JgeUMfTHIuA/m/so1jiycPEgAJ

Unfortunately, this does not work for what I had in mind (which I
also explained upthread). The problem I am trying to address is
on the Fortran side, in the interface block, when c_int == c_long.

So, I guess there is no way to do what I need in the Fortran language,
and configure / preprocessor hackery it shall be.

FortranFan

unread,
Apr 17, 2022, 2:19:41 PM4/17/22
to
On Sunday, April 17, 2022 at 1:43:36 PM UTC-4, Thomas Koenig wrote:

> ..
> Unfortunately, this does not work for what I had in mind (which I
> also explained upthread). The problem I am trying to address is
> on the Fortran side, in the interface block, when c_int == c_long.
>
> So, I guess there is no way to do what I need in the Fortran language,
> and configure / preprocessor hackery it shall be.

Another thing I would try is c_size_t instead of c_int / c_long kinds in the Fortran INTERFACE construct and with a single function definition in that construct:

module m
use, intrinsic :: iso_c_binding, only : c_int, c_size_t
type, bind(C) :: t
integer(c_int) :: x
end type
interface
function foo( a, b ) result(r) bind(C, name="foo")
import :: c_size_t, t
! Argument list
type(t), intent(in) :: a
integer(c_size_t), intent(in) :: b
! Function result
type(t) :: r
end function
end interface
interface operator(+)
procedure foo
end interface
end module
use, intrinsic :: iso_c_binding, only : clong => c_size_t
use m
type(t) :: u
integer(clong) :: v
u%x = 41 ; v = 1
u = u + v
print *, "u%x = ", u%x, "; expected is 42."
end

When on Linux platforms where (c_int == c_long) would be equivalent I think to -m32 option: The above setup with c_size_t should then work on both such platforms where (c_int == c_long) as well as "On a 64-bit system with 64-bit longs and 32-bit ints".

C:\temp>x86_64-w64-mingw32-gfortran.exe -m32 -c c.c

C:\temp>x86_64-w64-mingw32-gfortran.exe -m32 -c p.f90

C:\temp>x86_64-w64-mingw32-gfortran.exe -m32 p.o c.o -o p.exe

David Ruben

unread,
Apr 18, 2022, 12:21:35 AM4/18/22
to
On Sun, 17 Apr 2022 17:43:33 -0000 (UTC), Thomas Koenig wrote:

> Unfortunately, this does not work for what I had in mind (which I also
> explained upthread). The problem I am trying to address is on the
> Fortran side, in the interface block, when c_int == c_long.
>
> So, I guess there is no way to do what I need in the Fortran language,
> and configure / preprocessor hackery it shall be.

I haven't been following this discussion all that closely, but I think
that FortranFan had the right idea. The compiler doesn't choose a
particular module procedure on the basis of the text strings "c_int" or
"c_long". It chooses on the basis of the kind values assigned to those
strings. Therefore, if you write module procedures using all combinations
of "c_int32_t" and "c_int64_t", one and only one of those procedures will
be chosen regardless of whether or not c_int == c_long. Doesn't this
solve your problem?

D.R.

Thomas Koenig

unread,
Apr 18, 2022, 1:52:52 AM4/18/22
to
David Ruben <ru...@fbml-cmr.mit.edu> schrieb:
I can then chose a module procedure, but it would silently generate
wrong code if "long" is 32 bits and I am trying to pass a 64-bit
integer from Fortran.

As for a runtime check... either such a program should compile, or not.

Thomas Koenig

unread,
Apr 18, 2022, 4:08:05 AM4/18/22
to
FortranFan <pare...@gmail.com> schrieb:
> On Sunday, April 17, 2022 at 1:43:36 PM UTC-4, Thomas Koenig wrote:
>
>> ..
>> Unfortunately, this does not work for what I had in mind (which I
>> also explained upthread). The problem I am trying to address is
>> on the Fortran side, in the interface block, when c_int == c_long.
>>
>> So, I guess there is no way to do what I need in the Fortran language,
>> and configure / preprocessor hackery it shall be.
>
> Another thing I would try is c_size_t instead of c_int / c_long
> kinds in the Fortran INTERFACE construct and with a single function
> definition in that construct:

This would fail for the code (on the Fortran side) of

a = b + 1

when c_size_t does not match Fortran's standard integer.

FortranFan

unread,
Apr 18, 2022, 8:13:42 AM4/18/22
to
On Monday, April 18, 2022 at 4:08:05 AM UTC-4, Thomas Koenig wrote:
> FortranFan schrieb:
>..
> > Another thing I would try is c_size_t instead of c_int / c_long
> ..
> This would fail for the code (on the Fortran side) of
>
> a = b + 1
>
> when c_size_t does not match Fortran's standard integer.

See the fully worked out example upthread with c_size_t:
https://groups.google.com/g/comp.lang.fortran/c/JgeUMfTHIuA/m/GFMazEQUEgAJ

Note again in the Fortran standard, no interoperability is indicated of a Fortran "standard integer" with C. To state the obvious, what the standard states is that with a companion C processor, objects of certain types and kinds are interoperable. In the case of integer, these of course include kinds such as c_int, c_long, c_int32_t, c_size_t, etc. provided they are consistent with the parameters defined in the companion C processor.

Thus an instruction such as "a = b + 1" should not be expected to be interoperable to begin with.

Whereas "a = b + x" can be when 'a', 'b', and 'x' are interoperable,

Thomas Koenig

unread,
Apr 18, 2022, 9:19:16 AM4/18/22
to
FortranFan <pare...@gmail.com> schrieb:
> On Monday, April 18, 2022 at 4:08:05 AM UTC-4, Thomas Koenig wrote:
>> FortranFan schrieb:
>>..
>> > Another thing I would try is c_size_t instead of c_int / c_long
>> ..
>> This would fail for the code (on the Fortran side) of
>>
>> a = b + 1
>>
>> when c_size_t does not match Fortran's standard integer.
>
> See the fully worked out example upthread with c_size_t:
> https://groups.google.com/g/comp.lang.fortran/c/JgeUMfTHIuA/m/GFMazEQUEgAJ

I have already explained that size_t is not good enough for what I have
in mind.

> Note again in the Fortran standard, no interoperability is
> indicated of a Fortran "standard integer" with C. To state the
> obvious, what the standard states is that with a companion C
> processor, objects of certain types and kinds are interoperable.

>In the case of integer, these of course include kinds such as c_int,
> c_long, c_int32_t, c_size_t, etc. provided they are consistent
> with the parameters defined in the companion C processor.

Sure.

And if c_int happens to be the same kind as that of the default
integer, then the code will work. If not, it will fail.

> Thus an instruction such as "a = b + 1" should not be expected
> to be interoperable to begin with.

There are two possibilities: The compiler accepts the code
(if c_int == kind(1) or c_long == 1), then all's well. Or, in
the unexpeced case that the kind numbers do not match, there is
a compilation error, and the user can turn to the documentation
and can then insert 1_c_int or 1_c_long, as required.

Of course, a user using c_long will be restricted by the semantics
of C's long data type.

FortranFan

unread,
Apr 18, 2022, 10:02:20 AM4/18/22
to
On Monday, April 18, 2022 at 9:19:16 AM UTC-4, Thomas Koenig wrote:

> ..
> I have already explained that size_t is not good enough for what I have
> in mind.
> ..
> Of course, a user using c_long will be restricted by the semantics
> of C's long data type.

Clearly code that uses facilities with standard C and also follows *good practices* will face little to no issues of the kind mentioned in the original post when interoperating with Fortran, here it is assumed the types involved are interoperable as indicated in the Fortran standard.

The good practices aspect involves using types in C that are unambiguous or suffer far less from ambiguity. With signed integers in C, this corresponds to int32_t and int64_t in an overwhelming majority of the use cases. For such integer kinds, the Fortran options for interoperation have already been mentioned upthread.

The `long` data type in C has long been a cause of confusion with many unsuspecting coders, particularly with the upgrade to 64-bit Linux platforms. Our approach is generally to refactor the C code such that the use of this data type is suitably replaced with a clear, "modern" option.


Thomas Koenig

unread,
Apr 18, 2022, 11:43:07 AM4/18/22
to
FortranFan <pare...@gmail.com> schrieb:

> The `long` data type in C has long been a cause of confusion
> with many unsuspecting coders, particularly with the upgrade to
> 64-bit Linux platforms. Our approach is generally to refactor the
> C code such that the use of this data type is suitably replaced
> with a clear, "modern" option. > >

Not possible in this case - the type is fixed (and for good reason,
too).

Ev. Drikos

unread,
Apr 18, 2022, 12:05:45 PM4/18/22
to
On 18/04/2022 08:52, Thomas Koenig wrote:
> I have a C functions I want to interface to, which takes a long
> argument. I also want to have a wrapper function in Fortran
> around it, and operator overloading for it which works for as many
> kinds as possible.

...

> I can then chose a module procedure, but it would silently generate
> wrong code if "long" is 32 bits and I am trying to pass a 64-bit
> integer from Fortran.

It's the silent generation of wrong code that afraid me in a
recent discussion about some user defined interfaces:
https://groups.google.com/g/comp.lang.fortran/c/Akmj_d0opWE/m/ozWk72OJAgAJ



...
> Any ideas?
>

Yes :-)

I've just used the c preprocessor below to avoid making the reply
longer. One doesn't have to use fixed form code as in the example below.

Also, my solution (hackery) uses conditional compilation in module M,
which (I guess) isn't a necessity as well.

Finally, it doesn't seem to be a simple & straightforward solution.

------------------------------------------------------------

macbook:ops suser$ rm -rf *.mod *.smod *.o p.exe
macbook:ops suser$ gfc -c -fopenmp m.f90 -o m.o
macbook:ops suser$ gfc -c sm.F -DN=16 -DS16 -o sm16.o
macbook:ops suser$ gfc -c sm.F -DN=32 -DS32 -o sm32.o
macbook:ops suser$ gfc -c sm.F -DN=64 -DS64 -o sm64.o
macbook:ops suser$ gfc -c sm.F -DN=128 -DS128 -o sm128.o
macbook:ops suser$ gfc -c p.f90 -o p.o
macbook:ops suser$ gfc m.o sm16.o sm32.o sm64.o sm128.o p.o -o p.exe
macbook:ops suser$ ./p.exe
macbook:ops suser$
macbook:ops suser$ rm -rf *.mod *.smod *.o p.exe
macbook:ops suser$ gfc -c -m32 m.f90 -o m.o
macbook:ops suser$ gfc -c -m32 sm.F -DN=16 -DS16 -o sm16.o
macbook:ops suser$ gfc -c -m32 sm.F -DN=32 -DS32 -o sm32.o
macbook:ops suser$ gfc -c -m32 sm.F -DN=64 -DS64 -o sm64.o
macbook:ops suser$ gfc -c -m32 p.f90 -o p.o
macbook:ops suser$ gfc -m32 m.o sm16.o sm32.o sm64.o p.o -o p.exe
ld: warning: The i386 architecture is deprecated for macOS (remove from
the Xcode build setting: ARCHS)
macbook:ops suser$ ./p.exe


macbook:ops suser$ cat m.f90
module m
use, intrinsic :: iso_c_binding

type :: something
real :: x, y
end type something

interface operator(+)
module procedure int16_add, int32_add, int64_add
!$ module procedure int128_add
end interface operator(+)

interface

module function int16_add (a, b) result (c)
type(something), intent(in) :: a
integer(c_int16_t), intent(in) :: b
type(something) :: c
end function int16_add

module function int32_add (a, b) result (c)
type(something), intent(in) :: a
integer(c_int32_t), intent(in) :: b
type(something) :: c
end function int32_add

module function int64_add (a,b) result(c)
type(something), intent(in) :: a
integer(c_int64_t), intent(in) :: b
type(something) :: c
end function int64_add

!$ module function int128_add (a, b) result (c)
!$ type(something), intent(in) :: a
!$ integer(c_int128_t), intent(in) :: b
!$ type(something) :: c
!$ end function int128_add

end interface

end module m

macbook:ops suser$


macbook:ops suser$ cat sm.F
submodule (m) sm N
use, intrinsic :: iso_c_binding

contains

module procedure int N _add
!type(something), intent(in) :: a
!integer(c_int S _t), intent(in) :: b
!type(something) :: c

integer(c_long) bb
bb=b !then check if ( bb /= b )
!call some_c_function (a, bb, c)
end procedure int N _add

end submodule sm N


macbook:ops suser$ cat p.f90
program test
use iso_fortran_env
use, intrinsic :: iso_c_binding
use m
implicit none


integer(c_int16_t) :: c_int16=16;
integer(c_int32_t) :: c_int32=32;
integer(c_int64_t) :: c_int64=64;
integer(c_long) :: a_long =64;

type (something) st;

st = st + 1;
st = st + c_int16;
st = st + c_int32;
st = st + c_int64;
st = st + a_long;

end

Thomas Koenig

unread,
Apr 18, 2022, 12:22:47 PM4/18/22
to
Thomas Koenig <tko...@netcologne.de> schrieb:
Here's what I came up with.

I finally got around to setting size of int and long as preprocessor
variables, -DSIZEOF_INT=4 -DSIZEOF_LONG=8 in a to-be-preprocessed
Fortran file, and then use

interface operator(+)
module procedure add_long, add_long2
#if SIZEOF_INT < SIZEOF_LONG
module procedure add_int, add_int2
#endif
end interface operator(+)

(where add_long and add_long2 have are for a + i and i + a,
respectively) and the same for the procedures themselves.

A bit hackish, I admit, but it works well enough.

What could actually solve this is some sort of "weak" interface
which says "If I specified the same interface before, please
disregard this one".

interface operator(+)
modlue procedure add_long, add_long2
module procedure, weak: add_int, add_int2
end interface operator(+)

which could sort out this kind of problem. I'm not sure that this
is important enough, and that this idea is well-enough thought out,
to warrant bothering the comittee about :-)

gah4

unread,
Apr 18, 2022, 2:43:03 PM4/18/22
to
On Monday, April 18, 2022 at 9:22:47 AM UTC-7, Thomas Koenig wrote:

(snip)

> What could actually solve this is some sort of "weak" interface
> which says "If I specified the same interface before, please
> disregard this one".

Could be nice for all declarations. If I dimension an array twice,
to the same dimension, ignore the second one. Most languages
don't do that.

C #include files often include a test and #ifdef to avoid the problem
of declaring the same thing twice.

jfh

unread,
Apr 19, 2022, 1:28:16 AM4/19/22
to
I would welcome weak interfaces - they would make it easier to write programs to run with both gfortran which has 4 real kinds (precisions 6,15,18,33) in my system and ifort which has 3 (omitting 18).
0 new messages