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

Calling C++ DLL from Fortran with common blocks using Visual Studio 2010

Patrick_K_1
Beginner
3,181 Views

I have problem in writing  my bachelor work. My bachelor work is mostly about taking out some subroutines from fortran code (i think it's Fortran 77 ) .Then rewrite them in to C++ and make DLL which will work with old fortran code. C++ dll will need to take data from common blocks nad other fortran variables.

First of all i have never wrote in Fortran but rewriting most of thing wasn't a problem. I did stuck in some moment  and I really need help.

 My first problem is that how should i write connection between common block in Fortran and C++.All common blocks are used first in Fortran then in C++. Only one function(hard) is called by Fortran, rest of them is used by this function hard, but all of them are using variables and common block from fortran.

I wroted this :

libmnu.h (part) :
#ifndef LIBMNU_H
#define LIBMNU_H

typedef int     INTEGER;
typedef double   REAL;
typedef double  DOUBLE_PRECISION;
typedef int     LOGICAL;

#define SUBROUTINE  extern "C" void __declspec(dllexport)
namespace libMNU
{
    extern "C" {    //////////////// this variables in fortran code are not declarated anywhere ,so i think they should be extern
      extern   REAL r, t273, rt, z, yield;
      extern   REAL dsq3,beta,betas,r0,bts,bt,w;
      extern   REAL sigmainit, sigma_sse,e_c,e_xs,e_s,e_r,e_xr,sigma_ss,sigma_soft;
                }
    extern "C"
    {
        extern struct flow_function
        {
            INTEGER n_function;
        };
         extern struct flow_function flow_function;
    }
SUBROUTINE hard            (double&,double&,double&,double&);
void x1                 (double&,double&,double&,double&);
void x2                 (double&,double&,double&,double&);
void x3                 (double&,double&,double&,double&);
void x4                 (double&,double&,double&,double&);
void x5                 (double&,double&,double&,double&);
void x6                 (double&,double&,double&,double&);
...
//// End of file
libmnu.cpp:
SUBROUTINE hard(double& e,double& edot,double& t,double& yield)
    {
        // double e,edot,t,yield;
        
        switch(flow_function.n_function)
        {
...
/// End of file
My explanation:
I was reading that common blocks should be like that:
extern "C"
    {
        extern struct
        {
            INTEGER n_function;
        } flow_function;
    }
, but then i saw "IntelliSense: declaration is incompatible with "libMNU::flow_function libMNU::flow_function" " and i read that it should be like above in code.
Here is sample of fortran code:

      subroutine Hard(e,edot,t,yield)
      IMPLICIT REAL*8 (A-H,O-Z)
      common/flow_function/n_function
      GoTo(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)n_function
      call x1(t,e,edot,yield)
      go to 100
 1    call x2(t,e,edot,yield)
      go to 100
 2   call x3(e,edot,t273,yield)

My first question is does someone know  how should it be ?
 Does compiling of this dll will be diffrent from normal ?
Since i will need in the future to compile fortran with .lib file i want to ask is it possible to do that all in Visual Studio 2010 with Intel Fortran Composer XE ?
I'm sorry if i my writing is bit chaotic and ask too much in one topic.



0 Kudos
12 Replies
TimP
Honored Contributor III
3,181 Views
I don't know the implications of attempting to define Fortran linkage inside a C++ namespace (does extern "C" escape from the namespace?). Other than that, your linkage to labeled COMMON looks OK, but I suppose you will need DLLEXPORT directives to make for visibility across dlls. For variables in Fortran not in COMMON, you should have USE iso_c_interop to make the variables you choose linkable by extern "C". Variables which have Fortran implicit declaration and don't appear in a COMMON will not normally be linkable to C. iso_c_interop also provides facility to ensure linkability of labeled COMMON. For what it's worth, the form of implicit declaration you show here was never technically standard Fortran. You may have a fair amount of work to turn this into portable Fortran.
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
Yes, extern "C" omits the C++ name mangling. One issue will be name case - the Fortran name for the COMMON will be FLOW_FUNCTION which doesn't match the C declaration. If you add: BIND(C) :: /flow_function/ it will fix that problem. The second problem is that you have not told Fortran to export the COMMON from the DLL, nor told C to import it. So you will need: !DEC$ ATTRIBUTES DLLEXPORT :: /flow_function/ in the Fortran code, and __declspec(dllimport) in your declaration of the C struct.
0 Kudos
Patrick_K_1
Beginner
3,181 Views
Thank you for your responds, but i still have problems because when i making : extern "C" { extern __declspec(dllimport) REAL r, t273, rt, z, yield; extern __declspec(dllimport) REAL dsq3,beta,betas,r0,bts,bt,w; extern __declspec(dllimport) REAL sigmainit, sigma_sse,e_c,e_xs,e_s,e_r,e_xr,sigma_ss,sigma_soft; /*these vairables are used only in one function but it seems there is no declaration anywhere in original Fortran code.*/ } extern "C" { struct flow_function { INTEGER n_function; }; extern __declspec(dllimport) struct flow_function flow_function; } I get " error LNK2001: unresolved external symbol " on every variable. I read that i should to link my project with the shell32.lib file. In errors every symbol have _imp prefix for example : _imp_flow_function. I understand where that come from ("__declspec(dllimport)") but wouldn't be there a problem in Fortran with names or something like that? I still have question how to connect these to projects one in fortran and one with this dll,? I am asking because dll need to import some stuff from Fortran files and probably this is making problems when i am building dll and i don't know how to link these fortran files . To be clear i know that when i will be building fortran files then need to have linkage to lib file of my dll . its everything so messy for me.
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
I successfully built and ran an example with a C main program calling a Fortran DLL. The C struct was declared as: [cpp] extern "C" __declspec(dllimport) struct { int FVAR; } f_common; [/cpp] and the corresponding Fortran was: [fortran] INTEGER(C_INT) :: FVAR COMMON /F_COMMON/ FVAR BIND(C) :: /F_COMMON/ !DEC$ ATTRIBUTES DLLEXPORT :: /F_COMMON/ [/fortran] C_INT comes from ISO_C_BINDING. Note that the name of the variable in the struct/common doesn't have to match.
0 Kudos
Patrick_K_1
Beginner
3,181 Views
I sorry but problem is about "Fortran main program calling C/C++ dll" . The problem lays in compiling c++ dll with imagined (for that moment) connection to fortran. Maybe i wrote this wrong in post before that. I need to take data from fortran Main program when i will use functions from C++ dll which i need to connect some how to the main fortran program. i'm so desparate about this all :P deadline is closer and closer.
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
Same thing - just swap the dllimport/dllexport keywords.
0 Kudos
Patrick_K_1
Beginner
3,181 Views
Hi i still have problem with compiling dll ,,, here is code of .h file : #ifndef LIBMNU_H #define LIBMNU_H typedef int INTEGER; typedef double REAL; typedef double DOUBLE_PRECISION; typedef int LOGICAL; #define SUBROUTINE extern "C" void __declspec(dllexport) __stdcall namespace libMNU { #ifdef __cplusplus extern "C" { #endif __declspec(dllimport) REAL r, t273, rt, z, yield; REAL dsq3,beta,betas,r0,bts,bt,w; //<<<<<------ i removed __declspec(dllimport) becouse this variables was undeclared in subroutines . i readed that they are real by default. REAL sigmainit, sigma_sse,e_c,e_xs,e_s,e_r,e_xr,sigma_ss,sigma_soft; __declspec(dllimport) struct { INTEGER n_function; } flow_function; __declspec(dllimport) struct coeff { REAL a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20; } coeff; __declspec(dllimport) struct compos { REAL carb, nitr, niob, vana, tita, mang, sili, chro, nick, phos, alum, moli, copp, sulp; } compos; #ifdef __cplusplus } #endif SUBROUTINE hard (double&,double&,double&,double&); void shida (double&,double&,double&,double&); void sinhiperb (double&,double&,double&,double&); void f06_cemef (double&,double&,double&,double&); void f11_sellars (double&,double&,double&,double&); void f15_hansel_spittel (double&,double&,double&,double&); void f16_hansel_spittel (double&,double&,double&,double&); } #endif in .cpp file are only functions logic i compiled it in visual studio and tested in MinGW . In visual studio still error LNK1120: 8 unresolved externals { error LNK2001: unresolved external symbol _imp_coeff ... . In MinGW i done this like that : C:\dll>g++ -c libmnu.cpp C:\dll>g++ -shared -o libmnu.dll libmnu.o -Wl,--out-implib,liblibmnu.a Creating library file: liblibmnu.a libmnu.o:libmnu.cpp:(.text+0xa): undefined reference to `_imp__compos' libmnu.o:libmnu.cpp:(.text+0x11): undefined reference to `_imp__compos' libmnu.o:libmnu.cpp:(.text+0x19): undefined reference to `_imp__compos' libmnu.o:libmnu.cpp:(.text+0x392): undefined reference to `imp(long double) libmnu.o:libmnu.cpp:(.text+0x3b3): undefined reference to `_imp__t273' libmnu.o:libmnu.cpp:(.text+0x3ba): undefined reference to `imp(long double) libmnu.o:libmnu.cpp:(.text+0x3c1): undefined reference to `_imp__t273' libmnu.o:libmnu.cpp:(.text+0x3ca): undefined reference to `_imp__rt' <---- and lot of stuff like this later i'm making this all on win32 project (empty dll), maybe this is problem or something ? i have one question: if main subroutine have own variables does called subroutines by this subroutine have access to variables of main subroutine ?
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
You're using g++ - all bets are off. That is not a compiler we claim compatibility with on Windows. Please use either Microsoft Visual C++ or Intel C++. I'd be glad to look at a ZIP of an example with complete Fortran and C++ sources that don't work with you.
0 Kudos
mecej4
Honored Contributor III
3,181 Views
I think that the OP has taken on a task involving mixed language linkage without adequately preparing himself. The overall objectives appear dubious -- to convert existing Fortran code into C++, build a DLL out of that C++ code, one routine in the DLL to be called from Fortran, and a Fortran common block to be shared with the DLL. Here, I hope, is something that will help: a minimal example (architecture: IA32) that captures what I see to be the intent. I made one change: pass "flow_function" as a subroutine argument rather than sharing it using a common block (unnecessarily complicated). In libmnu.h, remove "__stdcall" in the macro definition of SUBROUTINE, remove the declaration of "struct flow_function", and change the argument list to "hard" as noted above. In libmnu.cpp, change the argument list as above, and change flow_function.n_function to n_function. Build the DLL using Intel C or VC: [bash]icl /O2 /LD /I. libmnu.cpp [/bash] Build a small test program in Fortran to call the DLL: [bash]ifort callcpp.f90 libmnu.lib. [/bash] Here is the source code of the test program. [fortran] program callcppdll !DEC$ ATTRIBUTES DLLIMPORT, ALIAS:"_hard" :: hard real*8 e,edot,t,yield n_function=6 t=1d2 e=1d3 edot=1d4 call hard(e, edot, t, yield,n_function) write(*,*)n_function,yield end program callcppdll [/fortran] Examine the output to see if it is reasonable.
0 Kudos
Patrick_K_1
Beginner
3,181 Views
I think that I can't just like that upload these codes, because they are property of university. Privately probably i could but how can i send that to you Steve ? Mecej4 i will check this today and write about it.
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
You can use Intel Premier Support to provide files privately.
0 Kudos
Steven_L_Intel1
Employee
3,181 Views
You provided me sources separately. On the C++ side, it looks like you did things correctly. But the Fortran side has made no provision to export the desired variables from the DLL. In particular, you have this in the C++ code: extern "C" __declspec(dllimport) double r, t273, rt, z, yield; but the corresponding names in the Fortran code are just ordinary local variables in subroutines - these can't be made visible outside the routine. What you can do is create a module that declares the variables you want exported. For example, the Fortran side of the C++ declaration above would look something like this: [fortran] module globalvars use, intrinsic :: iso_c_binding implicit none real(C_DOUBLE), bind(C) :: r, t273, rt, z, yield !DEC$ ATTRIBUTES DLLEXPORT :: r, t273, rt, z, yield end module globalvars [/fortran] In your Fortran subroutines you would need to add: use globalvars to make these variables visible to the Fortran code. I hope this helps.
0 Kudos
Reply