Visual C++ and Fortran

Visual C++ and Fortran

At the following website:
http://www.compaq.com/fortran/examples/vc-example1.html

The site gives an example of mixed-language programming with a Microsoft Visual C/C++ main program calling a Visual Fortran routine in a DLL. It says you can ALSO do it WITHOUT the DLL by adding the Fortran source to the same project as the C/C++ source (and omitting the DLL-related directives).
Does anyone know how to do it this way? I assume you have to declare the fortran function in the C++ code somewhere. If so, how?

21 post / 0 nuovi
Ultimo contenuto
Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione

Yes, your Fortran functions must be declared in a header file using

extern "C" type __stdcall ROUTINE_NAME(arg-list);

Replace type with return type or void for subroutines. ROUTINE_NAME must be uppercase.

If you dislike uppercase (as I do -- you'll have to use uppercase throughout C++ code), you can use !DEC$ATTRIBUTES ALIAS directives, for example:

SUBROUTINE SetFoo(iFoo, fFoo)
!DEC$ATTRIBUTES ALIAS: "_SetFoo@8":: SetFoo
INTEGER:: iFoo
REAL:: fFoo

extern "C" void __stdcall SetFoo(int&, float&); 

However,note that if you use ALIAS in this way, Fortran routines that call SetFoo (if any) also have to have explicit interface (via INTERFACE blocks or USE association); otherwise you'll get LNK2001s.

HTH
Jugoslav

Jugoslav www.xeffort.com

What do you mean when you say that "everything" has to be in caps in C ? Do you just mean the variables that I pass to Fortran?
I tried writing a simple test program, but I keep getting this warning:

--------------------Configuration: test1 - Win32 Debug--------------------
Compiling...
test1main.cpp
Linking...
LINK : warning LNK4098: defaultlib "libc.lib" conflicts with use of other libs; use /NODEFAULTLIB:library

test1.exe - 0 error(s), 1 warning(s)

When I run the program, when it gets to the part where the fortran code needs a variable passed from the C code, the program crashes and gives the following error:

Application Error
The instruction as "0x0041dca9" referenced memory at "0x0000000c". The memory could not be "read".

Do you know what I'm doing wrong? I attached the very simple source files. Great thanks for your help.

Fortran produces routine names that are uppercased, so you either use those default names from C or you need to tell Fortran to use an alias as described in the first response.

Your warning usually comes from mixing libraries, see recent thread about link error LNK4098.

Your application error is a programming error, you passed your subroutine the value rather than the address of the integer. Change your prototype and calling code to pass the address instead, your Fortran subroutine is expecting an integer passed by reference.

James

Can you tell me how to include or exclude libraries with the linker in MS Visual C++ 6.
Microsoft's online documention only tells how to do this with .NET stuff, which I don't have.
Also, why would there be library conflicts if I'm simply only including and using only cout and cin ?
Thanks

As the error message indicated, /nolibrary:libc.lib would work in your case. You likely have a problem referencing the debug libraries and the default libraries at the same time. Try building a release version rather than a debug version, for example, you shouldn't see the link error then.

James

I don't think I built either of those two versions (release or debug), at least I don't know if I did. I created a new Win32 Console Application, empty project.
Could you please tell me specificaly what I can do? I'm using MS Visual C++ 6
thanks so much

With the visual IDE you have a build configuration in effect, and according to your second post in this thread you were using Win32 Debug as your configuration. Try going to Build -> Set Active Configurations... to see your options. Try Win32 Release.

James

By default, CVF expects all arguments to be passed by reference, i.e. int* or int&. Take care about character variables -- they require two consecutive arguments, one char* and one int (length, by value).

By uppercase I meant only Fortran routine names.

Jugoslav www.xeffort.com

Thanks. Can you explain specifically how to pass character string variables as arguments to fortran subroutines?
For example if I have a character array in fortran declared as:
character*80 cards(2)
So this holds two 80-byte strings.
How would I do this in the C++ declaration of the function and how would I call it?

Each formal argument will appear as two actual arguments from C++, the address of your character string (char *) and the value of the length of the string (unsigned int). Prototype something like:

extern "C" void __stdcall FORCALL (char *string, unsigned int string_length )

The only relevance using the array of strings should have is they need to be of equivalent size.

James

The easiest way is to give it an ALIAS. Take care about stdcall name mangling (underscore prefix and @+4*number of arguments as suffix):

MODULE MyMod
CONTAINS
SUBROUTINE MyRoutine(f,i)
!DEC$ATTRIBUTES ALIAS: "_MyRoutine@8":: MyRoutine
...

Since ALIASed name and C++ are both case-sensitive, C++ declaration will be:

void __stdcall MyRoutine(float,int)

Jugoslav www.xeffort.com

1) Yes, the C++ doesn't know anything about modules. C++ does have similar mechanisms (precompiled headers and namespaces) but they're incompatible with modules. Actually, the whole point is in .mod file generated by the Fortran compiler -- it contains all info related with module exports and enables USErs of the module to take advantage of argument checking, global declarations & definitions etc. However, .mod files are not standardized -- they're different even for e.g. CVF 5 and CVF 6.

2) CVF decorates module procedure names as _MODULENAME_mp_ROUTINENAME@n (again, case matters). Thus, you could declare routine of appropriate name in C as

extern "C" void __stdcall MODULENAME_mp_ROUTINENAME(...

. Note that you can also reach private routines -- in Fortran, hiding is done so that the routine is not listed in .mod file as export. It still appears in module's .obj file and you can even cheat to access it from Fortran in the same manner:

INTERFACE
   SUBROUTINE PrivateSub(i)
   !DEC$ATTRIBUTES ALIAS: "_MODNAME_mp_PRIVATESUB@4":: PrivateSub
   INTEGER i
   END SUBROUTINE
END INTERFACE

Jugoslav

Jugoslav www.xeffort.com

1) Probably, I'm not sure

2) A propos assumed-shape arrays:
a) Don't

b) If you really have to, create a Fortran wrapper which will receive the array as assumed-size. Assumed-size array is just a float* (int*, void*)(if it's a matrix, pass the Fortran leading / C ending dimension as an additional argument) and pass it further as assumed shape. (Btw, it just struck my mind that creating a Fortran wrapper would be a more ellegant solution to the previous problem -- accessing module variables)

c) If you really, really want to use assumed-shapes from C++ as-is, search for "array descriptor" in CVF Help. There's also a sample called IIRC Descript.f90.
Shortly, you'll have to pass a pointer to a descriptor structure to Fortran. That won't be easy though. In any case, take care about row-major vs. column-major order of matrices in Fortran/C++.

Jugoslav www.xeffort.com

For the @n suffix, n is the number of bytes pushed on the stack prior to the call that the called routine is to pop before returning. This is four bytes (32-bit addresses and values) per actual argument, with the following exceptions:

1. CHARACTER arguments count double (address and length passed)
2. FUNCTIONS that return pointers, arrays, derived types, CHARACTER (and maybe COMPLEX*16, but I'd have to look) pass the return value as a hidden additional argument (two for CHARACTER).

Steve

Steve - Intel Developer Support

Can you please say where the hidden return values are. I couldn't
find it in the cvf programmers guide. Do the address/length pair for
a returned string go before or after the rest of the parameter data.

thank you
sol gongola

For functions with a hidden return, the hidden argument is the first actual argument. For CHARACTER arguments, the length immediately follows the address unless you have specified the "after all args" option for this. This should be detailed in the Programmer's guide section on mixed-language programming.

Steve

Steve - Intel Developer Support

Yes you are, if by "overloaded procedure" you refer to "generic name" as it's usually called (as opposed to "specific name"). Generic names don't appear anywhere except in .mod file; references to specific procedures are resolved at compile time (thus the requirement that argument-lists must be unambiguously different). Note that names that appear in .obj files are specific names, i.e. they have to differ throughout (thus you cannot have two routines of the same name in one project*).

Don't hold my word firmly for the info about C++ overloading/name mangling. My conclusion is that, since in C++ there are no modules but overloading is allowed (i.e. having different functions of the same name), it would be impossible to disambiguate calls to say (remind you, this is allowed in C++):

void DoSomething(int, float);
void DoSomething(char*, int*);

If they both are mangled as _DoSomething (@8), how's linker supposed to know which one to pull in? The only solution is to put a unique signature into linker symbol name.
___________
*Actually it's possible but using some cheats; due to stdcall convention same names may be mangled differently. (but note that it's Windows-specific; Unixen always use cdecl). Another trick is playing with linker's command line.

Jugoslav

Jugoslav www.xeffort.com

The Programmer's Guide goes into quite a bit of detail on how to access module variables from other languages.

I don't understand your question about assumed-sized and assumed (deferred) shape arrays. Can you come up with a simple example?

Steve

Steve - Intel Developer Support

I (obviously) erred :-) -- I meant "procedures" not "variables" (you really have to caught every word :-))).

as for assumed-size arrays - my understanding is passing such an array into a procedure expecting assumed-shape arrays causes copying in/out. Is that (always) the case?

No -- you got it wrong (reverse). You mustn't pass anything to an assumed-shape dummy unless having an explicit interface; once you have explicit interface, no copy in/out will occur. Actually, it's all about contiguity. A copy in/out may occur if you pass a (potentially) non-contiguous array as an assumed-size dummy, e.g.

SUBROUTINE Caller
REAL, POINTER:: p(:)
ALLOCATE(p(Something))
CALL Callee(p)  !here, it may happen
DEALLOCATE(p)
p=>A(1:10:2)
CALL Callee(p)  !here, it will certainly happen
END SUBROUTINE Caller
!======
SUBROUTINE Callee(x)
REAL:: x(*)
...
END SUBROUTINE Callee

CVF has greatly improved in this regard. In the second call, p is certainly discontiguous and a copy in/out must occurr. In the first call, p is contiguous; if a compiler is sufficiently smart, it can perform a run-time contiguity test and not do copy in/out if the actual argument is contiguous as in first call. Early versions (5.0D) did a copy in/out even if you pass x(1:n) where x is not a pointer. Steve could comment better on improvements in this field.

The solution I proposed in my previous post will not invoke copy in/out; to repeat, it cannot occur for an assumed-shape dumy, only for assumed-size.

Jugoslav

Jugoslav www.xeffort.com

We believe that the 6.6A compiler avoids unnecessary copies in all but the most obscure cases.

Steve

Steve - Intel Developer Support

Lascia un commento

Eseguire l'accesso per aggiungere un commento. Non siete membri? Iscriviti oggi