Sharing constants between Fortran and C

I can make module variables protected so that they cannot be changed in Fortran outside the module, but if I share them with C, they can be changed in the C code. Is there a workaround? For example, compiling and running

module constants_mod
use iso_c_binding, only: c_double
implicit none
real(kind=c_double), bind(c), protected :: pi, sqrt_2
contains
subroutine set_constants()
pi     = 3.14159265359_c_double
sqrt_2 = 1.41421356237_c_double
end subroutine set_constants
end module constants_mod

program main
use constants_mod, only: set_constants
interface
subroutine print_constants() bind(c)
end subroutine print_constants
end interface
call set_constants()
call print_constants()
call print_constants()
end program main

with

#include <stdio.h>

double pi, sqrt_2;

void print_constants() {
	printf("\npi = %f",pi);
	printf("\nsqrt_2 = %f",sqrt_2);
	pi = pi*2; // bad
}

gives

pi = 3.141593
sqrt_2 = 1.414214
pi = 6.283185
sqrt_2 = 1.414214
1 Like

There is probably no solution on the Fortran side.
On the C side, they should be declared constants:

const double pi, sqrt_2;

I had tried that, but when I change the C code to

#include <stdio.h>

const double pi;
const double sqrt_2;

void print_constants() {
	printf("\npi = %f",pi);
	printf("\nsqrt_2 = %f",sqrt_2);
}

and compile with gfortran and gcc, the resulting program runs on WSL2 but crashes on Windows with

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Reading What kind of optimization does const offer in C/C++? - Stack Overflow it appears that a C compiler has the freedom to optimize away foo in

const int foo = 42;

so I don’t know if such a variable interoperates with

integer(kind=c_int) :: foo

1 Like

Probably the cleanest single sourt of truth solution is the definition via the preprocessor.

It’s good fun to probe the limits of interoperability, but sometimes it’s best to just stick to an in-language concept.

Joining @MarDie’s answer, the canonical way of defining parameters in C is with preprocessor macros. The math constants in <math.h> are one example.

2 Likes

I think that the problem that you have to contend with is your expectation that a restriction that you specify in the Fortran program (protected) should be known to and honored/enforced by the C routine, or vice versa.

Do the C-interoperability rules in the applicable Fortran standard specify such behavior?

If not, that level of cooperation between the Fortran compiler and its companion C compiler may exist with certain compiler pairs, but not so with others. If you do not trust the C compiler and/or the C routine to leave your shared variables unchanged, only make “throwaway” copies available to C; load the copies with values before calling C; make the call, and reload the copies after the C call returns.

2 Likes

The parameter atribute conflicts with the bind(c) attribute. That alone suggests interoperability of constants is problematic - presumably because C’s global constants are basically macros. I would either interop copies of the original constants (as @mecej4 recommended), or just define print_constants with arguments, something like:

program main
use constants_mod!, only: set_constants
implicit none
interface
  subroutine print_constants(pi, sqrt_2) bind(c)
  use iso_c_binding, only: c_double
  real(kind=c_double), value :: pi, sqrt_2
  end subroutine print_constants
end interface
call set_constants()
call print_constants(pi, sqrt_2)
call print_constants(pi, sqrt_2)
end program main

with

#include <stdio.h>

void print_constants(const double pi, const double sqrt_2) {
	printf("\npi = %f",pi);
	printf("\nsqrt_2 = %f",sqrt_2);
	pi = pi*2; // This will cause an error, as it should!
}

If print_constants really needs to modify the constants, it should define local copies of those. However, I realize this is just a stripped short example of the real code. If said real code needs to modify pi or sqrt_2, remove the const attribute in C-side. Even then, the original constants in F-side will remain intact.

Could you try the extern keyword in the c code to avoid this?

e.g.

#include <stdio.h>

extern const double pi;
extern const double sqrt_2;

void print_constants() {
	printf("\npi = %f",pi);
	printf("\nsqrt_2 = %f",sqrt_2);
}
2 Likes

Thanks. Now the program no longer crashes on Windows, and it runs correctly as before on WSL2.
A program where I add another C source file

extern const double pi;

double area_circle(double radius) {
	return pi*radius*radius;
}

and call the function in it also works.

1 Like

I’m a bit late to the party, but the extern keyword is necessary on the C side.

const double pi;

This defines an uninitialized const variable pi. (Sadly, it is valid C to leave it uninitialized!)

extern const double pi;

This declares that somewhere, in a different object file, there will be a const variable pi, and the linker should go and find it. Since the actual definition is in your fortran module, this is the form that you want for the C code.

The const keyword basically means “read-only”. It’s less formal than fortran’s parameter keyword.

1 Like