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,203 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
Schwandt__Olaf
Beginner
835 Views

Hi mecej4,

 

thank you for your answer.

Yes, I do have the .lib-file (the name is PPSystem.lib and I have it in "additional dependency" in "configuration properies".)

About Line 73 in #19 you wrote, that my linker problems 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". But what can I do?

 

Olaf

0 Kudos
mecej4
Honored Contributor III
835 Views

The key points are to make pErrorHandlerFct a pointer and to make it point to the external routine ErrorHandlerFct. The former is a variable whose value is some routine address. The latter is a constant address whose value is supplied by the linker.

The statement

     pErrorHandlerFct => ErrorHandlerFct

in #19 copies the linker-supplied address into the pointer variable, and that variable can then be passed as the last argument to fhpt(). The argument is of the correct type, is a variable as the interface specifies, and has been set to point to the correct target.

P.S. I now realize that it may be confusing to follow advice from two people at the same time. Please follow Steve's directions. I am sure that he will help you resolve the problems that you have been experiencing.

0 Kudos
Steve_Lionel
Honored Contributor III
835 Views

You didn't do what I suggested. Replace this line:

 myH2S, myN2, myO2, mySO2, pErrorHandlerFct))

with:

 myH2S, myN2, myO2, mySO2, ErrorHandlerFct))

You will have no reference to pErrorHandlerFct in your code. Instead you are referencing the module procedure ErrorHandlerFct. There is no need for a pointer.

Please do this to help us diagnose the link issue. Open a Fortran Build Environment command prompt (in the Start menu under Intel Parallel Studio 20xx, Compiler 19.x for IA-32). Set default (cd) to the folder containing PPSystem.lib. If this is a static library, type the command:

dumpbin -symbols -exports PPSystem.lib > lib.txt

Attach lib.txt to a reply here.

0 Kudos
Schwandt__Olaf
Beginner
835 Views

 Hi Steve, hi mecej4,

 

in wich line you mean the changing from myH2S, myN2, myO2, mySO2, pErrorHandlerFct)) to myH2S, myN2, myO2, mySO2, ErrorHandlerFct)).

And if it is in my main program (ig. #17, line 79 or 80), wich definition of the variable I do need?

 

For the diagnose earliest I have time tomorrow.

 

Greetings, Olaf

 

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Here you find atteched the lib.txt file

the interface to _fhpt includes 124 bytes (fifteen 8 byte values and one 4 byte pointer)

caution: you forgot ig. in #16 two values (p and t) in the function parameter list

0 Kudos
Steve_Lionel
Honored Contributor III
835 Views

Yes, I did miss the p and t arguments. As I guessed, your library assumes the STDCALL interface. Use this instead:

    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
      !DEC$ ATTRIBUTES STDCALL :: 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

It was line 80 that I suggest you modify, in conjunction with the code I supplied. You will not have any reference to pErrorHandlerFct except in the interface block.

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Steve, it works!!!!!!!!!!!!!!!!!

At least with including the STDCALL --> !DEC$ ATTRIBUTES STDCALL :: fhpt in the interface definition (how you wrote in #27) the linking was without problems and I get the accurate result now. I'm so happy.

There is only one thing (or two things) witch would be useful in addition: If I could read the errorhandler (at lest the value "code") I could react. The variable "errorhandlerFct" has currently no data type. And if there would be a solution, I could use IMPLICIT NONE in my main progam. Do you know how?

But wether with the content of the errorhandler or without: thank you Steve, thanks even to mecej4

 

Olaf

 

ps: Here is my full code now:

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
     !DEC$ ATTRIBUTES STDCALL :: 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

    double precision :: fhtp, p, t, myWATER, myAR, myC2H6, myC3H8, myCH4, myCO, myCO2, myH2, &
                        myH2O, myH2S, myN2, myO2, mySO2

    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, ErrorHandlerFct))

end program prop_C_to_Fort_01

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
835 Views

>>If I could read the errorhandler (at lest the value "code") I could react.

In your post #15 you show an example of what you pass in for "code".

This illustrates that you have defined the values for "code".

*** your possible use of code:

! after you capture error_string
if(code >= 0) then
  ! fatal error code
  ... ! any shutdown code
  STOP "Fatal Error Message Here"
endif
! code < 0 == recoverable error
... ! any error logging code
return

That is just a suggestion.

Example use

... (after saving error_string)
if (abs(d) ==0)
{
  pErrorHandlerFct(0, “Division by zero!”);
}
if (abs(d) =< 1.e-12)
{
   pErrorHandlerFct(-1, “Division overflow”); // log warning
   ReturnValue = DBL_MAX; // or use some very large special value that you define
                          // e.g. 9999999999.99
}
else
{
  ReturnValue = a/d; // #15 had /b in error
}

Jim Dempsey

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Hi Jim,

 

no, I asked about the program who calls the c-function. There (in the main programm "prop_C_to_Fort_01") I like to read the feedback "ErrorHandlerFct". Currenty this variable has not a data type declaration and because of that no value in my main.

 

Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
835 Views

ErrorHandlerFct is not a variable, it is a subroutine defined in the module I had you create. The subroutine stores the error code and the error string in module variables error_code and error_string, which you can reference after the call to fhpt.

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Yes Steve, that's really great, your informations are all really great

 

direct access to error_code and error_string in the main I didn't get in the debugger (although I have a use to the fhpt_mod), but with

    ecode=error_code
    estring=error_string

I have access with no problem. But not really without any problem: the program crashes rigorous, if ig. wrong values are handled down to the c-function.

Can I intercept that?

 

Olaf

0 Kudos
Steve_Lionel
Honored Contributor III
835 Views

No, you can't intercept what happens in the C function.

In the debugger you can do a QuickWatch on fhpt_mod::error_code, etc.

Remember that you'll want to reset error_code to -1 before each call to fhpt.

0 Kudos
Schwandt__Olaf
Beginner
835 Views

OK, thank you, thank you for all

 

Olaf

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Hi Steve,

 

if ig. a wrong value is handled down to the c-routine, it does not crash. It only gives back something via the ErrorHandlerFct (I asked the dealer of the program wich includes the c-routine).

So the crash maybe comes from the connecton to fortran.

Attached you see the crash report in the debugger, but there is no other possibilities to get informations from the debugger.

Do yo have some further idea what I can do?

 

Greetings, Olaf

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Steve again, sorry,

 

there is a new problem with the whole program:

After including the STDCALL --> !DEC$ ATTRIBUTES STDCALL :: fhpt (look #28, line19) I got the right return value in
line 79 yesterday.

But as I recognized today it works only in the debugger linking, not in release linking.

While release linking the errors are:

fatal error LNK1120: 1 nicht aufgelöste Externe    Release\prop_C_to_Fort_02.exe            
error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_fhpt@124" in Funktion "_MAIN__".    prop_C_to_Fort_02.obj         

But I need this version, would you help me again?

 

Olaf 

       

0 Kudos
Steve_Lionel
Honored Contributor III
835 Views

When you added the library to Additional Dependencies, the default was that it was only for the Debug configuration. You need to do it again for Release. Next time, for something like this, select "All Configurations".

The error is an access violation. There are many possible causes of this, not something one can diagnose from a screenshot.

0 Kudos
Schwandt__Olaf
Beginner
835 Views

Hi Steve, thank you again, this solved my problem on point, thanks.

 

Olaf

0 Kudos
Reply