How to call Fortran dll with function as argument in C - c++

I use VS2013 and Intel Visual Fortran, then make a dll from Fortran code.
In Fortran subroutine, it has 3 arguments, "fa" for passing a function, "a" for passing an array, "b" for passing a number. But it doesn't work when I call it in VC++.
Update:
Well... program above is chaotic, so I remodify program below. I create a fortran dll that it can pass an array to sum its elements and call a c function.
module callctest
use, intrinsic :: iso_c_binding
implicit none
private
public callcfun
contains
subroutine callcfun(a,b) bind(C,name = "callcfun")
!DEC$ ATTRIBUTES DLLEXPORT :: callcfun
implicit none
real(c_double), dimension(*):: a !receive an array from c
type(c_funptr), value :: b ! receive a c function
real, parameter::pi=3.14159
integer :: i
real :: sum = 0.0
do i = 1,3 ! sum the array elements
sum = sum+a(i)
end do
write(*,*) 'total',sum
return
end subroutine callcfun
end module callctest
And c code that it call fortran subroutine:
#include "stdafx.h"
#include <iostream>
using namespace std;
extern "C" {
void callcfun( double[], void(f)(int)); //
}
void sayhello(int a){
cout << "Hi! fortran " <<a<< endl;
}
int main(){
double a[] = { 10.0, 50, 3.0 };
callcfun(a, sayhello(50));
system("pause");
return 0;
}
In c program, function "sayhello" print words "Hi! fortran" and an integer, I want to call sayhello functionby calling "callcfun( array, function )" which is written by fortran.
It works when I call "callcfun( array, function )" in c program if function
like "sayhello" only print "Hi! fortran". But I add an int argument for sayhello function so that it print words "Hi! fortran" and an integer argument. Function "callcfun" doesn't execute successfully.
Error list:
Error 1 error C2664: 'void callcfun(double [],void (__cdecl *)(int))' : cannot convert argument 2 from 'void' to 'void (__cdecl *)(int)' c:\users\emlab\documents\visual studio 2013\projects\vc++\call_for_ex\call_for_ex\call_for_ex.cpp 21 1 call_for_ex
2 IntelliSense: argument of type "void" is incompatible with parameter of
type "void (*)(int)" c:\Users\EMlab\Documents\Visual Studio
2013\Projects\VC++\call_for_ex\call_for_ex\call_for_ex.cpp 21 14 call_for_ex
Errors show the problem in the c function, how to pass the c function back to fortran dll then call it in fortran?

For a Fortran procedure to be interoperable, the BIND(C) attribute is required. Both Fortran procedures are missing this attribute.
For a procedure to be exported from a DLL on Windows, the symbolic name of the procedure must ultimately be supplied to the linker. (There are three ways of doing this: a source directive passed through by the compiler into the object code (as per the !DEC$ ATTRIBUTES DLLEXPORT directive used with the arraysum procedure), by listing the procedure name in a module definition file or by listing the name after /EXPORT on the linker command line - I am assuming that the two latter methods have not been used.) Given the source directive approach of nominating one procedure has been used for one of the procedures (arraysum) to be exported, that method should also be used for the other procedure (fcn) that needs to be exported.
For a C++ function to have C linkage, it must be declared with extern "C". There is no such declaration for fcn in the C++ code. (The absence of any declaration for fcn means that C++ code should not have compiled.)
The import library (.lib) generated by the linker when building the Fortran DLL must be provided to the linker when the exe is built from the C++ code. Typically this is done within Visual Studio by providing the import library as an "Additional Dependency" on the Linker > Input property page.

Related

Interoperability: Fortran to C++

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.

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.

Fortran Call Errors

In my Fortran code, I have the commands:
CALL EXPECTATION(X, 0.5D0*SIGMAZ, Z_EXPECTATION)
WRITE(1,*) T, Z_EXPECTATION
CALL EXPECTATION(X, 0.5D0*SIGMAY, Y_EXPECTATION)
WRITE(4,*) T, Y_EXPECTATION
for which I get the errors:
Error: Symbol ‘sigmay’ at (1) has no IMPLICIT type
Error: Symbol ‘sigmaz’ at (1) has no IMPLICIT type
But I get no error for a similar command:
CALL EXPECTATION(X, 0.5D0*SIGMAX, X_EXPECTATION)
WRITE(3,*) T, X_EXPECTATION
What could be the issue? I am calling the variables from the same subroutine and I have defined them to be
COMPLEX*16, DIMENSION(2,2) :: SIGMAX, SIGMAY SIGMAZ
You forgot a comma in the declaration between SIGMAY and SIGMAZ. Since Fortran doesn't care about whitespaces, it created only two complex variables: SIGMAX and SIGMAYSIGMAZ.
Just add the comma and your code should compile fine.

How to wrap a C++ function?

I'm trying to wrap a c++ function called i_receive() by following this tutorial, I first created a wrap.c file, the content of this file is like this:
int i_receive(const uint32_t *f, int32_t t){
static int (*real_i_receive)(const uint32_t *, int32_t)=NULL;
printf("hello world");
return real_i_receive;
}
I compiled this file with gcc -fPIC -shared -o wrap.so wrap.c -ldl, when I used the LD_PRELOAD to run some C++ code with LD_PRELOAD=/full/path/to/wrap.so ./mycppcode I got this message:
ERROR: ld.so: object '/full/path/to/wrap.so' from LD_PRELOAD cannot be preloaded: ignored`.
I was guessing the reason might be that the wrap file is a C file, and I'm using it with C++ code, am I right?
I changed the file to wrap.cc with the same content, when compiling in the same way as before, I got:
ERROR: invalid conversion from 'int (*)(const uint32_t*, int32_t)' to 'int'
First of all, your 2nd error your are getting becase you are returning a Pointer to function type instead of a int type.
If you want to return an int, call the function from the code :
return real_i_receive(f,t);
Notice the "()" which means a function call.
Regarding your guess : it doesn't matter if you are using C or C++ code, the libaries are all assembly code.
One difference between exporting C functions and C++ functions is the name mangling. You would rather export a function as a C function to be able to access it inside your library through unmagled name.
To export a function without name mangling it, you can use extern "C" .
Replace
return real_i_receive;
with
return real_i_receive(f, t);
As it is, the return type of your function is int but you're returning a function pointer.

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