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

mixing a FORTRAN program with C++ functions

Schwandt__Olaf
Beginner
2,236 Views

Hi all,

I have an Intel Visual FORTRAN Compiler together with an Intel C++ Compiler (parallel studio xe 2018 professional) in the Microsoft Visual Studio Professional 2017.

Usually I program in FORTRAN 95, But I have some codes in c from other sources. Because of that I need to integrate c-functions in my fortran programm, 

But I don't know how. I know there is an example in the mixed language help (fortran calles c). But there is no instruction how to start (how to create this in VS) and build this and on the other hand the example is about subroutines and not about functions. Iin addition some functions have errorhandler, (some not). So I need to know how I can realize solving both types

Would someone help me?

 

Olaf

 

0 Kudos
37 Replies
Steve_Lionel
Honored Contributor III
1,394 Views

Create your Fortran project/solution with the Fortran part normally. While you have that solution open, select File > Add > New Project. Select Visual C++ > Windows Desktop > Static Library. Add your C sources to that (and probably delete the other stuff it creates for you.) You'll probably want to turn off Precompiled Header support. Add your C source(s) to that project.

Now right click on the Fortran project, select Dependencies > Project Dependencies. Check the box for your C++ project.

You'll now want to study the material on C interoperability in Fortran to see how to declare the C routine in Fortran.

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Thank you very much Steve, I will try and tell you then how it works or/and contact you for additional questions.

Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

Study the various project settings of the Fortran_Calls_C sample, and read the README.TXT file included.

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Hi Steve,

 

sorry for my (maybe stupid) method and my asking again

I get an Error Massage while repeating the fortran_calls_c example. (At first I tried this for understanding the problem)

I show you the steps I made

  1. With “File/New project” I create a new project with the Intel Visual Fortran Console Application – Main Program Code (name: mixed_05)
  2. I copy and paste the FORTRAN-Code from fmain.f90 in fortran_calls_c to the application from no. 1
  3. File > Add > New Project  Visual C++ > Windows Desktop > Static Library (name: StaticLib1.cpp)
  4. I copy and paste the cpp-Code from Csub.cpp in fortran_calls_c to the application from no. 3
  5. I correct the project-explorer, now it shows the same content as in fortran_calls_c,  but I don’t know, what this is what I means
  6. I right click on the Fortran project Mixed_05, Dependencies > Project Dependencies, setting box at staticLib1.
  7. Create Mixed_05 with new creating

But there are two Errors while compiling:

  • fatal error LNK1181: Eingabedatei "C:\Users\schwandt\Documents\Visual Studio 2017\Projects\Mixed_05\Mixed_05\Debug\StaticLib1.lib" kann nicht geöffnet werden. (LINK)                               
  • C1010   Unerwartetes Dateiende während der Suche nach dem vorkompilierten Header. Haben Sie möglicherweise vergessen, im Quellcode "#include "pch.h"" einzufügen? (staticlib1.cpp)
  • otherwise (when I complete my c-code with #include <pch.h>) C1083    Datei (Vorkompilierte Headerdatei) kann nicht geöffnet werden: "Debug\StaticLib1.pch": No such file or directory    c:\users\schwandt\documents\visual studio 2017\projects\mixed_05\mixed_05\staticlib1\staticlib1.cpp   

I dont know, what to do and I don’t know where and why I need the pch.h header in difference to fortran_calls_c

 

Can you please help me again?

Olaf

 

 

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

one complement:

the original fortran_calls_c works with compiling and running without any problem

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

I don't see that you disabled precompiled header support, as I suggested above, as this is what it is complaining about.

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

ok, thank you, I didn't understand all in detail what you wrote before

now everythink is ok,thanks

I have still one question: If the c-routine is in a dll. how can I call this from fortran? (I do have the .lib from this dll, maybe this is helpful)

 

Olaf

0 Kudos
mecej4
Honored Contributor III
1,394 Views

Yes, when you build an application (EXE) that calls routines from the DLL, you link the OBJs of the application with the import library (xxxx.LIB) that was generated when you built the DLL. For details, read the section "Create a client app that uses the DLL" at https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019 .

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

thank you mecej4

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

There's a catch when the C++ code is in a DLL - you can't rely on the project dependencies to link in the dependent .LIB (Visual Studio mis-feature), so you'll have to add in the DLL project's .LIB to your Fortran project's linker "additional dependencies". This isn't the case for static libraries (yet).

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Hi Steve,

 

 

thank you for your help.

 

Today I tried to realize the function calling from a dll. To my regret I was not able.

I do not understand your comment in detail again. You wrote (as I understood) my only chance to use the c-function in the dll from my FORTRAN routine is to define the .lib file in the additional dependencies. But this is the same I found in the link of mecej4. But how I do that? I entered the fully path and name of the .lib-file in the additional dependencies and then I tried to call the c-function in my FORTRAN-source. It didn’t work.

In the sample of fortran_calls_c they work with a subroutine. But what is the difference to a function with an error handler. I need this one.

The dealer of the dll with the C-function insight sad I should call the routine like this:

typedef void (CALLBACK* HANDLERPROC)(int , const char* );

extern "C"
{   

double EXTERN_FUNCTION fhpt(double p, double t,
                                        double myWATER,
                                        double myAR,
                                        double myC2H6,
                                        double myC3H8,
                                        double myCH4,
                                        double myCO,
                                        double myCO2,
                                        double myH2,
                                        double myH2O,
                                        double myH2S,
                                        double myN2,
                                        double myO2,
                                        double mySO2,
                                        HANDLERPROC pErrorHandlerFct);
}
 

But he couldn’t tell me how to call it from FORTRAN. From the errorhandling I only need an error code, not a spevial function

thank you, Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

Olaf, you have not shown what you tried, just said "it didn't work". You will need an interface block for the C++ routine. I will show you what an interface to fhpt would look like, but I don't understand your comment about an error code. As you show it here, fhpt (I assume) calls a user function in case of an error, so you would have to write that, and to tell you what that looks like I'd need to know the definition of HANDLERPROC.

So, here is a suggested interface:

interface
  function fhpt (myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2O, &
                        myH2S, myN2, myO2, mySO2, pErrorHandlerFct) BIND(C)
  use, intrinsic :: ISO_C_BINDING
  real(C_DOUBLE) :: fhpt
  real(C_DOUBLE), VALUE, INTENT(IN) :: myWATER
  real(C_DOUBLE), VALUE, INTENT(IN) :: myAR
  real(C_DOUBLE), VALUE, INTENT(IN) :: myC2H6
  real(C_DOUBLE), VALUE, INTENT(IN) :: myC3H8
  real(C_DOUBLE), VALUE, INTENT(IN) :: myCH4
  real(C_DOUBLE), VALUE, INTENT(IN) :: myCO
  real(C_DOUBLE), VALUE, INTENT(IN) :: myCO2
  real(C_DOUBLE), VALUE, INTENT(IN) :: myH2
  real(C_DOUBLE), VALUE, INTENT(IN) :: myH2O
  real(C_DOUBLE), VALUE, INTENT(IN) :: myH2S
  real(C_DOUBLE), VALUE, INTENT(IN) :: myN2
  real(C_DOUBLE), VALUE, INTENT(IN) :: myO2
  real(C_DOUBLE), VALUE, INTENT(IN) :: mySO2
  type(C_FUNPTR), VALUE :: pErrorHandlerFct
  end function fhpt
end interface

Before you use this I need to know what HANDLERPROC is defined as, or documentation on what the function should be.

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Steve,

 

I meaned, the mixed language programming workes. it can be compiled and workes (runs). But this is not with a third part dll. I compiles both pats together, successful after I realized at least your tip: disable precompiled header support,

but now I have another task: I like to use in my fortran progam a c-function witch is locatet in third party dll

I will try now your interface and then I tell you how it workes

I think, I inform you tomorrow, because I need at first to ask, what is the errorhandler in detail. Now here it is half past 9 in the evening.

thank you for your patience with me.

(With my comment about an error code I mean, it gives back more then I need I need to have an informatio, if the result is correct or not)

 

Olaf

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Hi Steve,

 

it’s me again, Olaf.

You helped me a lot and your tips were very useful. But I have one additional question:

As you know, the c-function has a callback-function to handle the errormassage (HANDLERPROC pErrorHandlerFct). Next I show you a typical construction of the c-routine with the line wich forms the errormassage -- pErrorHandlerFct(0, “Division by zero!”); -- insight (it's taken from a hadbook)


 

typedef void (CALLBACK* HANDLERPROC)(int, const char*);

double EXTERN_FUNCTION Div(double c, double d, HANDLERPROC pErrorHandlerFct);
double EXTERN_FUNCTION dDiv_dc(double c, double d, HANDLERPROC pErrorHandlerFct);
double EXTERN_FUNCTION dDiv_dd(double c, double d, HANDLERPROC pErrorHandlerFct);

// The implementation the external of the function “Div” can look like this:

double EXTERN_FUNCTION Div(double c, double d, HANDLERPROC pErrorHandlerFct)
{
double ReturnValue = 0;
if (abs(d) =< 1.e-12)
{
pErrorHandlerFct(0, “Division by zero!”);
}
else
{
ReturnValue = a/b;
}
return ReturnValue;
}

Steve, you defined the call of the errorhandler in the interface as

             type(C_FUNPTR), VALUE :: pErrorHandlerFct

 

But I have to define it in Fortran to as the compiler reported:

“error #6404: This name does not have a type, and must have an explicit type.   [PERRORHANDLERFCT]“

 

I need both the error code and the error text - int, const char* - e.g. - 0, “Division by zero!”

 

How I do that?

 

Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

First, I see that I left out myH2 in the argument list...

Here is what I suggest. In a separate source file, call it fhpt_mod.f90, put in this:

module fhpt_mod
    use, intrinsic :: iso_c_binding
    implicit none
    
    abstract interface
        subroutine HANDLERPROC (code, string) BIND(C)
        import
        integer(C_INT), VALUE :: code
        character, dimension(*) :: string
        end subroutine HANDLERPROC
    end interface
    
    
    interface
      function fhpt (myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, myH2O, &
                            myH2S, myN2, myO2, mySO2, pErrorHandlerFct) BIND(C)
      import
      real(C_DOUBLE) :: fhpt
      real(C_DOUBLE), VALUE, INTENT(IN) :: myWATER
      real(C_DOUBLE), VALUE, INTENT(IN) :: myAR
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC2H6
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC3H8
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCH4
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2O
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2S
      real(C_DOUBLE), VALUE, INTENT(IN) :: myN2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: mySO2
      procedure(HANDLERPROC) :: pErrorHandlerFct
      end function fhpt
    end interface
    
    ! Variables that hold the last value from an error handler function
    integer(C_INT) :: error_code = -1
    integer, parameter :: max_error_string_len = 80
    character(max_error_string_len) :: error_string = ''
    
    contains
    
    subroutine ErrorHandlerFct (code, string) BIND(C)
      integer(C_INT), VALUE :: code
      character :: string(*)
      
      integer i
      
      ! Store code
      error_code = code
      
      ! Determine C string length
      error_string = ""
      do i=1,max_error_string_len+1
          if (string(i) == C_NULL_CHAR) exit
          error_string(i:i) = string(i)
      end do
      
    end subroutine ErrorHandlerFct
   
end module fhpt_mod

In the procedure that calls fhpt, don't put in the interface I suggested earlier but instead add:

use fhpt_mod

right after the PROGRAM, SUBROUTINE or FUNCTION statement.

Now when you call fhpt, pass ErrorHandlerFct as the last argument. After the call, you can reference variables error_code and error_string. If you call fhpt more than once you'll want to reset error_code to -1 (and test it after the call to see if there was some error.)

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Hi Steve,

thank you again. A big thank you. It looks very interesting and very plausible.

But I get two errors while linking. Maybe I do a mistake and you find it. To make this easier I'll show you the complete source code with a very simple test main program,

module fhpt_mod
    use, intrinsic :: iso_c_binding
    implicit none
    
    abstract interface
        subroutine HANDLERPROC (code, string) BIND(C)
        import
        integer(C_INT), VALUE :: code
        character, dimension(*) :: string
        end subroutine HANDLERPROC
    end interface
    
    
    interface
      function fhpt (p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, myH2O, &
                            myH2S, myN2, myO2, mySO2, pErrorHandlerFct) BIND(C)
      import
      real(C_DOUBLE) :: fhpt
      real(C_DOUBLE), VALUE, INTENT(IN) :: p
      real(C_DOUBLE), VALUE, INTENT(IN) :: t
      real(C_DOUBLE), VALUE, INTENT(IN) :: myWATER
      real(C_DOUBLE), VALUE, INTENT(IN) :: myAR
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC2H6
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC3H8
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCH4
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2O
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2S
      real(C_DOUBLE), VALUE, INTENT(IN) :: myN2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: mySO2
      procedure(HANDLERPROC) :: pErrorHandlerFct
      end function fhpt
    end interface
    
    ! Variables that hold the last value from an error handler function
    integer(C_INT) :: error_code = -1
    integer, parameter :: max_error_string_len = 80
    character(max_error_string_len) :: error_string = ''
    
    contains
    
    subroutine ErrorHandlerFct (code, string) BIND(C)
      integer(C_INT), VALUE :: code
      character :: string(*)
      
      integer i
      
      ! Store code
      error_code = code
      
      ! Determine C string length
      error_string = ""
      do i=1,max_error_string_len+1
          if (string(i) == C_NULL_CHAR) exit
          error_string(i:i) = string(i)
      end do
      
    end subroutine ErrorHandlerFct
   
    end module fhpt_mod
    
!****************************************************************************

program prop_C_to_Fort_01

    use fhpt_mod
!    IMPLICIT NONE
    double precision :: os_cpp_fhpt, fhtp, p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, &
                        myH2O, myH2S, myN2, myO2, mySO2
    procedure(HANDLERPROC) :: pErrorHandlerFct

    p=1.0; t=50.0; myWATER=1.0; myAR=0.0; myC2H6=0.0; myC3H8=0.0; myCH4=0.0; myCO=0.0
    myCO2=0.0; myH2=0.0; myH2O=0.0; myH2S=0.0; myN2=0.0; myO2=0.0; mySO2=0.0


    os_cpp_fhpt = dble(fhpt (p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, myH2O, &
                            myH2S, myN2, myO2, mySO2, pErrorHandlerFct))

end program prop_C_to_Fort_01

 

And the error messages while linking are here. (Maybe I did something wrong with the sequence.)

error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_fhpt" in Funktion "_MAIN__".
error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_perrorhandlerfct" in Funktion "_MAIN__".

 

We are (I am) only one little step away from the correct solution. I think. It would be very nice again if you would help me once again.

Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
1,394 Views

1) Remove your declaration of pErrorHandlerFct (line 73) and replace that same name in the call with ErrorHandlerFct

2) It looks as if you need to link in whatever library supplies fhpt. I had assumed it was your C++ library. I wonder, though, if CALLBACK is defined as a __stdcall interface. Can you find the definition of CALLBACK?

0 Kudos
mecej4
Honored Contributor III
1,394 Views

Here is a modified version of your Fortran driver and a skeleton C source for building a DLL.

File olaf.f90:

module fhpt_mod
    use, intrinsic :: iso_c_binding
    implicit none
    
    abstract interface
        subroutine HANDLERPROC (code, string) BIND(C)
        import
        integer(C_INT), VALUE :: code
        character, dimension(*) :: string
        end subroutine HANDLERPROC
    end interface
    
    
    interface
      function fhpt (p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, myH2O, &
                            myH2S, myN2, myO2, mySO2, pErrorHandlerFct) BIND(C)
      import
      real(C_DOUBLE) :: fhpt
      real(C_DOUBLE), VALUE, INTENT(IN) :: p
      real(C_DOUBLE), VALUE, INTENT(IN) :: t
      real(C_DOUBLE), VALUE, INTENT(IN) :: myWATER
      real(C_DOUBLE), VALUE, INTENT(IN) :: myAR
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC2H6
      real(C_DOUBLE), VALUE, INTENT(IN) :: myC3H8
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCH4
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO
      real(C_DOUBLE), VALUE, INTENT(IN) :: myCO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2O
      real(C_DOUBLE), VALUE, INTENT(IN) :: myH2S
      real(C_DOUBLE), VALUE, INTENT(IN) :: myN2
      real(C_DOUBLE), VALUE, INTENT(IN) :: myO2
      real(C_DOUBLE), VALUE, INTENT(IN) :: mySO2
      procedure(HANDLERPROC) :: pErrorHandlerFct
      end function fhpt
    end interface
    
    ! Variables that hold the last value from an error handler function
    integer(C_INT) :: error_code = -1
    integer, parameter :: max_error_string_len = 80
    character(max_error_string_len) :: error_string = ''
    
    contains
    
    subroutine ErrorHandlerFct (code, string) BIND(C)
      integer(C_INT), VALUE :: code
      character :: string(*)
      
      integer i
      
      ! Store code
      error_code = code
      
      ! Determine C string length
      error_string = ""
      do i=1,max_error_string_len+1
          if (string(i) == C_NULL_CHAR) exit
          error_string(i:i) = string(i)
      end do
      
    end subroutine ErrorHandlerFct
   
    end module fhpt_mod
    
!****************************************************************************

program prop_C_to_Fort_01

    use fhpt_mod
!    IMPLICIT NONE
    double precision :: os_cpp_fhpt, fhtp, p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, &
                        myH2O, myH2S, myN2, myO2, mySO2
    procedure(HANDLERPROC), pointer :: pErrorHandlerFct

    p=1.0; t=50.0; myWATER=1.0; myAR=0.0; myC2H6=0.0; myC3H8=0.0; myCH4=0.0; myCO=0.0
    myCO2=0.0; myH2=0.0; myH2O=0.0; myH2S=0.0; myN2=0.0; myO2=0.0; mySO2=0.0

    pErrorHandlerFct => ErrorHandlerFct
    os_cpp_fhpt = dble(fhpt (p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, myH2O, &
                            myH2S, myN2, myO2, mySO2, pErrorHandlerFct))

end program prop_C_to_Fort_01

File fhpt.c:

#include <stdio.h>

double fhpt(double p, double t, double myWATER, double myAR, double myC2H6,
   double myCH4, double myCO, double myCO2, double myH2, double myH2O, 
   double myH2S, double myN2, double myO2, double mySO2, void (*pErrorHandlerFct)(int, const char *)){
printf("In FHPT: p, T, mySO2 = %e %e %e\n",p,t,mySO2);
return(1.2345);
}

Building the DLL (+its LIB), the EXE and running at the command line:

icl /LD fhpt.c /link /export:fhpt
ifort olaf.f90 fhpt.lib
S:\LANG>olaf
In FHPT: p, T, mySO2 = 1.000000e+00 5.000000e+01 0.000000e+00

 

0 Kudos
Schwandt__Olaf
Beginner
1,394 Views

Hi Steve,

 

1) at first I removed line 73, but then the error massage while linking is:

error #6636: When a dummy argument is a subroutine, the corresponding actual argument must also be a subroutine.   [PERRORHANDLERFCT]

2) I have no farther information about the content of the c-function in the dll. But on monday I can ask the the distribution company. Maybe they have the source-code of this c-function. So they could tell me about the definition of the CALLBACK function with the HANDLERPROC.

Or I write an intermediate function (in c++), wich has a defined interface. Then I can hand over no procedure but only variables: amongst other an integer and a string for the evaluation of the error(s)

 

Hi mecej4,

 

thank you, but as I already said I have no information about the the c-function without the definition of the interface. But with your program piece as the mentioned intermediate function your idea can help. At first I'll try the method of Steve and if there are farther problems I'll try your code.

 

Olaf

0 Kudos
mecej4
Honored Contributor III
1,381 Views

You do have information about the interface to the C function fhpt; you listed the C interface in #12. The vendor of the DLL should have provided the matching LIB file to go with the DLL. Or, you may have to generate the import library from the DLL.

Once you have the import library file, you simply have to use the name of that *.LIB file instead of fhpt.lib in the second command at the end of #19.

Most vendors of commercial libraries are extremely selective about sharing the source code of their products, so you are unlikely to be given access to the source code of the DLL.

The linker problems that you list in #19 relate to using an external symbol (which is a constant address once the program has been loaded) and a pointer to the same external symbol, on the Fortran side. See line-73 of olaf.f90 in #19.

0 Kudos
Reply