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

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

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.

13 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.

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.

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.

Steve - Intel Developer Support

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.

I successfully built and ran an example with a C main program calling a Fortran DLL. The C struct was declared as:


extern "C" __declspec(dllimport) struct {

	int FVAR;

} f_common;

and the corresponding Fortran was:


INTEGER(C_INT) :: FVAR

COMMON /F_COMMON/ FVAR

BIND(C) :: /F_COMMON/

!DEC$ ATTRIBUTES DLLEXPORT :: /F_COMMON/

C_INT comes from ISO_C_BINDING. Note that the name of the variable in the struct/common doesn't have to match.

Steve - Intel Developer Support

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.

Same thing - just swap the dllimport/dllexport keywords.

Steve - Intel Developer Support

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 ?

Fichiers joints: 

Fichier attachéTaille
Télécharger libmnu.cpp4.25 Ko
Télécharger libmnu.h1.28 Ko

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.

Steve - Intel Developer Support

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:

icl /O2 /LD /I. libmnu.cpp


Build a small test program in Fortran to call the DLL:
ifort callcpp.f90 libmnu.lib.

Here is the source code of the test program.


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

Examine the output to see if it is reasonable.

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.

You can use Intel Premier Support to provide files privately.

Steve - Intel Developer Support

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:


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

In your Fortran subroutines you would need to add:

use globalvars

to make these variables visible to the Fortran code. I hope this helps.

Steve - Intel Developer Support

Laisser un commentaire

Veuillez ouvrir une session pour ajouter un commentaire. Pas encore membre ? Rejoignez-nous dès aujourd’hui