We are talking about the following difference:
! print_bool.f90
use iso_c_binding, only: c_bool
print *, storage_size(.true._4), storage_size(.true._c_bool)
end
$ gfortran -Wall print_bool.f90
$ ./a.out
32 8
In C the size of the type _Bool
(C99) or bool
(since C23) is implementation-specific, but appears to be one byte in most compilers, just like the one above. In Fortran, the standard requires the default logical has the same storage size as the default integer
and real
types.
I couldn’t find anything like this in gcc or clang (unless you modify the compiler yourself…) at least, which leaves only the wrapper option. You can choose between bool
or an integer type:
subroutine print_bool(b)
logical, intent(in) :: b
print *, b
end subroutine
! Pass C _Bool as logical
subroutine f_print_bool_v1(b) bind(c)
use, intrinsic :: iso_c_binding, only: c_bool
integer, parameter :: lk = kind(.true.)
logical(c_bool), value :: b
call print_bool(logical(b,kind=lk))
end subroutine
! Use integer as logical
subroutine f_print_bool_v2(b) bind(c)
use, intrinsic :: iso_c_binding, only: c_int
integer, parameter :: FBOOL = c_int
integer(FBOOL), value :: b
logical :: fb
fb = b /= 0 ! 0 is false, anything else is true
call print_bool(fb)
end subroutine
The C binding would then look like this:
// f_binding.h
#include <stdbool.h>
typedef int FBOOL;
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
extern void f_print_bool_v1(bool b);
extern void f_print_bool_v2(FBOOL b);
The integer approach has the downside it can be misused:
FBOOL myFunc(int a)
{
if (a < 3) {
return FALSE;
} else if (a > 3) {
return TRUE;
} else {
return 2; // !???
}
}
Nonetheless some API’s such as Win32 rely heavily on this approach. The difference between the Windows-specific BOOL
and the standard C/C++ bool
is a known source of problems according to this answer: windows - When should BOOL and bool be used in C++? - Stack Overflow
Trying to pass a 4-byte logical from C to Fortran, without using bind(c)
is difficult. Compilers are allowed to use different internal representations for .true.
and .false.
, meaning your C library would be tied to a particular Fortran compiler:
#if defined(GFORTRAN)
typedef int_fast32_t logical;
// We cast the `bool` to the wider integer type
#define logical(b) ((logical) b)
#elif defined(IFORT)
typedef int32_t logical;
// Convert to representation used in Compaq Visual Fortran
#define logical(b) ((b) ? -1 : 0)
#else
#error "Unsupported Fortran compiler"
#endif
#define FNAME(name) name##_
#define print_bool FNAME(print_bool)
// Compiler-specific (!) function prototype
extern void print_bool(logical *b);
void f_print_bool(bool b) {
print_bool(logical(b));
}
To make things worse, the width of the default logical
can change depending on compiler flags:
gfortran
: -fdefault-integer-8
ifort
: -integer-size 16|32|64
, -i2|4|8
nagfor
: -double
, -i8
With the Intel Compiler, the representation can change depending upon the flags -fpscomp [no]logicals
and -standard-semantics
.
Even if you manage to do the preprocessor logic, account for flag-dependent behaviour and name-mangling, you are still left to deal with other dummy arguments (e.g. character variables).