Interoperability: Fortran to C++ - fortran

I would like to develop a Fortran Static Library that include a custom subroutine that takes a long time to complete.
This static library will be linked in my C++ application statically too.
My goal is monitoring the current status of this subroutine in my C++ application in real-time.
So, for each step of a "fortran loop", I would like to send the loop index to my C++ application.
I'm new in Fortran world, so I was thinking that this task could be something like this:
My C++ header:
extern "C" void fortran_status(int* value);
My Fortran-90 file:
module my_interfaces
use iso_c_binding
interface
subroutine fortran_status(progress_value) bind(C, name = 'fortran_status')
use, intrinsic :: iso_c_binding
integer(c_int), intent(out) :: progress_value
end subroutine fortran_status
end interface
end module my_interfaces
! My long calc subroutine
subroutine my_long_calc(progress_value) BIND(C, name = 'my_long_calc')
use, intrinsic :: ISO_C_BINDING
use my_interfaces
implicit none
EXTERNAL fortran_status
integer (C_INT), INTENT(INOUT) :: progress_value
integer (C_INT) :: count
do count = 0, 5
progress_value = progress_value + 1
! Send 'progress_value' to a C++ function:
call fortran_status(progress_value)
! Wait 1 second:
call sleep(1)
end do
end subroutine my_long_calc
This Fortran code gives me a compile-time error:
error #6406: Conflicting attributes or multiple declaration of name. [FORTRAN_STATUS] C:\Users\lamar\Desktop\Lib1_interface\Lib1.f90 18
I'm using Microsoft Visual Studio 2019 with Intel Visual Fortran (Windows 10 x64).
How could I monitoring that subroutine status? Or get the fortran loop index in my C++ application?
UPDATE 1:
I removed the EXTERNAL fortran_status and now I got no errors in compile-time of my Fortran code.
Now, I would like to link this static Lib (x86) with my C++ code:
#include <iostream>
extern "C" {
void my_long_calc(void);
void fortran_status(int* value);
}
void fortran_status(int* value)
{
std::cout << "Fortran current status = " << *value << std::endl;
}
int main (void)
{
std::cout << "Monitoring Fortran subroutine..." << std::endl;
my_long_calc();
return 0;
}
I'm trying to compile and link it using MingW:
g++ -Wall -L. .\Lib2.lib -lgfortran .\main.cpp -o app.exe
And I got this linker error:
undefined reference to my_long_calc
How can I link it?
I would like to see in my terminal output:
Monitoring Fortran subroutine...
Fortran current status = 0
Fortran current status = 1
Fortran current status = 2
​​​​​​​Fortran current status = 3
​​​​​​​Fortran current status = 4
​​​​​​​Fortran current status = 5
UPDATE 2
Now this changed Fortran code compiles and it work well ONLY if I'm using MingW g++ (x86).
Fortran code:
module my_interfaces
use iso_c_binding
interface
subroutine fortran_status(progress_value) bind(C, name = 'fortran_status')
use, intrinsic :: iso_c_binding
integer(c_int), intent(out) :: progress_value
end subroutine fortran_status
end interface
end module my_interfaces
! My long calc subroutine
subroutine my_long_calc() BIND(C, name = 'my_long_calc')
use, intrinsic :: ISO_C_BINDING
use my_interfaces
implicit none
integer (C_INT) :: progress_value
integer (C_INT) :: count
progress_value = 0
do count = 0, 5
progress_value = count
! Send 'progress_value' to a C++ function:
call fortran_status(progress_value)
! Wait 1 second:
call sleep(1)
end do
end subroutine my_long_calc
C++ Code:
#include <iostream>
extern "C" {
void my_long_calc(void);
void fortran_status(int* value);
}
void fortran_status(int* value)
{
std::cout << "Fortran current status = " << *value << std::endl;
}
int main (void)
{
std::cout << "Monitoring Fortran subroutine..." << std::endl;
my_long_calc();
return 0;
}
Compile c++: g++ -Wall -c .\main.cpp
Compile fortran: gfortran -c .\gfortran.f90
Link all together: g++ .\main.o .\gfortran.o -o app.exe -lgfortran
The problem now is that I need to use Visual Studio 2019 and Visual Fortran to develop my Fortran Code.
And I would like to compile all Fortran code to a single static library (*.lib) file using Visual Studio.
What I need to change to get to link the *.lib file (from Visual Fortran) using the MingW g++ command?
I was thinking to use something like this:
g++ main.cpp my_lib.lib -o app.exe
But I got this linker error:
undefined reference to my_long_calc
What I need to do?

The code in your "Update 2" is perfectly fine and works with Intel Visual Fortran and Microsoft Visual C++. See also the discussion you started in https://community.intel.com/t5/Intel-Fortran-Compiler/Interoperability-Fortran-to-C/m-p/1144147
I do not recommend mixing Intel Visual Fortran compiled objects/libraries with g++ on Windows. You can get Microsoft Visual Studio Community Edition free if you meet the rather liberal license terms. If you insist on using g++, mix it with gfortran.

Related

Error when running Fortran program (forrtl: severe (157): Program Exception - access violation)

I have a C program that has a function named get_name. This function returns a string (i.e. char *) and changes the argument size (passed to it) with the size of the string:
char *get_name(int &size)
{
*size = strlen(name); // name is a C global variable declared as a char *
return name;
}
I have created the following Fortran module to be able to call the C function get_name:
MODULE X
USE, INTRINSIC :: iso_c_binding, ONLY: c_intptr_t
IMPLICIT NONE
INTERFACE
TYPE(c_ptr) FUNCTION get_name_(size) BIND(C, name = "get_name")
USE, INTRINSIC :: iso_c_binding, ONLY: c_int, c_ptr
INTEGER(c_int), INTENT(OUT) :: size
END FUNCTION
END INTERFACE
CONTAINS
FUNCTION get_name()
USE, INTRINSIC :: iso_c_binding, ONLY: c_int, c_char, c_f_pointer, c_ptr, c_associated
!DEC$ ATTRIBUTES DLLEXPORT :: get_name
CHARACTER(LEN = :), ALLOCATABLE :: get_name
INTEGER(c_int) :: size
TYPE(c_ptr) :: c_string
c_string = get_name_(size)
IF (c_associated(c_string)) THEN
BLOCK
CHARACTER(KIND = c_char, LEN = size), POINTER :: f_string
CALL c_f_pointer(c_string, f_string)
get_name = f_string
END BLOCK
ELSE
get_name = ""
END IF
END FUNCTION
END MODULE
I can successfully compile this Fortran module using IFORT 2016 in Windows, IFORT 2016 in Linux, and gfortran in Linux.
To test things, I have created a short Fortran program:
PROGRAM Test
USE X
WRITE(*, *) "Name: ", get_name()
END PROGRAM
I can successfully compile this Fortran program using IFORT 2016 in Windows, IFORT 2016 in Linux, and gfortran in Linux.
Now, when running the program it works well in IFORT 2016 for Linux, gfortran in Linux, but not in IFORT 2016 for Windows. It actually gives the following error:
forrtl: severe (157): Program Exception - access violation
Any idea how this error can be solved?
I assume your C function is char *get_name(int *size) instead of using a C++ reference.
Then this code works here on Windows and Linux with Ifort 16 and gcc-6.3.1 or Visual Studio 2015.
Please add your used C compiler and command lines to compile the code. I used:
cl -c testc.c && ifort -c testf.f /extend-source:132 && ifort testm.f testf.obj testc.obj /extend-source:132 && testm.exe
Btw: If you are using this function in parallel code I would suggest to avoid the block construct - I had bad experience using this in parallel (OpenMP) code with Ifort 16.

Why does linking my program with iso_c_binding cause undefined references to __gfortran_?

I have an legacy Fortran code that I want to mix with a new C/C++ program.
The Fortran subroutine allocates dynamically some arrays that I want to pass to c program. I will only get the size of these arrays after running the Fortran code.
After getting some tips here in this forum I arrived to the following code that I thought it would best compile, link and run.
Actually I can compile my C code and my Fortran code separately, but it doesn't link giving the following errors:
undefined reference to _gfortran_runtime_error
undefined reference to _gfortran_os_error
I'm using g++ and GFortran compilers version 5.4.0, and linking both .o files with g++ and the option -lg fortran.
fortran code:
subroutine test_allocation(outp) bind(c)
use iso_c_binding
implicit none
type (c_ptr), value :: outp
integer, target ::b(2)
integer(c_int), pointer :: a(:)
b(1)=1
b(2)=2
allocate(a(2))
a=>b
call c_f_pointer(outp, a,[2])
end subroutine
c code:
#include <iostream>
using namespace std;
extern "C" void test_allocation(int ** ptr);
int main ()
{
int* ptr;
test_allocation(&ptr);
}
EDIT:
As Vladimir F said on comments there was a mistake in my compiler option. The correct is -lgfortran.
Now it's linking but the results is not what I expect. I change a little my code to show this:
Fortran code:
subroutine test_allocation(outp) bind(c)
use iso_c_binding
implicit none
type (c_ptr), value :: outp
integer, target ::b(2)
integer(c_int), pointer :: a(:)
b(1)=1
b(2)=2
allocate(a(2))
a=>b
print*, "a(1) in Fortran: ", a(1)
print*, "a(2) in Fortran: ", a(2)
call c_f_pointer(outp, a,[2])
print*, "outp after c_f_pointer: ", outp
end subroutine
C code:
#include <iostream>
using namespace std;
extern "C" void test_allocation(int** ptr);
int main ()
{
int* ptr;
test_allocation(&ptr);
cout<<"ptr[0] in C: "<< ptr[0]<<endl;
cout<<"ptr[1] in C: "<< ptr[1]<<endl;
}
The output is:
a(1) in fortran: 1
a(2) in fortran: 2
outp after c_f_pointer: 140726088663920
ptr[0] in C: 1447122753
ptr[1] in C: 1107265857
I also tried changing the declaration of extern function to the following and it still does not work:
extern "C" void test_allocation(int*& ptr);
...
test_allocation(ptr);
The output is:
a(1) in fortran: 1
a(2) in fortran: 2
outp after c_f_pointer: 140729541703872
ptr[0] in C: 1447122753
ptr[1] in C: 1107265857
Now it works. Instead of using C_F_POINTER I used the function C_LOC.
I also removed the parameter VALUE from the TYPE(C_PTR) declaration.
Here is the code:
subroutine test_allocation(outp) bind(c)
use iso_c_binding
implicit none
type (c_ptr) :: outp
integer ::b(2)
integer,dimension(:), pointer :: a
b(1)=1
b(2)=2
allocate(a(2))
a=b
print*, "a(1) in fortran: ", a(1)
print*, "a(2) in fortran: ", a(2)
outp=c_loc(a)
print*, "outp after c_loc: ", outp
end subroutine
and it's output:
a(1) in fortran: 1
a(2) in fortran: 2
outp after c_loc: 36866928
ptr[0] in C: 1
ptr[1] in C: 2
The following link helped me a lot although I'm not using intel compiler:
https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/269660

Compiling a module with f2py with an interface

I am trying to compile a fortran module with f2py. It is the following code
module my_log_mod
implicit none
interface my_log
module procedure my_log_array
module procedure my_log_vector
end interface my_log
private ! hides items not listed on public statement
public :: my_log
contains
subroutine my_log_array(a,res)
double precision, dimension (:,:), intent (in) :: a
double precision, dimension (:,:), intent (out) :: res
where (a>1.0)
res = log(a)
else where
res = 0.D0
end where
end subroutine
subroutine my_log_vector(a,res)
double precision, dimension (:), intent (in) :: a
double precision, dimension (:), intent (out) :: res
where (a>1.0)
res = log(a)
else where
res = 0.D0
end where
end subroutine
end module my_log_mod
that I compile with the following command
f2py.py -c -m my_log_mod_comp my_log_mod.f90
and it results in the following error
C:\Users\weisshau\AppData\Local\Temp\tmpf0apqa7s\src.win32-3.6\my_log_mod_comp-f2pywrappers2.f90:7:28:
use my_log_mod, only : my_log_array
1
Error: Symbol 'my_log_array' referenced at (1) not found in module 'my_log_mod'
C:\Users\weisshau\AppData\Local\Temp\tmpf0apqa7s\src.win32-3.6\my_log_mod_comp-f2pywrappers2.f90:18:28:
use my_log_mod, only : my_log_vector
1
Error: Symbol 'my_log_vector' referenced at (1) not found in module 'my_log_mod'
I don't really know much about fortran and f2py, so I don't have any idea what is happening. If i use the module in pure fortran it works nicely
F2py appears to be creating another wrapper code which uses the subroutines in your module.
But it calls directly the subroutines my_log_vector and my_log_array. It seems f2py does not support private. I would delete the private.
Also be prepared that you won't be able to use the generic my_log in Python. This concept of generics is alien to Python. You may need to delete the generic interface if deleting private does not make it compilable. You should definitely delete the public :: my_log.
Unfortunately, f2py does not support all features of modern Fortran.
The code I tested:
module my_log_mod
implicit none
interface my_log
module procedure my_log_array
module procedure my_log_vector
end interface my_log
contains
subroutine my_log_array(a,res)
double precision, dimension (:,:), intent (in) :: a
double precision, dimension (:,:), intent (out) :: res
where (a>1.0)
res = log(a)
else where
res = 0.D0
end where
end subroutine
subroutine my_log_vector(a,res)
double precision, dimension (:), intent (in) :: a
double precision, dimension (:), intent (out) :: res
where (a>1.0)
res = log(a)
else where
res = 0.D0
end where
end subroutine
end module my_log_mod
compilation:
f2py -c -m my_log_mod_comp my_log_mod.f90
...
Post-processing...
Block: my_log_mod_comp
Block: my_log_mod
Block: my_log
Block: my_log_array
Block: my_log_vector
Post-processing (stage 2)...
Block: my_log_mod_comp
Block: unknown_interface
Block: my_log_mod
Block: my_log_array
Block: my_log_vector
Building modules...
Building module "my_log_mod_comp"...
Constructing F90 module support for "my_log_mod"...
Creating wrapper for Fortran subroutine "my_log_array"
res = my_log_array(a)
res = my_log_array(a)
Creating wrapper for Fortran subroutine "my_log_vector"("my_log_vector")...
Constructing wrapper function "my_log_mod.my_log_vector"...
res = my_log_vector(a)
Wrote C/API module "my_log_mod_comp" to file "/tmp/tmp7e5v0u/src.linux-x86_64-2.7/my_log_mod_compmodule.c"
Fortran 90 wrappers are saved to "/tmp/tmp7e5v0u/src.linux-x86_64-2.7/my_log_mod_comp-f2pywrappers2.f90"
...
gfortran:f90: /tmp/tmp7e5v0u/src.linux-x86_64-2.7/my_log_mod_comp-f2pywrappers2.f90
/usr/bin/gfortran -Wall -g -Wall -g -shared /tmp/tmp7e5v0u/tmp/tmp7e5v0u/src.linux-x86_64-2.7/my_log_mod_compmodule.o /tmp/tmp7e5v0u/tmp/tmp7e5v0u/src.linux-x86_64-2.7/fortranobject.o /tmp/tmp7e5v0u/my_log_mod.o /tmp/tmp7e5v0u/tmp/tmp7e5v0u/src.linux-x86_64-2.7/my_log_mod_comp-f2pywrappers2.o -L/usr/lib64 -lpython2.7 -lgfortran -o ./my_log_mod_comp.so
Removing build directory /tmp/tmp7e5v0u

Undefined reference to Fortran function in C++

I can't seem to figure out why this isn't working.
/* main.cpp */
#include <stdio.h>
extern "C"
{
int __stdcall inhalf(int *);
}
int main()
{
int toHalf = 2;
int halved = inhalf(&toHalf);
printf("Half of 2 is %d", halved);
return 0;
}
Ok, that looks good.
$ g++ -c main.cpp
No errors.
! functions.f90
function inhalf(i) result(j)
integer, intent(in) :: i
integer :: j
j = i/2
end function inhalf
I'm pretty sure that's right.
$ gfortran -c functions.f90
So far so good...
$ gcc -o testo main.o functions.o
main.o:main.cpp:(.text+0x24): undefined reference to `inhalf#4'
collect2.exe: error: ld returned 1 exit status
I've been looking this up for over an hour, but I couldn't find anything that worked for this case. How should I solve this?
For full C compatibility you can use the bind feature of modern Fortran:
! functions.f90
function inhalf(i) result(j) bind(C,name='inhalf')
integer, intent(in) :: i
integer :: j
j = i/2
end function inhalf
This allows you to give a name to the function that you can use in C (and others) without relying on the naming scheme your compiler uses on its own.
The __stdcall is Win32 only (and the default behavior for linking, see here). You can safely remove it. [Actually it is required for compiling your code in Linux. ]

Create .lib file with c++ and Fortran / Call c++ code from Fortran / Unresolved external symbol

I am attempting to create a .lib library file that contains Fortran functions that call c++ functions, but I am getting the dreaded "error LNK2019: unresolved external symbol...". The code will eventually be compiled with a bunch of other libraries as a DLL and used in a separate program (PSSE). I am getting the compile error when PSSE attemps to create the DLL using my library. Here is the code I attempting to use, followed by the compiling code. The code should just add two numbers together and output the answer.
fort_code.f
SUBROUTINE TESTCPP ( II, JJ, KK, LL )
INCLUDE 'COMON4.INS'
integer*4, external :: CPPFUNCTION
INTEGER a, b, c, test
IF (.NOT. IFLAG) RETURN
a = ICON(II)
b = ICON(II + 1)
test = CPPFUNCTION( a , b, c )
WRITE ( ITERM, * ) 'C = ', c
RETURN
END
cpp_code.cpp
extern "C" {
void _CPPFUNCTION(int a, int b, int *c);
}
void _CPPFUNCTION(int a, int b, int *c) {
*c = a + b;
}
compile.bat
cl /nologo /MD /c /W3 /O2 /FD /EHsc /errorReport:prompt /D"MSWINDOWS" /D"WIN32" ^
/D"_WINDOWS" /D"NDEBUG" "cpp_code.cpp"
IFORT /nologo /Od /Oy- /assume:buffered_io /traceback /libs:dll /threads /c /Qip ^
/extend_source:132 /noaltparam /fpscomp:logicals /warn:nodeclarations ^
/warn:unused /warn:truncated_source /Qauto /Op /iface:cvf /define:DLLI ^
/include:"C:\Program Files (x86)\PTI\PSSE32\PSSLIB" ^
/object:"fort_code.OBJ" ^
"fort_code.f"
lib /out:fort_cpp.lib fort_code.obj cpp_code.obj
When the PSSE program attempts to create the DLL, this is the output I get:
ifort /nologo /assume:buffered_io /traceback /libs:dll /threads /c /Qip /extend_source:132 /noaltparam /fpscomp:logicals /Qprec /warn:declarations /warn:unused /warn:truncated_source /Qauto /fp:source /iface:cvf /define:DLLI /include:"C:\Program Files (x86)\PTI\PSSE32\PSSLIB" /object:"C:\temp\INIT_620289\11hw2ap_conec.obj" /module:"C:\temp\INIT_620289" "11hw2ap_conec.f"
ifort /nologo /assume:buffered_io /traceback /libs:dll /threads /c /Qip /extend_source:132 /noaltparam /fpscomp:logicals /Qprec /warn:declarations /warn:unused /warn:truncated_source /Qauto /fp:source /iface:cvf /define:DLLI /include:"C:\Program Files (x86)\PTI\PSSE32\PSSLIB" /object:"C:\temp\INIT_620289\11hw2ap_conet.obj" /module:"C:\temp\INIT_620289" "11hw2ap_conet.f"
link /INCREMENTAL:NO /NOLOGO /DLL /SUBSYSTEM:WINDOWS /MACHINE:X86 /ERRORREPORT:PROMPT #"C:\temp\INIT_620289\linkfilestod9p1.txt" /OUT:"C:\temp\INIT_620289\11hw2ap_dsusr.dll" /map:"C:\temp\INIT_620289\11hw2ap_dsusr.map"
fort_cpp.lib(fort_code.obj) : error LNK2019: unresolved external symbol _CPPFUNCTION#12 referenced in function _TESTCPP
C:\temp\INIT_620289\11hw2ap_dsusr.dll : fatal error LNK1120: 1 unresolved externals
ERROR during link(1)... Aborted
conec/conet are simply Fortran calls to the external library functions:
SUBROUTINE CONEC
C
INCLUDE 'COMON4.INS'
C
CALL TESTCPP ( 55791, 0, 0, 0)
C
RETURN
END
SUBROUTINE CONET
C
INCLUDE 'COMON4.INS'
C
IF (.NOT. IFLAG) GO TO 9000
C
C NETWORK MONITORING MODELS
C
C
9000 CONTINUE
C
RETURN
END
I have seen a few different examples of calling c++ functions from Fortran, but they all look slightly different. One thing I noticed was differening uses of the _ before or after the c++ function name. How do I know which to use: _CPPFUNCTION, CPPFUNCTION, or CPPFUNCTION_. Do I need to export the function in c++ using __declspec( dllexport )? Do I need to create an ALIAS:'_CPPFUNCTION' in the Fortran code?
I am using the following compilers:
ifort: IVF IA-32 v12.1.0.233
cl: v16.00.30319.01 x86
Is there something I am missing to link the c++ code properly to the Fortran functions?
The problem with is that there are a lot of options. None, one or two underscores before and or after, string length after a variable or at the end of a list, call by value or call by reference. Capitalize, lowercase or original naming. With just these options, the probability of getting it right is already lower than 1 in 100 (1/3*1/3*1/2*1/2*1/3).
You can reduce it a bit by introspecting the .lib file using the dumpbin utility and manually checking intel fortran default settings and the settings in the project files.
The most elegant way, like some suggested, is to use the combination of bind(C) and iso_c_binding module. The bind(C) statement avoids having to know the name mangling. The iso_c_bindings provides c strings and integer(c_int) instead of integer*4 to ensure compatibility of your types with C. You have to know that fortran calls by reference by default and you can use , value to call by value. This should raise your succes rate all the way back to 1.
Here is a simple example of how to call the add1 function defined in c++ below:
test.f90
program example
use iso_c_binding
implicit none
interface
integer(c_int) function add1(x) bind(C,name="add1")
!DEC$ ATTRIBUTES DLLEXPORT :: add1
use iso_c_binding
integer(c_int), value :: x
end function add1
end interface
call callingadd1()
contains
subroutine callingadd1()
write(*,*) '1+1=', add1(1)
end subroutine callingadd1
end program example
add1.cpp
extern "C" {
int add1(int x);
}
int add1(int x) {
return(x+1);
}
edit an example with only a subroutine. For inclusion in your shared object/dll/dylib.
subroutineonly.f90
subroutine callingadd1() bind(C, name="callingadd1")
!DEC$ ATTRIBUTES DLLEXPORT :: callingadd1
use iso_c_binding
implicit none
interface
integer(c_int) function add1(x) bind(C,name="add1")
!DEC$ ATTRIBUTES DLLEXPORT :: add1
use iso_c_binding
integer(c_int), value :: x
end function add1
end interface
write(*,*) '1+1=', add1(1)
end subroutine callingadd1