Я пытаюсь создать файл библиотеки .lib, содержащий функции Fortran, которые вызывают функции c ++, но я получаю страшную «ошибку LNK2019: неразрешенный внешний символ …». Код в конечном итоге будет скомпилирован с кучей других библиотек в виде DLL и использован в отдельной программе (PSSE). Я получаю ошибку компиляции, когда PSSE пытается создать DLL, используя мою библиотеку. Вот код, который я пытаюсь использовать, а затем код компиляции. Код должен просто сложить два числа и вывести ответ.
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
Когда программа PSSE пытается создать DLL, я получаю следующий вывод:
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 — это просто Fortran-вызовы функций внешней библиотеки:
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
Я видел несколько разных примеров вызова функций c ++ из Fortran, но все они выглядят немного иначе. Одна вещь, которую я заметил, была различное использование _
до или после имени функции c ++. Как я знаю, какой использовать: _CPPFUNCTION
, CPPFUNCTION
, или же CPPFUNCTION_
, Нужно ли экспортировать функцию в C ++, используя __declspec( dllexport )
? Нужно ли создавать ALIAS:'_CPPFUNCTION'
в коде Фортрана?
Я использую следующие компиляторы:
ifort: ЭКО IA-32 v12.1.0.233
кл .: v16.00.30319.01 x86
Есть ли что-то, что мне не хватает, чтобы правильно связать код C ++ с функциями Fortran?
Проблема в том, что вариантов много. Нет, один или два подчеркивания до и после, длина строки после переменной или в конце списка, вызов по значению или вызов по ссылке. Использование заглавных, строчных или оригинальных имен. Только с этими вариантами, вероятность сделать это правильно уже меньше, чем 1 на 100 (1/3 * 1/3 * 1/2 * 1/2 * 1/3).
Вы можете уменьшить его, проанализировав файл .lib с помощью DUMPBIN Утилита и ручная проверка настроек Intel Fortran по умолчанию и настроек в файлах проекта.
Самый элегантный способ, как некоторые предположили, заключается в использовании комбинации bind(C)
а также iso_c_binding модуль. bind(C)
Заявление избегает необходимости знать имя искажения. iso_c_bindings
обеспечивает с струнами а также integer(c_int)
вместо integer*4
для обеспечения совместимости ваших типов с C. Вы должны знать, что fortran вызывает по умолчанию по ссылке, и вы можете использовать , value
звонить по значению. Это должно поднять ваш уровень успеха вплоть до 1.
Вот простой пример того, как вызвать функцию add1, определенную в c ++ ниже:
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);
}
редактировать пример только с подпрограммой. Для включения в ваш общий объект / 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
Других решений пока нет …