Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Calling Fortran Functions/Subroutines From MSVC++ programs

1 view
Skip to first unread message

Tim Wilkens

unread,
Sep 24, 2001, 1:45:25 PM9/24/01
to
Hi,
I am trying to call a subroutine from a MSVC++ program. However,
I do not know how to do this. I am sure it's been posted on this
group at some time in the past.. and I apologize for not looking over
the many postings concerning this issue. However, I really need some
assistance and think it's rather simple to do.. once you have done it.
Could someone enlighten me concerning it. I've made a very simple
example.. in which a dotproduct function is called from a C++ program.
Here is the source for each of the 2 files:

modvect.cpp
========================================================================

#include <iostream>
using namespace std;

extern double __stdcall dotproduct (int n, double x[],double y[]);

//****************
// MAIN PROGRAM
//****************
int main()
{ int n;
// Size Of Vectors
const int vect_size = 10;
double y[vect_size];
double dot_prod;

for(n=0;n<vect_size;n=n+1) {
y[n] = float(vect_size - n);
}

dot_prod = dotproduct(vect_size,y,y);

cout << " Square Magnitude of y is: " << dot_prod << endl;
return 1;

}

========================================================================

Fortran function "dotproduct.f90"
========================================================================
REAL*8 FUNCTION dotproduct(n,x,y)
IMPLICIT NONE
INTEGER, INTENT(in) :: n
REAL*8, DIMENSION(n), INTENT(in):: x
REAL*8, DIMENSION(n), INTENT(in):: y
INTEGER :: i

dotproduct=0.0
DO i=1,n
dotproduct = dotproduct + x(i)*y(i)
ENDDO

return
END FUNCTION dotproduct
========================================================================

Any help is greatly appreciated. If you help, could you please
correct the two files in your post with the changes required to
successfully execute this program. Thanks again..

Tim Wilkens Ph.d.

David Yasko

unread,
Sep 24, 2001, 2:53:22 PM9/24/01
to
Hello,

I just got a load of help for doing the opposite, calling C/C++ routines
from FORTRAN. I am using MS VisualC++ and Digital/Compaq Visual Fortran.
In a nutshell:

C/C++
-------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
long cfunc(long aLongIn, long* aLongOut);
#ifdef __cplusplus
};
#endif

long cfunc(long aLongIn, long* aLongOut){
*aLongOut = aLongIn;
return 0;
}

FORTRAN
-------------------------------------
INTERFACE
INTEGER*4 FUNCTION cfunc(alongin, alongout)
!DEC$ATTRIBUTES C :: cfunc
!DEC$ATTRIBUTES ALIAS : "_cfunc" :: cfunc
!DEC$ATTRIBUTES VALUE :: alongin
!DEC$ATTRIBUTES REFERENCE :: alongout
INTEGER*4 alongin
INTEGER*4 alongout
END FUNCTION
END INTERFACE

PROGRAM fprog
INTEGER*4 longout
INTEGER*4 result
result = cfunc(4, longout)
END

If I didn't make any stupid mistakes, the following to code snippets should
compile cleanly. Then, stepping through the fortran code should cause
"result" to equal 0 and "longout" to equal 4. There's a bunch more that can
be done, but it should be easy enough to do the reverse.

Also, the alias attribute in the function interface definition may not be
needed. I've had success with and without it. There's more going on there
that I haven't looked into yet,

I imagine your program would work if you define an interface similar to the
following:

INTERFACE
REAL*8 FUNCTION dotproduct(n, x, y)
!DEC$ATTRIBUTES C :: dotproduct
!DEC$ATTRIBUTES ALIAS : "_dotproduct" :: dotproduct
!DEC$ATTRIBUTES VALUE :: n
!DEC$ATTRIBUTES REFERENCE :: x
!DEC$ATTRIBUTES REFERENCE :: y
INTEGER*4 n
REAL*8, DIMENSION(n) x
REAL*8, DIMENSION(n) y
END FUNCTION
END INTERFACE

I hope I didn't miss anything. Good luck.

--
David Yasko
"The day after tomorrow is the third day of the rest of your life."


"Tim Wilkens" <wil...@uiuc.edu> wrote in message
news:7b1e74d1.01092...@posting.google.com...
: Hi,

Arjen Markus

unread,
Sep 25, 2001, 2:43:38 AM9/25/01
to


If you want to solve it as portably as possible, then I suggest the
following:

#ifndef WIN32
#define DOTPRODUCT dotproduct
/* Or: dotproduct_ */
#define FORCALL
#else
#define FORCALL __stdcall
#endif

extern double FORCALL DOTPRODUCT (int *n, double x[],double y[]);
^^^^^^^ ^^^^^^^^^^ ^^

Notes:
- The keyword __stdcall is a typical MS Visual C++ extension to
solve low-level calling convention problems.
- The Fortran name "dotproduct" is translated to uppercase by the
Compaq Visual Fortran compiler. Other compilers may translate
the name to lowercase and append one or two underscores. Hence
using a macro makes your code more portable.
(The WIN32 macro is used to distinguish between Windows and other
platforms)
- Fortran makes no distinction between passing by value or passing
by reference. All arguments are passed by reference. Hence you
need a pointer at the C++ side of things.
- If you want to pass strings, you have to deal with other conventions
and extra arguments (invisible on the Fortran side).
- As C/C++ provides standardly all kinds of facilities for preprocessing
and Fortran does not (as most languages by the way), put all the
specific details on the C++ side, rather than rely on
platform-dependent
extensions of the Fortran compiler.
- that is, if portability is at all an issue and even changing the
brand of compiler on the same hardware is. Sometimes you have to
worry about portability between versions of the same compiler!


Regards,

Arjen Markus

Carsten A. Arnholm

unread,
Sep 29, 2001, 6:57:49 AM9/29/01
to
"Arjen Markus" <Arjen....@wldelft.nl> wrote in message
news:3BB0279A...@wldelft.nl...

>
> If you want to solve it as portably as possible, then I suggest the
> following:
>
> #ifndef WIN32
> #define DOTPRODUCT dotproduct
> /* Or: dotproduct_ */
> #define FORCALL
> #else
> #define FORCALL __stdcall
> #endif
>
> extern double FORCALL DOTPRODUCT (int *n, double x[],double y[]);
> ^^^^^^^ ^^^^^^^^^^ ^^

or alternatively

#include <fortran.h> // ref. my web page
DOUBLE_PRECISION_FUNCTION DOTPRODUCT(INTEGER& N, DOUBLE_PRECISION* x,
DOUBLE_PRECISION* y);


>
> Notes:
> - The keyword __stdcall is a typical MS Visual C++ extension to
> solve low-level calling convention problems.

This is true. It is best disguised in a #define macro in a separate header
file. This macro could/should also deal with the linkage convention isse.
C++ normally creates internal names for all functions, allowing several
functions to use the same name, as long as the number and/or types of
arguments are unique ("name mangling"). To be able to call code generated by
a compiler that does not use this convention (e.g. C or Fortran), you need
to tell that to the C++ compiler using the extern "C" linkage convention
specification. This can conveniently be hidden in a SUBROUTINE or FUNCTION
macro.

> - The Fortran name "dotproduct" is translated to uppercase by the
> Compaq Visual Fortran compiler. Other compilers may translate
> the name to lowercase and append one or two underscores. Hence
> using a macro makes your code more portable.
> (The WIN32 macro is used to distinguish between Windows and other
> platforms)

Another approach which can be even more portable, is to always use uppercase
names without defining macros. When the fortran compiler translates the name
to something else than simple uppercase, an automatically generated stub
function can be used to translate between the conventions. Such a stub
function can also solve other issues, typically how CHARACTER strings are
passed. Using the uppercase macro trick are not sufficient in these cases.

> - Fortran makes no distinction between passing by value or passing
> by reference. All arguments are passed by reference. Hence you
> need a pointer at the C++ side of things.

I used to say this also. Someone corrected me by saying that the language
does not require variables to be passed by reference, but it is certainly
true for the compilers I have used. The statement that you need pointers at
the C++ side of things is not accurate. Doing that would be more C style
than C++ style. Unlike C, C++ explicitely supports passing variables by
reference. IMHO, this should be preferred in C++ when calling Fortran, as
the source code becomes much cleaner and a closer match to fortran compilers
that pass variables by reference:

double sine(double* angle); // pass pointer to angle, possible in C and
C++ (pointer is passed by value here)
double sine(double& angle); // pass angle by reference, possible in C++
but not in C

> - If you want to pass strings, you have to deal with other conventions
> and extra arguments (invisible on the Fortran side).

Ref. my comments above. I have found that a C++ CHARACTER class can help a
lot here.

> - As C/C++ provides standardly all kinds of facilities for preprocessing
> and Fortran does not (as most languages by the way), put all the
> specific details on the C++ side, rather than rely on
> platform-dependent
> extensions of the Fortran compiler.

I agree 100%

> - that is, if portability is at all an issue and even changing the
> brand of compiler on the same hardware is. Sometimes you have to
> worry about portability between versions of the same compiler!

Indeed. Portability is mostly a question of which compiler combinations are
used. When you use other hardware, you normally use other compilers. On a
system such as Windows, where many language implementations (i.e. compilers)
exist, you can have portability issues without changing hardware.


--
Carsten A. Arnholm
arn...@online.no
http://home.online.no/~arnholm/
N59.783 E10.500


David Young

unread,
Oct 5, 2001, 4:29:43 PM10/5/01
to
HI, All!

I am also trying to develop some mixed-language program using VC++6 and
CVF. The primary difficulties for me is to pass string from C++ to
Fortran 90 and I have the following problem:

C++
-------------------------------------------------
extern "C" { void __stdcall TEST_FOR(char *c);}

int main(){
char *c;
c=new char[10];
TEST_FOR();
return 0;
}

Fortran 90
-------------------------------------------------
subroutine TEST(Name)
!DEC$ ATTRIBUTES C:: TEST
!DEC$ ATTRIBUTES ALIAS : "_TEST" :: TEST
!DEC$ ATTRIBUTES REFERENCE :: Name

character*(*)::Name

return
end subroutine TEST

----------------- end ------------------------

I keep geting errors like
error LNK2001: unresolved external symbol _TEST@4
fatal error LNK1120: 1 unresolved externals

Could you please help me out? Thank you very much!

Best Regards,

David

Tim Prince

unread,
Oct 5, 2001, 7:02:52 PM10/5/01
to
So make the C and Fortran names agree. Run dumpbin /symbols on the .obj
files to see where they don't agree. Read your CVF docs, including the
following:
Character arguments are passed as follows:

a.. By default:


a.. On OpenVMS, Tru64 UNIX, and Linux systems, hidden lengths are put at
the end of the argument list.


b.. On Windows systems, hidden lengths immediately follow the variable.
To get the OpenVMS, Tru64 UNIX, and Linux behavior, use the
/iface:nomixed_str_len_arg compiler option.

"David Young" <davi...@netscape.net> wrote in message
news:3BBE1837...@netscape.net...

Arjen Markus

unread,
Oct 8, 2001, 3:08:39 AM10/8/01
to

The problem is twofold:
- The Fortran compiler adds @number-of-bytes to the internal name of the
routine. Because a string actually consists of TWO arguments, one
the string itself and the other, invisible to the Fortran side of
things, the length of the string (as returned by len(string)), the
number of bytes is 8.

One part of the solution therefore is:

extern "C" { void __stdcall TEST_FOR(char *c, int len_c);}
^^^^^^^^^

Note:
For CVF/VC++ this hidden argument comes directly AFTER the string
argument. This may not be true with other Fortran compilers.

I would keep the Fortran code as clean as possible:

SUBROUTINE TEST_FOR( string )
CHARACTER*(LEN=*) :: string
...
END SUBROUTINE

The attributes make it somewhat difficult to see the effect.

In these cases I always handle the conversion of Fortran strings
to C strings explicitly.

Hope this helps,

Arjen Markus

Jugoslav Dujic

unread,
Oct 8, 2001, 5:07:58 AM10/8/01
to
"David Young" <davi...@netscape.net> wrote in message
news:3BBE1837...@netscape.net...
| HI, All!
|
| I am also trying to develop some mixed-language program using VC++6 and
| CVF. The primary difficulties for me is to pass string from C++ to
| Fortran 90 and I have the following problem:
|
| C++
| -------------------------------------------------
| extern "C" { void __stdcall TEST_FOR(char *c);}
|
| -------------------------------------------------
| subroutine TEST(Name)
| !DEC$ ATTRIBUTES C:: TEST
| !DEC$ ATTRIBUTES ALIAS : "_TEST" :: TEST
| !DEC$ ATTRIBUTES REFERENCE :: Name
|
| character*(*)::Name
|
| return
| end subroutine TEST
|
| ----------------- end ------------------------
|
| I keep geting errors like
| error LNK2001: unresolved external symbol _TEST@4
| fatal error LNK1120: 1 unresolved externals
|
| Could you please help me out? Thank you very much!
|
| Best Regards,
|
| David


You mismatched calling conventions (heavily). First, you've declared
test in C as __stdcall TEST_FOR, which is mangled by C side as
_TEST_FOR@4 [stdcall]. On fortran side, you declared it as TEST with
__cdecl convention, which is mangled as _TEST [cdecl]. So, you have
to match them in some of the following manners:

a)


extern "C" { void __stdcall TEST_FOR(char *c);}

subroutine TEST_FOR(Name)
!DEC$ ATTRIBUTES REFERENCE :: Name
!No alias necessary; no stdcall necessary since it's default

b)
extern "C" { void __cdecl TEST_FOR(char *c);}

subroutine TEST_FOR(Name)
!DEC$ ATTRIBUTES C:: TEST_FOR
!DEC$ ATTRIBUTES REFERENCE :: Name
!No alias necessary (I think)

c)

extern "C" { void __stdcall foobar(char *c);}

subroutine TEST_FOR(Name)
!DEC$ ATTRIBUTES ALIAS: "_foobar@4":: TEST_FOR
!DEC$ ATTRIBUTES REFERENCE :: Name

HTH
Jugoslav


________________________
www.geocities.com/jdujic

0 new messages