gfortran: pass logical argument to Fortran function from C
Asked Answered
S

2

7

What argument type should I use in C when calling a Fortran function that takes logical arguments, specifically with gfortran? Where is this documented for gfortran?

Here's an example program that doesn't compile without warnings:

Contents of one.f:

      subroutine proc1(x)
      logical x
      end

Contents of main.c:

void proc1_(_Bool *x);

int main() {
    _Bool x;

    proc1_(&x);

    return 0;
}

If I compile using GCC as follows, with LTO enabled, I get a warning about mismatching function prototypes:

gfortran -flto -c one.f
gcc -flto -c main.c
gcc -flto main.o one.o

The warning I get:

main.c:2:6: warning: type of 'proc1_' does not match original declaration [-Wlto-type-mismatch]
    2 | void proc1_(_Bool *x);
      |      ^
one.f:2:22: note: 'proc1' was previously declared here
    2 |       subroutine proc1(x)
      |                      ^
one.f:2:22: note: code may be misoptimized unless '-fno-strict-aliasing' is used

Note that enabling LTO allows the linker to verify that argument types match between prototypes. Using LTO is unfortunately not our choice. CRAN requires the submitted code to compile without these warnings with LTO enabled.

I only see problems when trying to use logical arguments. real, integer and character are all fine.

gfortran can be asked to produce C prototypes, and this is the output it gives me:

gfortran -flto -fc-prototypes-external -c one.f
void proc1_ (int_fast32_t *x);

Using int_fast32_t in the C prototype doesn't work either. No type that I tried did, neither int, nor _Bool. Usually, when there is a type mismatch between prototypes, the error message mentions what the type should be—but not in this case.

How can I find what is the correct type to use?

Shrewd answered 2/11, 2021 at 18:50 Comment(0)
B
3

The correct and guaranteed to be portable solution is, as explained in the answer by Vladimir F, to create a Fortran wrapper routine that uses ISO_C_BINDING. This wrapper can also take the opportunity to make a more idiomatic C interface, e.g. using the value specifier to pass scalars by value.

However, for the quick and dirty solution that works on GFortran WITHOUT LTO (and somewhat likely on other compilers, but no guarantees), see https://gcc.gnu.org/onlinedocs/gfortran/Internal-representation-of-LOGICAL-variables.html#Internal-representation-of-LOGICAL-variables . That is, you can pass a C integer variable of the appropriate size containing 1 for true and 0 for false. Appropriate size here meaning that unless you have compiled your Fortran code with -fdefault-integer-8 or such compile options, the GFortran default kind logical will be 4 bytes, so a plain C int should be good (or int32_t if you really want to be sure, though I don't think GFortran supports any targets where the C int is not 32 bits).

The reason this doesn't work with LTO is that while the above works, in the bowels of GCC the Fortran LOGICAL variables are almost the same as integers, but not quite. So in practice they are special integer variables with max value 1 and min value 0 even though they take up more space (as specified by their kind parameter). So this kind of type mismatch is likely what it complains about. Unfortunately no solution to this one, except the above correct solution via ISO_C_BINDING.

Brickbat answered 3/11, 2021 at 6:37 Comment(3)
Did you check that it does not produce the warning? Because Szabolcs writes that they tried int.Cigar
@VladimirF I added a section explaining why it doesn't work with LTO.Brickbat
Thanks, this explains why there is no solution for getting rid of the warning. But it also shows that it is safe to use an integer type of the correct size, ensure that the values are only 0 or 1, and ignore the warning. (Sadly, we are required to eliminate the warning, but at least we are clear about the options now.)Shrewd
A
9

For real modern C-Fortran interoperability you should use the types (kinds) supplied by the iso_c_binding module and make your Fortran procedure bind(c). That way you can use logical(c_bool).

In the old style the best thing is to work with integers and pass an int and only correct from integer to logical inside Fortran. Old C did not have any bool, it was added later.

With minimal changes:

      subroutine proc1(x)
      use iso_c_binding
      logical(c_bool) x
      end
#include <stdbool.h>
void proc1_(bool *x);

int main() {
    bool x;

    proc1_(&x);

    return 0;
}
> gfortran -flto -c one.f
> gcc -flto -c main.c
> gcc -flto main.o one.o

issues no warning on my Linux and GCC 7 and 10.

Or after further changes:

      subroutine proc1(x) bind(C, name="proc1")
      use iso_c_binding
      logical(c_bool), value :: x
      end
#include <stdbool.h>
void proc1(bool x);

int main() {
    bool x;

    proc1(x);

    return 0;
}

The change to pass-by-value of course only when it is indeed just an input parameter.

Algol answered 2/11, 2021 at 19:19 Comment(6)
I haven't tried to compile and run the code, but you may need to have logical, value :: x and add bind(c,name="proc1")'. You then can simply do proc1(x)` in the c code.Sandblind
Thanks for the answer Vladimir. Unfortunately, we are interfacing with old Fortran code (mostly F77). In fact it's a part of ARPACK. Are you aware of a solution that does not require extensive modification to the Fortran sources?Shrewd
@Shrewd No, but you can always write an intermediate Fortran procedure that calls the old Fortran procedure.Cigar
@Sandblind I did mention bind(C) but I wanted to demonstrate the minimal required changes which are in the datatype.Cigar
@VladimirF, Yes, I understand that you were trying to keep it brief. My comment was only meant to augment yours in that one of the biggest issues people new to bind(c) have is the omission of the value attribute.Sandblind
Thanks again for the answer Vladimir, it has been very useful. I accepted the other answer because it makes it clear what goes wrong and why, why this is not fixable from the C side, and when it is safe to ignore the warning.Shrewd
B
3

The correct and guaranteed to be portable solution is, as explained in the answer by Vladimir F, to create a Fortran wrapper routine that uses ISO_C_BINDING. This wrapper can also take the opportunity to make a more idiomatic C interface, e.g. using the value specifier to pass scalars by value.

However, for the quick and dirty solution that works on GFortran WITHOUT LTO (and somewhat likely on other compilers, but no guarantees), see https://gcc.gnu.org/onlinedocs/gfortran/Internal-representation-of-LOGICAL-variables.html#Internal-representation-of-LOGICAL-variables . That is, you can pass a C integer variable of the appropriate size containing 1 for true and 0 for false. Appropriate size here meaning that unless you have compiled your Fortran code with -fdefault-integer-8 or such compile options, the GFortran default kind logical will be 4 bytes, so a plain C int should be good (or int32_t if you really want to be sure, though I don't think GFortran supports any targets where the C int is not 32 bits).

The reason this doesn't work with LTO is that while the above works, in the bowels of GCC the Fortran LOGICAL variables are almost the same as integers, but not quite. So in practice they are special integer variables with max value 1 and min value 0 even though they take up more space (as specified by their kind parameter). So this kind of type mismatch is likely what it complains about. Unfortunately no solution to this one, except the above correct solution via ISO_C_BINDING.

Brickbat answered 3/11, 2021 at 6:37 Comment(3)
Did you check that it does not produce the warning? Because Szabolcs writes that they tried int.Cigar
@VladimirF I added a section explaining why it doesn't work with LTO.Brickbat
Thanks, this explains why there is no solution for getting rid of the warning. But it also shows that it is safe to use an integer type of the correct size, ensure that the values are only 0 or 1, and ignore the warning. (Sadly, we are required to eliminate the warning, but at least we are clear about the options now.)Shrewd

© 2022 - 2024 — McMap. All rights reserved.