Reputation: 650
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?
Upvotes: 0
Views: 2907
Reputation: 23215
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
Upvotes: 1