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

Loading Fortran function from dynamic library: Debug vs Release

Sanjana_T_
Beginner
735 Views

Hi, I'm trying to load a Fortran DLL and pass in values from C++ and am running into problems when my VS project is built in Release that I don't get when it's built in Debug.

I wrote some code to load a dynamic library that was compiled in Fortran. Among the exported functions in the Fortran library are exported setters and getters, which I'm attempting to load using the Windows API. While this works for my C++ code built in debug, it does not work with my code built in release. In release, when I attempt to pass 1.0f through to set() to pass into the fortran function, I'm seeing 0.0f in the debugger when I step into the fortran code.

If I only load the setValue() function, my problem completely goes away. Only when I load additional functions from the library do I start having the issues I'm seeing.

Compilers

  • Intel Fortran 2017 Update 5
  • Visual Studio 2017 v15.5.2

Debugging

I've determined that when my C++ code is compiled in release, my loaded set() function will pass 0.0f to the fortran function. This was discovered by loading the debug version of the fortran library and running it through Visual Studio's debugger. Performing the same thing with the debug build of my code yields the correct value getting passed through to the fortran function.

I've tried the following in the attempt to figure out what is happening:

  • Tried loading both Debug and Release binaries of the compiled Fortran in both Debug and Release builds of my loader. Both fortran builts work for the Debug build of my loader, but neither work for the Release build of the loader.
  • Rebuilt the Fortran code verifying compiler flags such as Mutithreaded Debug DLL for Debug and Multithreaded DLL for Release.
  • Performed a dumpbin of the fortran binary to verify function address are getting correctly loaded; they are.

edit: Edited code to show that the gets/sets were in a wrapper - when they aren't in the wrapper and directly in the test body, they all pass in Release and Debug.

FORTRAN

    REAL FUNCTION getValue()
!DEC$ ATTRIBUTES DLLEXPORT, c:: getValue
    IMPLICIT NONE
    INCLUDE 'VALUE.CMN'

    getValue = VAL
    RETURN
      END FUNCTION getValue

    SUBROUTINE setValue(x)
!DEC$ ATTRIBUTES DLLEXPORT, c:: setValue
    IMPLICIT NONE 
    INCLUDE 'VALUE.CMN'

    REAL, INTENT(IN) :: x

    VAL = x 

      END SUBROUTINE setValue

C++

Wrapper.cpp

typedef void(*Set)(float&);
typedef float(*Get)(void);

class Wrapper
{
public:
     Wrapper()
     {
          this->handle = reinterpret_cast<HMODULE>(LoadLibrary("fortLibName.dll"));
          if (this->handle != nullptr)
          {
               this->set = reinterpret_cast<Set>(GetProcAddress(handle, "setvalue"));
               this->get = reinterpret_cast<Get>(GetProcAddress(handle, "getvalue"));
          }
          //else, error checking code
     }
     HMODULE handle{nullptr};

     Get get{nullptr};
     Set set{nullptr};
}

Test.cpp

#include all the relevant includes

Wrapper wrapper;
auto value = 1.0f;

// 0.0f gets set to the internal fotran variable when this code is compile in release.
wrapper->set(value);

// Only succeeds when this code is compiled in Debug.
// get() returns 0.0f when this code is compiled in Release.
if(value == wrapper->get()) 
{
    std::cout << "Success!\n";
}
else
{
    std::cout << "Fail!\n";
}

I'm at a loss. Any thoughts or suggestions on what could be happening here?

0 Kudos
2 Replies
FortranFan
Honored Contributor II
735 Views

@Sanjana T.,

Without a zip of your solution folder and all the project and source files, it will be difficult to point out anything you can check; it would appear you are following all the right steps per Intel Fortran facilities for mixed-language programming.  So an alternative approach can be for you to try out the solution and the project in the attached zip and see if you can get it to work.  You can then decide whether to tweak your current solution to get it to work for you based on what you notice in the attached or take the one attached here to adapt it for your needs.  In the attached zip, you will note the following:

  • Fortran code utilizes standard features based on interoperability with a companion C processor: https://software.intel.com/en-us/fortran-compiler-18.0-developer-guide-and-reference-standard-tools-for-interoperability. ; Note the BIND(C..) attribute to overcome name mangling.  Fortran code is also made to be CONTAINed in a MODULE which can help greatly with unit tests of the Fortran DLL using  Fortran callers.  I generally tend to use Multithreaded library setting for both Debug and Release configurations in the Fortran DLL projects.
  • C++ wrapper class with typedefs for function pointers to the Fortran dll use the extern "C" specifier to bring into play the C processor and help with name mangling on the C++ side.
  • Note the use of the .DEF definition file with the Fortran DLL which I think is a better approach than DLLEXPORT compiler directives: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx

So the Fortran code:

module m

   use, intrinsic :: iso_c_binding, only : c_float

   implicit none

   private

   real(c_float), save :: val = 0.0_c_float

   public :: getValue
   public :: setValue

contains

   function getValue() result( ret ) bind(C, name="getValue")

      ! Function result
      real(c_float) :: ret

      ret = val

      return

   end function getValue

   subroutine setValue(x) bind(C, name="setValue" )

      ! Argument list
      real(c_float), intent(in) :: x

      val = x

      return

   end subroutine setValue

end module

C++ code: Wrapper.hpp header

#include "windows.h"

extern "C" {
   typedef void(*Set)(float&);
   typedef float(*Get)(void);
}

class Wrapper
{
public:
   Wrapper()
   {
      this->handle = reinterpret_cast<HMODULE>(LoadLibrary("Fdll.dll"));
      if (this->handle != nullptr)
      {
         this->set = reinterpret_cast<Set>(GetProcAddress(handle, "setValue"));
         this->get = reinterpret_cast<Get>(GetProcAddress(handle, "getValue"));
      }
      //else, error checking code
   }
   HMODULE handle{ nullptr };

   Get get{ nullptr };
   Set set{ nullptr };
};

C++ Test main:

#include <iostream>
#include <string>
#include "Wrapper.hpp"
using namespace std;

int main()
{
   Wrapper wrapper;
   auto value = 42.0f;

   wrapper.set(value);

   if (value == wrapper.get())
   {
      std::cout << "Success!\n" << "value = " << value << "\n";
   }
   else
   {
      std::cout << "Fail!\n";
   }
}

Upon execution of the executable created by the Visual Studio project file in the attached solution, whether for Debug or Release configuration, the output I see is:

Success!
value = 42

 

0 Kudos
FortranFan
Honored Contributor II
735 Views

Also, you can look into the VALUE attribute introduced in Fortran as part of the standard interoperability facility with C:

https://software.intel.com/en-us/fortran-compiler-18.0-developer-guide-and-reference-value

Using the VALUE attribute, you can bring into the play the common pass-by-value convention for function parameters in C++ if that is of interest.  That is you can do:

   typedef void(*Set)(float);

and on the Fortran side,

   subroutine setValue(x) bind(C, name="setValue" )

      ! Argument list
      real(c_float), value :: x

      val = x

      return

   end subroutine setValue

and if you don't intend the dummy argument to be on the left-hand side of an assignment, you can even include INTENT(IN) attribute for good effect.

0 Kudos
Reply