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 Beiträge / 0 neu
Letzter Beitrag
Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.

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.

Bild des Benutzers Steve Lionel (Intel)

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

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.

Bild des Benutzers Steve Lionel (Intel)

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

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.

Bild des Benutzers Steve Lionel (Intel)

Same thing - just swap the dllimport/dllexport keywords.

Steve

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 ?

Anlagen: 

AnhangGröße
Herunterladen libmnu.cpp4.25 KB
Herunterladen libmnu.h1.28 KB
Bild des Benutzers Steve Lionel (Intel)

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
Bild des Benutzers mecej4

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.

Bild des Benutzers Steve Lionel (Intel)

You can use Intel Premier Support to provide files privately.

Steve
Bild des Benutzers Steve Lionel (Intel)

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

Melden Sie sich an, um einen Kommentar zu hinterlassen.