Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29253 Discussions

Name-mangling when linking C++ code with Visual Fortran code

nkinar
Beginner
2,702 Views

I would like to call a Fortran subroutine from a C++ code function.  

I have two projects in one solution.  The project with C++ code calls a Fortan subroutine.  The Fortran project is set up to compile as a static library.

(1) Project with C++ code (test-Q.cpp)

(2) Project with Fortran code (getqpf.F)

At the top of my C++ code file, I've placed:

extern"C" {
         void getqpf (double *tri);

}

However, when trying to call the Fortran subroutine from code in the C++ file, I get the following linker error:

1>test-Q.obj : error LNK2019: unresolved external symbol _getqpf referenced in function "void __cdecl call_function(class std::vector<double,class std::allocator<double> >)" (?call_function@@YAXV?$vector@NV?$allocator@N@std@@@std@@@Z)
1>E:\DEVELOPMENT-FINAL\EXPERIMENTS\test-Q-analysis-1\fortran_code\Debug\C-drivers.exe : fatal error LNK1120: 1 unresolved externals

What is the proper way for C++ code to call Fortran code?  Is there a name-mangling convention used when calling Fortran code from C++ code?

0 Kudos
11 Replies
TimP
Honored Contributor III
2,702 Views
The standard way to over-ride Fortran name mangling is under the standard module USE ISO_C_BINDING which you can read about in either the ifort documentation or on general Fortran web sites. Your Fortran procedure declaration would include the bind attribute e.g. subroutine getqpf(tri) bind(c,name='getqpf') real(C_DOUBLE) tri ! identical to double precision on Intel processors Without that, the default for Visual Studio compatible Fortran compilers is to upper-case the symbol.
0 Kudos
nkinar
Beginner
2,702 Views
Thanks Tim; this is very much appreciated! So when both Intel C++ and Intel Fortran are being used as compilers, the extern function should be the following (in uppercase)? extern"C" { void GETQPF (double *tri); } In the Fortran code, the symbol should also be GETQPF as well? I've tried to make both of the symbols uppercase, but I still receive a rather cryptic link error: 1>test-Q.obj : error LNK2019: unresolved external symbol _GETQPF referenced in function "void __cdecl call_function(class std::vector >)" (?call_function@@YAXV?$vector@NV?$allocator@N@std@@@std@@@Z) 1>E:\DEVELOPMENT-FINAL\EXPERIMENTS\test-Q-analysis-1\fortran_code\Debug\C-drivers.exe : fatal error LNK1120: 1 unresolved externals
0 Kudos
TimP
Honored Contributor III
2,702 Views
I would have expected it to work this way. You could run dumpbin /symbols on your ifort built .obj to see what symbol was actually chosen. If it matches, it seems you have some problem with specifying the project dependencies, as you are seeing the C .obj but not the Fortran one.
0 Kudos
nkinar
Beginner
2,702 Views
Thanks, Tim. I tried running dumpbin /symbols on the getqpf.obj, and this is the output. I am working with Visual Studio 2010, and using the most recent version of the Intel C and Fortran compilers. The Fortran subroutine that I would like to call is GETQPF. E:\DEVELOPMENT-FINAL\EXPERIMENTS\test-Q-analysis-1\fortran_code\fortran_code\Deb ug>dumpbin /symbols getqpf.obj Microsoft (R) COFF/PE Dumper Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file getqpf.obj File Type: COFF OBJECT COFF SYMBOL TABLE 000 00000001 ABS notype Static | @feat.00 001 00000000 SECT1 notype Static | .text Section length F14, #relocs 53, #linenums 0, checksum 0 003 00000000 SECT1 notype () External | _GETQPF 004 00000000 SECT2 notype Static | .debug$S Section length 818, #relocs 4, #linenums 0, checksum 0 006 00000000 SECT6 notype Static | .debug$T Section length 7C, #relocs 0, #linenums 0, checksum 0 008 00000000 SECT3 notype Static | .rdata Section length 1E0, #relocs 8, #linenums 0, checksum 0 00A 00000110 SECT3 notype Static | _2il0floatpacket.32 00B 00000120 SECT3 notype Static | _2il0floatpacket.30 00C 00000128 SECT3 notype Static | _2il0floatpacket.28 00D 0000012C SECT3 notype Static | _2il0floatpacket.31 00E 00000130 SECT3 notype Static | __rtc_frame_desc_2.29 00F 00000000 UNDEF notype () External | _for_emit_diagnostic 010 00000000 UNDEF notype () External | _QESTM 011 00000000 UNDEF notype () External | _QESTM1 012 00000000 UNDEF notype () External | _QESTF 013 00000000 UNDEF notype () External | _QESTF1 014 00000000 UNDEF notype () External | @_RTC_CheckStackVars@8 015 00000000 UNDEF notype () External | __RTC_CheckEsp 016 00000000 SECT3 notype Static | __STRLITPACK_0 017 00000064 SECT3 notype Static | __STRLITPACK_1 018 0000006C SECT3 notype Static | __STRLITPACK_2 019 00000074 SECT3 notype Static | __STRLITPACK_3 01A 0000007C SECT3 notype Static | __STRLITPACK_4 01B 00000084 SECT3 notype Static | __STRLITPACK_5 01C 00000088 SECT3 notype Static | __STRLITPACK_6 01D 0000008C SECT3 notype Static | __STRLITPACK_7 01E 00000094 SECT3 notype Static | __STRLITPACK_8 01F 0000009C SECT3 notype Static | __STRLITPACK_9 020 000000A4 SECT3 notype Static | __STRLITPACK_10 021 000000A8 SECT3 notype Static | __STRLITPACK_11 022 000000B0 SECT3 notype Static | __STRLITPACK_12 023 000000B8 SECT3 notype Static | __STRLITPACK_13 024 000000C0 SECT3 notype Static | __STRLITPACK_14 025 000000C8 SECT3 notype Static | __STRLITPACK_15 026 000000D0 SECT3 notype Static | __STRLITPACK_22 027 000000D4 SECT3 notype Static | __STRLITPACK_23 028 000000D8 SECT3 notype Static | __STRLITPACK_24 029 000000E0 SECT3 notype Static | __STRLITPACK_16 02A 000000E8 SECT3 notype Static | __STRLITPACK_17 02B 000000EC SECT3 notype Static | __STRLITPACK_18 02C 000000F4 SECT3 notype Static | __STRLITPACK_19 02D 000000F8 SECT3 notype Static | __STRLITPACK_20 02E 00000100 SECT3 notype Static | __STRLITPACK_21 02F 00000000 UNDEF notype External | __fltused 030 00000000 SECT4 notype Static | .drectve Section length BA, #relocs 0, #linenums 0, checksum 0 032 00000000 SECT5 notype Static | .trace Section length 104, #relocs 2, #linenums 0, checksum 0 String Table Size = 0x235 bytes Summary 818 .debug$S 7C .debug$T BA .drectve 1E0 .rdata F14 .text 104 .trace
0 Kudos
Anthony_Richards
New Contributor I
2,702 Views
Clearly the C++ is looking for _GETQPF and clearly _GETQPF is a symbol in your Fortran library. Therefore, the C++ project has not been told to use your Fortran library when trying to resolve symbols. Can you add the Fortran Library to your C++ project and try that?
0 Kudos
nkinar
Beginner
2,702 Views
Thanks, Anthony! I tried going into the project properties of the C++ project and adding the Fortran link directory as well as the Fortran library to the linker command line. Unfortunately, I still receive a linker error. What am I still doing wrong here? 1>test-Q.obj : error LNK2019: unresolved external symbol "void __cdecl _GETQPF(double *,int,int,int,double,float,int,double,double *,double *,double *,double *,double *,double *,double *,int,int,int,double *,double *,double *,double *,int,int,int,float,int,int)" (?_GETQPF@@YAXPANHHHNMHN0000000HHH0000HHHMHH@Z) referenced in function "void __cdecl call_function(class std::vector >)" (?call_function@@YAXV?$vector@NV?$allocator@N@std@@@std@@@Z) 1>E:\DEVELOPMENT-FINAL\EXPERIMENTS\test-Q-analysis-1\fortran_code\Debug\C-drivers.exe : fatal error LNK1120: 1 unresolved externals 1> 1>Build FAILED.
0 Kudos
Anthony_Richards
New Contributor I
2,702 Views
In the first example you gave , you have extern"C" { void GETQPF (double *tri); } which implies a subroutine (void) with one argument supplied by reference. However, you do not supply a calling convention (e.g. __cdecl or whatever). In the second link error, it appears C++ is looking for a rather different function having many more arguments, but at least a calling convention __cdecl is supplied: "void __cdecl _GETQPF(double *,int,int,int,double,float,int,double,double *,double *,double *,double *,double *,double *,double *,int,int,int,double *,double *,double *,double *,int,int,int,float,int,int)" Perhaps you should first decide on (1) the number of arguments that the C++ prototype function must have to match those in the Fortran subroutine, and (2) the calling convention to be used, which must match that specified either explicitly in the Fortran code or implicitly by default. Also, I am not clear whether or not you are passing the function's name as an argument in your C++ code. I have no idea what that might lead to or how to reconcile it with an EXTERN "C" function. May I suggest you write a small bit of C++ code to try calling the Fortran routine directly and get that working first before trying anything more complicated.
0 Kudos
Steven_L_Intel1
Employee
2,702 Views
One doesn't need to explicitly state the __cdecl calling convention unless you compile the C/C++ code with /Gz (forces __stdcall). Regarding the name case mismatch, my recommendation is to use BIND(C) in the Fortran declaration and lowercase in the C++ declaration. The name= clause is not required here because a downcased name is what the standard specifies if name= is omitted.
0 Kudos
nkinar
Beginner
2,702 Views
Thanks, Anthony and Steve! The Fortran function inside the extern "C" block for the Intel C++/Fortran compiler is: extern"C" { void GETQPF (double *tri, int &nsamp, int &lwin, int &nfreqfit, double &dt, float &null, int &L2, double &df, double *qq, double *pf, double *ampls, double *work1, double *work2, double *work3, double *work4, int &mem, int &morder, int &nfs, double *xReal, double *xImag, double *xAbs, double *x1, int &cen, int &top, int &bot, float &cut, int &nfst, int &raw); } // end I am calling the Fortran subroutine from C code in the following manner: GETQPF (tri, nsamp, lwin, nfreqfit, dt, null, L2, df, qq, pf, ampls, work1, work2, work3, work4, mem, morder, nfs, xReal, xImag, xAbs, x1, cen, top, bot, cut, nfst, raw); Using the gcc compiler and the gfortran compiler under GNU/Linux, the calling convention is somewhat different, and I've had to replace the GETQPF() function call with getqpf_(). Using gcc and gfortran, I am now able to compile the program, and I can verify that the Fortran code is being called in the proper fashion with the right number of arguments. Here's what I've learned using default compiler name-mangling: 1. Using Intel C++/Fortran, it appears that adding the Fortran library to the C++ project and calling the function using all caps GETQPF() works well and gets rid of that particular linker error. 2. Using gcc and gfortran, the default name-mangling uses an underscore and small caps: getqpf_(). 3. OpenWatcom C/C++/Fortran uses the default GETQPF() in a similar fashion to Intel C/C++/Fortran. 4. As Steve says, the BIND(C) in the Fortran declaration allows for a lowercase C++ declaration as getqpf().
0 Kudos
Steven_L_Intel1
Employee
2,702 Views
In particular, using BIND(C) frees you from worrying about name mangling conventions. Please use that instead of hard-coding (or using switches) for naming conventions. It also makes sure that Fortran and C are in agreement as to how arguments are passed and how function values are returned.
0 Kudos
nkinar
Beginner
2,702 Views
Thanks, Steve. Yes, it is definitely better to use BIND(C) instead of using name mangling conventions! This doesn't seem to be the case for much older compilers which lack support for these conventions (such as Open Watcom), but it is very nice to have this modern feature in Intel C/C++/Fortran.
0 Kudos
Reply