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

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

Imagen de nkinar

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?

publicaciones de 12 / 0 nuevos
Último envío
Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.
Imagen de Tim Prince

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.

Imagen de nkinar

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

Imagen de Tim Prince

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.

Imagen de nkinar

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

Imagen de Anthony Richards

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?

Imagen de nkinar

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.

Imagen de Anthony Richards

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.

Imagen de Steve Lionel (Intel)

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.

Steve
Imagen de nkinar

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().

Imagen de Steve Lionel (Intel)

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.

Steve
Imagen de nkinar

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.

Inicie sesión para dejar un comentario.