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

C++ and Fortran source in one Project

weingart__daniel
Beginner
1,245 Views

Hi,

in front i´m complete new with Fortran.

So my Problem is: We have an realy old Software Project. It was generatet with Compaq Visual Fortran 6.1  and includes Source Files in *.cpp and *.for.

From my point of view, it was extremly confortable to compile  and Debug this Project in one step by pressing F5.

My question is: Is there a way to do the same with VisualStudio and IntelParallelstudio XE 2016?

In best case i would like to convert my Compaq c++ DSW to VisualStudio.

Until now i only found examples where a Fortran dll was use by a c++ Programm. IBut i would like to create a dll  from c++ and Fortran sourcefiles.

 

Best 

Daniel

 

0 Kudos
24 Replies
Arjen_Markus
Honored Contributor I
1,099 Views

Visual Studio doesn't allow source files for different programming languages in one project. So you should split the set of source files into two or more separate projects. However, by setting the dependencies correctly, all you need to do is build the topmost project and all dependencies are automatically taken care of.
 

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Suppose I build the C++ as a dll within the project and attempt to connect to it via Fortran.  I have had luck doing this with: 1) C++, and 2) C#.  The Fortran poses unique problems.

In order to remove the name mangling of the C++, I declared its exports with EXTERN_C.  But there are limitations to this approach which I do not fully comprehend.  The attempt was to create a C++ class with a static function to export.  I cannot seem to get the syntax quite right because everything compiles but the linkage fails with several complaints about missing external symbols.  With compilation being Win32 Debug in both projects, the first complaint is

Error    2     error LNK2001: unresolved external symbol @_RTC_CheckStackVars@8    

with similar complaints following.  I know this has been pursued elsewhere, but I have USED code from former forum posts to build the example without luck.

 

0 Kudos
Arjen_Markus
Honored Contributor I
1,099 Views

This could be a matter of differing calling conventions - CDECL versus STDCALL. Typically for STDCALL: the number of arguments (or rather the total size in bytes) is encoded in the name - the @8 bit in your case.

Could you show us a code example that exhibits this problem? (Say, a Fortran program that calls the single C++ routine in the DLL)

 

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

That reference is to a MSVC library routine, though the leading @ seems wrong. Can you show us a small but complete example that demonstrates the problem?

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Attached is a zip of the solution.  I am using VS2013 with Intel Fortran 16.0.2.180 [IA-32].

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

Most of the error messages are because you changed the Fortran project's linker property "Ignore all default libraries" to Yes. Set that to No.

What you're left with is the reference to  _GetLastErrorMessage which you have defined in the C++ project as a class member. This won't work. To call C++ code from Fortran the C++ routine has to be declared with 'extern "C"' and not part of a class. Remember, it's C interoperability, not C++ interoperability.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Thank you for the heads up on the "No" for "Ignore All Default Libraries".  I built the exported dll function as not static and EXTERN_C.

The complaint is still

Error    1     error LNK2019: unresolved external symbol _GetLastErrorMessage referenced in function _MAIN__ 

What linkage step have I left out?  "Additional Dependencies"?  I am noting the C++ dll is created within the Debug output directory.

 

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

You didn't do the 'extern "C"' thing. You also never DLLEXPORTed the C++ routine, nor did you tell the Fortran project to link in the C++ project's export library.

Here's what you need to do:

  1. Right click on the Fortran project, select Build Dependencies > Project Dependencies. Check the box for the C++ project and click OK
  2. In your C++ code, change the function header to:     extern "C" __declspec(dllexport) char* GetLastErrorMessage()
  3. Right click on the Fortran project, select Properties. Go to Linker > Input. Change the Configuration dropdown to All Configurations. In the Additional Dependencies property, put in: $(OutDir)\PassingStuffToFromCpp.lib

Normally this last step would not be needed, but there's a bug in MSVC where it won't make a C++ DLL project visible to Fortran as a link dependency.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

I followed those steps completely.  Per usual, I have no proof because it continues not to link.  I am attaching my latest version of the code.  It bonks with

Error    2     fatal error LNK1107: invalid or corrupt file: cannot read at 0x2F0    Debug\PassingStuffToFromCpp.dll    

I know you said to use PassingStuffToFromCpp.lib as the Additional Dependencies, but that lib file is not created in the build of my C++ project.

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

One doesn't link to a DLL directly. Always the .LIB.

You didn't do step 2.

By the way, you don't need the DllMain routine if you're not going to do anything in it.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

But I did do step 2, didn't I?  My header file code reads

namespace PassingToFro
{
	EXTERN_C __declspec(dllexport) char* GetLastErrorMessage();
}

Is this not what was mentioned in step 2?

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

Not in the ZIP you attached.

// PassingStuffToFromCpp.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"

namespace PassingToFro
{
	char HelloWorld[12];
	char* GetLastErrorMessage()
	{
		strcpy_s(HelloWorld, 12, "Hello World");
		return HelloWorld;
	}
}

Your not adding __declspec(dllexport) prevents the .lib from being created.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Now I'm confused.  Where does the __declspec(dllexport) phrase lie?  And where does the extern "C" phrase lie?  I put both in the header file.  Should I have put them in the source and not in the header file?

Then the real question to be asked is, if I am using somebody else's library who has used appropriate export statements and entry points, how do I access their library?  At present all I have is a dll file with "appropriate" entry points, but I have been completely unable to make anything link.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Okay, now I have discovered the inherent foolishness of my approach.  All it would have taken is to add the line

#include "PassingStuffToFromCpp.h"

to the file PassingStuffToFromCpp.cpp.  Then your method would have worked without trouble.

Forgive my mistake.

The question remains, how does one connect Fortran source to a dll?  I am connecting to the lib file without error.  Do I need the lib files for third party software to make this work?

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

Nothing is looking at the header file. You put these in the function itself. If you are using a header file you could also put them there, but that's not going to fix your problem on its own.

If all they give you is a DLL, you cannot link to it. I have seen tools that will construct an export library from a DLL, or you can use LoadLibrary and GetProcAddress - see the DLL\DynamicLoad example we provide.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

Thank you for the help.  I am now pursuing the "DLL\DynamicLoad" sample.  I have one question: would a Fortran program be capable of catching an exception thrown by an external dll?

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

A DLL is just a collection of procedures. There's no difference in behavior of calling a routine in a DLL or one in a static library (or even in its own objects.)

I will note though that Fortran has no exception handling capability.

0 Kudos
NotThatItMatters
Beginner
1,099 Views

The calling of a routine in a DLL is posing some challenges.  I first conquered the calling of routine

extern "C" { __declspec(dllexport) double Add(double a, double b); }

using the Fortran abstract interface

FUNCTION ADD_double(a, b)
    USE iso_c_binding, ONLY : c_double
    REAL (KIND = c_double) :: ADD_double
    REAL (KIND = c_double), INTENT(IN) :: a, b
END FUNCTION ADD_double

and the declarations

PROCEDURE (ADD_double), POINTER :: ADD
REAL (KIND = c_double) a, b, ret

and the CALL sequence

ret = ADD(%VAL(a), %VAL(b))

I guess this one demonstrated the call-by-reference/call-by-value distinction in the two languages.  However, the calling of routine

extern "C" { __declspec(dllexport)  char * Subtract(double a, double b); }

using the Fortran abstract interface

FUNCTION SUBTRACT_double(a, b)
    USE iso_c_binding, ONLY : C_PTR, c_double
    TYPE (C_PTR) :: SUBTRACT_double
    REAL (KIND = c_double), INTENT(IN) :: a, b
END FUNCTION SUBTRACT_double

and the declarations

PROCEDURE (SUBTRACT_double), POINTER :: SUBTRACT
TYPE (C_PTR) ret_sub

and the CALL sequence

ret_sub = SUBTRACT(%VAL(a), %VAL(b))

did not work.  Debugging this one saw that the input values attained in the C++ Subtract function were not equal to the values sent by the Fortran.  What have I omitted in the calling of the second routine (Subtract)?  This example is based summarily on the C++ example posed in

https://msdn.microsoft.com/en-us/library/ms235636.aspx

0 Kudos
Steven_L_Intel1
Employee
1,099 Views

First of all, don't use %VAL. Instead, add the VALUE attribute to the dummy arguments a and b in the interface.

I can't tell what went wrong because you have not shown a complete program - you haven't even shown the contents of the C Subtract function (there is no such function on the MSDN page you linked to.) How are a and b declared in the caller?

It would be best if you'd attach a ZIP of the complete solution (do a Build > Clean first) so we can see the whole picture.

0 Kudos
NotThatItMatters
Beginner
908 Views

Attached is the entire project: C++ dll, C++ driver for dll, C# driver for dll and Fortran driver for dll.  The Fortran driver is a reproduction of the sample DLL\DynamicLoad.  I tried applying the "VALUE" attribute rather than INTENT(IN) in the ABSTRACT INTERFACE without any luck.  The code comment reflects this.

As for the code not following the Microsoft sample, I guess this is Microsoft's newest sample and I had used an older one.  Sorry.

0 Kudos
Reply