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

Trouble having an Intel Fortran program call C++ functions

rkiseral
Beginner
1,027 Views

I am trying to link a very simple C++ function to a very simple Intel Visual Fortran program.

Fortran program looks like this (In a file called VFTestBed.f90):

 program VFTestBed

 integer pInteger

 pInteger = 11

 call SimpleTest1( pInteger )

 end program

Fortran interface block looks like this (in a file called interfaces.f90):

MODULE INTERFACES

interface

subroutine SimpleTest1( pInteger) 

!DEC$ATTRIBUTES DECORATE, ALIAS: "SimpleTest1"

integer pInteger

end subroutine SimpleTest1

end interface

END MODULE

C++ function looks like this (in a file called cppresponder.cpp):

#include <windows.h>
#include <sstream>

void SimpleTest1(int pInteger);

void SimpleTest1(  int pInteger)

{

      std::string pString = "";
      std::string pTitle = "";

      std::string pName = "SimpleTest1\0";

   pString = "Integer was entered.";
   pTitle = "In Routine: " + pName;

   MessageBoxA(NULL, pString.c_str(), pTitle.c_str(), MB_OK |    MB_ICONINFORMATION);

}        

I have researched this problem on Google and tried many dozens of permutations and combinations of various settings, declares, decorations, etc. all to no avail. Indeed, many of the posts were long and quite convoluted and often didn't seem to come to a successful conclusion.

Among other things I have tried:

  1. making the C++ code a .LIB
  2. making the C++ code a .DLL
  3. using various forms of !DEC$ATTRIBUTES DECORATE, ALIAS: "SimpleTest1"
  4. using BIND(C, ...)
  5. using plain aliases
  6. using decorated aliases
  7. used DUMPBIN to see the symbols in the .DLL
  8. using the preamble extern "C"
  9. compiling as C code (disables all the C++ constructs)

and a whole lot of other things. I've tried all the things that supposedly worked for other posters with absolutely no luck.

No matter what I do, I get a linker error message LNK2019 about an unresolved external symbol _SIMPLETEST1 referenced in function _MAIN__

As per the Intel site, I have added in CppResponder.DLL just like adding in a source file.

If it matters, I am using Visual Studio Enterprise 2017 and Intel Parallel Studio XE 2016 Update 4 composer Edition for Windows, all running on a 64 bit Windows 8.1 machine.

Assuming that Intel Fortran can indeed call a C++ function (I'm assuming it can) then I must be missing a switch or setting somewhere. I've set up both the Fortran and C++ projects with the defaults that Visual Studio provided. Both projects were setup as Release | x86.

Surely, this really can't be that hard of a thing to do. I've spent about 12 hours on this and have nothing to show for it. I have decades of experience with Fortran but am fairly new to C++.

Has anyone done this before and would be willing to walk me through how you did it?

Thanks in advance,

Bob Kiser

0 Kudos
10 Replies
jimdempseyatthecove
Honored Contributor III
1,027 Views

extern "C" void SimpleTest1(int pInteger);

Or if you are not using a function prototype

extern "C"
{

void SimpleTest1(int pInteger)
{
// code
}
}

Without the extern "C" the functions have decorations.

Jim Dempsey

0 Kudos
JVanB
Valued Contributor II
1,027 Views

A couple of tweaks made it work for me. I used BIND(C) and the VALUE attribute on the Fortran side and extern "C" on the C++ side. C++ will create mangled names that the Fortran compiler doesn't necessarily know how to produce without extern "C".

MODULE INTERFACES
   implicit none
   interface
      subroutine SimpleTest1( pInteger) bind(C,name='SimpleTest1')
      integer, value :: pInteger
      end subroutine SimpleTest1
   end interface
END MODULE

program VFTestBed
   use INTERFACES
   implicit none
   integer pInteger

   pInteger = 11
   call SimpleTest1( pInteger )
end program
#include <windows.h>
#include <sstream>

extern "C"
{
void SimpleTest1(int pInteger);

void SimpleTest1(  int pInteger)

{

      std::string pString = "";
      std::string pTitle = "";

      std::string pName = "SimpleTest1\0";

   pString = "Integer was entered.";
   pTitle = "In Routine: " + pName;

   MessageBoxA(NULL, pString.c_str(), pTitle.c_str(), MB_OK |    MB_ICONINFORMATION);

}        
}

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,027 Views

Also,

The above is not quite correct for your Fortran code, because the Fortran code declared the interface pInteger as pass by reference. To correct, either use int* in the C++ function, or in the Fortran interface: INTEGER(C_INT), VALUE :: pInteger

C_INT is defined using:

USE, INTRINSIC :: ISO_C_BINDING

o

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT

See IVF documentation "Standard Fortran and C Interoperability"

Jim Dempsey

0 Kudos
rkiseral
Beginner
1,027 Views

 

I appreciate the efforts of the commenters above, but unfortunately none of that worked, at least for me.

But after a lot more grief, here is what finally worked for me  Fortran:

 program VFTestBed
    
    use INTERFACES
    
    integer pInteger
    
    pInteger = 11
    
    call SimpleTest1( pInteger )
    
    end program








MODULE INTERFACES
       
interface

subroutine SimpleTest1( pInteger)

!DEC$ ATTRIBUTES  ALIAS:'?SimpleTest1@@YGXPAH@Z' :: SimpleTest1
 
integer pInteger

end subroutine SimpleTest1

end interface


END MODULE

And for the C++:
#define WIN32_DEFAULT_LIBS
#include <windows.h>
#include <iostream>
#include <sstream>


__declspec(dllexport) void SimpleTest1(int* pInteger);

	void SimpleTest1(int* pInteger)

	{

		std::string pString = "";
		std::string pTitle = "";

		std::string pName = "SimpleTest1\0";
		//-----------------------------------------------------------------------------------------------------------------


		pString = "Integer entered = " + std::to_string(*pInteger);
		pTitle = "In Routine: " + pName;

//		std::cout << "Integer entered = " << *pInteger << '\n';
		
		MessageBoxA(NULL, pString.c_str(), pTitle.c_str(), MB_OK | MB_ICONINFORMATION);


	}

 

Also, I had to give up on using a .DLL and instead compile the C++ code into a .LIB file.

With the excellent advice of Mr. Dempsey above, I changed the int to an int* in the C++ code.

You'll notice that I had to add in:     __declspec(dllexport)  and I used DUMPBIN to figure out the C++ name decoration.

Once I made all these changes, it compiled, linked and ran.

Hopefully, this will save some future reader from going through all the grief I went through to get such a simple test case like that to run.  I hope someone finds this useful.

 

0 Kudos
gib
New Contributor II
1,027 Views

I'm not very familiar with the Windowsy C++ functions, but this simple program pair works:

test.f90:

MODULE INTERFACES
   implicit none
   interface
      subroutine SimpleTest1( pInteger) bind(C,name='SimpleTest1')
      integer, value :: pInteger
      end subroutine SimpleTest1
   end interface
END MODULE

program VFTestBed
   use INTERFACES
	USE, INTRINSIC :: ISO_C_BINDING
   implicit none
   integer(C_int) :: pInteger

   pInteger = 11
   call SimpleTest1( pInteger )
end program

simple.cpp:

//#include <windows.h>
//#include <sstream>
#include <stdio.h>
extern "C"
{
//void SimpleTest1(int pInteger);
void SimpleTest1(  int pInteger)
{
 //  std::string pString = "";
 //  std::string pTitle = "";
 //  std::string pName = "SimpleTest1\0";
 //  pString = "Integer was entered.";
 //  pTitle = "In Routine: " + pName;
 //  MessageBoxA(NULL, pString.c_str(), pTitle.c_str(), MB_OK |    MB_ICONINFORMATION);
	printf("got: %d\n",pInteger);
}        
}

I first compiled simple.cpp

cl /c simple.cpp

then

ifort test.f90 simple.obj

to get test.exe

0 Kudos
FortranFan
Honored Contributor II
1,027 Views

rkiseral wrote:

.. unfortunately none of that worked, at least for me.

But after a lot more grief, here is what finally worked for me  ..

Also, I had to give up on using a .DLL and instead compile the C++ code into a .LIB file.

..

You'll notice that I had to add in:     __declspec(dllexport)  and I used DUMPBIN to figure out the C++ name decoration.

..

Hopefully, this will save some future reader from going through all the grief I went through to get such a simple test case like that to run.  I hope someone finds this useful.

@rkiseral,

There are some concerns with your comments as well as the code in your latest post and until they are cleared up, your post should *not* be considered as guidance for any future reader.

First, it is strange "none of that worked" in terms of earlier good advice you received - it should have.  You don't show what all you did to figure out where you were going wrong.

Secondly, if you "had to give up on using a .DLL", then your statement, "I had to add in:     __declspec(dllexport)" makes no sense.  Separately, there are no issues with using DLLs, sure given how things on Windows, one has to get the details right but it is not that difficult.

Re: "all the grief I went through", readers will note if one approaches standard facilities in Fortran systematically by going through Intel Fortran documention (link provided by Jim above), the sources in Dr Fortran blog (https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world​) , and some effort to do a "Google" search on this forum, a lot can be achieved in a better fashion.

Immediately I suggest using Quote #6 in the thread below as an initial go-by for invoking a C++ function in Fortran:

https://software.intel.com/en-us/node/699941#comment-form​

If I get some time later on, I'll see about a DLL example.

0 Kudos
rkiseral
Beginner
1,027 Views

@FortranFan

Well, "should have worked" wasn't good enough for me.  I actually did the leg work and found what worked and what didn't.  In my engineering business, we have a saying: "One test trumps 1000 opinions."

As for the __declspec(dllexport) and using .LIB vs .DLL stuff, try it for yourself and see what you get.  If you can get it to actually work your way, then post it here and show us how it is done.  As you noted, lots of details all have to be set up 100% exactly right to achieve success.  Get just one small thing wrong and you're sunk.  I found one such combination.  Maybe there are others.  Show me.

My many hours of searching on this site, StackOverflow and many others was quite fruitless.  To say that a lot could be achieved in a better fashion doesn't ring true.  Easy to say, tougher to do.

One thing I found over and over was the suggestion that Fortran couldn't directly deal with name decoration by C++.  I demonstrated that it could.

You're right, I didn't document the many dozens of things I tried over some 15 hours that didn't work.  Didn't have the time nor space.  I did take the time to spell out the one thing that did work for me in case it might prove useful to someone.  Like it or not, your choice.

At any rate, future readers can take it for what it is, or leave it.  It it helps them in some way to solve a problem, then good.  If it doesn't, then I' m sorry that it didn't work for them.

 

 

 

 

0 Kudos
mecej4
Honored Contributor III
1,027 Views

rkiseral, #5 wrote:

... unfortunately none of that worked, at least for me.

But after a lot more grief, here is what finally worked for me

We can mitigate the grief by being slightly more methodical. The steps are more or less the same as what you may have ended up with, but there is no need to try anything else that you are not certain about.

1. First, decide whether the C++ function could be replaced by an equivalent C function. If so, the function name mangling will be simple and even avoidable, if so desired. 

2. In either case, build the C/C++ DLL first.  For the C++ code in #5, which I put into file st.cpp, I did

S:\LANG> icl /LD st.cpp user32.lib

3. Find out the decorated name using the command

s:\LANG> dumpbin /exports st.dll

and note the decorated name

    ordinal hint RVA      name

          1    0 00001000 ?SimpleTest1@@YAXPAH@Z

4. Insert the decorated name in your DEC$ directive in the Fortran code. By default, IFort passes arguments by reference; if your C/C++ function expects otherwise, you have more adjustments to do on the C or Fortran sides (or both). Here is a shortened version of your Fortran code of #5 (file fmain.f90):

 program VFTestBed
!DEC$ ATTRIBUTES  ALIAS:'?SimpleTest1@@YAXPAH@Z' :: SimpleTest1
    integer pInteger
    pInteger = 11
    call SimpleTest1( pInteger )
end program

Note that I did not bother to provide an interface block since the implicit interface suffices.

5. Compile and link the Fortran code against the C/C++ export library that was produced in Step 2.

S:\LANG> ifort fmain.f90 st.lib

If you plan to do similar mixed language programming often and with larger projects, it would be worth your effort to use the more modern Fortran/C interoperability features of Fortran 200x instead of using the DEC$ approach. However, these interoperability features apply only to C, not to C++.

0 Kudos
JohnNichols
Valued Contributor III
1,027 Views

rkiseral wrote:

@FortranFan

Well, "should have worked" wasn't good enough for me.  I actually did the leg work and found what worked and what didn't.  In my engineering business, we have a saying: "One test trumps 1000 opinions."

As for the __declspec(dllexport) and using .LIB vs .DLL stuff, try it for yourself and see what you get.  If you can get it to actually work your way, then post it here and show us how it is done.  As you noted, lots of details all have to be set up 100% exactly right to achieve success.  Get just one small thing wrong and you're sunk.  I found one such combination.  Maybe there are others.  Show me.

My many hours of searching on this site, StackOverflow and many others was quite fruitless.  To say that a lot could be achieved in a better fashion doesn't ring true.  Easy to say, tougher to do.

One thing I found over and over was the suggestion that Fortran couldn't directly deal with name decoration by C++.  I demonstrated that it could.

You're right, I didn't document the many dozens of things I tried over some 15 hours that didn't work.  Didn't have the time nor space.  I did take the time to spell out the one thing that did work for me in case it might prove useful to someone.  Like it or not, your choice.

At any rate, future readers can take it for what it is, or leave it.  It it helps them in some way to solve a problem, then good.  If it doesn't, then I' m sorry that it didn't work for them.

 

 

 

 

 

Dear Sir:

I thought your comments were not that helpful and actually for an Australian sailing man to say this -- downright nasty. 

You are dealing with some of the best Fortran programmers in the world (I am not one, but these people help me a lot). They have never failed to help me. 

They never offer advice they have not tried.  I would suggest that if you spend hours researching it on other sites, you try the textbook, Intel publishes excellent PDF's that are free to download and have excellent examples. The last time I tried C++ to Fortran it took about an hour of reading. 

In terms of your rather interesting saying, as a person who was trained in physics at ANU (top 16 in the world) and I am not very good, the idea that a single experiment outshines some competent thought is not really statistically accurate. If you are using a random variable then one experiment may give you one answer, where it belongs in the PDF is anyone's guess as I teach my many research students, 134 at last count. I suggest you company needs to invest in a good library, personally a Fisher book and probably David's famous work would help explain why your statement is interesting., but not something to repeat in a public forum. 

If I offend you I apologize, but a touch of humility is lot better than raising a useless argument.

Speaking of the people who offered you their time, personally I am not sure which is the Top Gun, but if mecej4 cannot solve a problem is it probably unsolvable, Jim provides the longest comments and always helpful, repeat offender is a curmudgeon but one cannot but like him, and Fortran Fan is just the best to read. 

As for spoiling my last day in Italy in reading your comments -- thanks. 

John

 

0 Kudos
Devorah_H_Intel
Moderator
1,027 Views
I am going to lock this thread for the time being. (and for John to be able to enjoy his time in Italy). Bob, thank you for your contribution to this forum. We hope to see you participating in other Fortran topics as well.
0 Kudos
Reply