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

Mixing C code in a Fortran Project?

chstoyer
Beginner
2,358 Views
I have a small C program which converts between IEEE floating point and the old IBM float format (which are actually remarkably similar, though not the same). In Visual Studio 2000 and CVF, I just added it to the project and Visual Studio figured it out, compiled it and linked it.

Visual Studio 2010 seems to recognize it as C code, the text colors are correct and the icon in the file view tree shows it as a C program. However it does not seem to compile. I did buy the VS 2010 and Help/About does show the C++ compiler.

Do I need a custom build step and what is that?

Or is there another way to convert between IEEE and the old IBM floating point formats?

Thanks,

Charles
0 Kudos
13 Replies
Steven_L_Intel1
Employee
2,358 Views
That was Visual Studio 98 you used with CVF.

Microsoft no longer supports mixed-language projects, so the way you have to do this now is create a Solution with two projects, one for your C code and one for your Fortran code. Whichever is the main program should be an executable project and the other a static library project. Use the Project > Dependences menu to make the library a dependent of the executable. See also configuring Visual C++ for mixed-language applications.

Take a look at the mixed-language samples provided.
0 Kudos
chstoyer
Beginner
2,358 Views
I actually was (am) using Visual Studio 2000. We were actively developing C++ Foundation Class projects 10 or so years ago and out of several hundred CDs, I have a handfull which I found useful, mostly Office and Visual Studio 2000. But all this dies with Windows 7.

The "configuring Visual C++ for mixed-language applications" actually addresses Fortran libraries with C++ main programs, which is different from what I am trying to do. I have a C code of several lines containing two functions. I tried to create a project, but I cannot seem to find anything that looks like creating a C language static library, which is what I think I want. I see that for Fortran, but not for C. What kind of project do I need?

Too bad Microsoft abandonded the mixed language programming. Maybe they got too many languages and couldn't manage it anymore.

Thanks in advance,

Charles
0 Kudos
anthonyrichards
New Contributor III
2,358 Views
If you have already compiled the C-code in the past and it links OK into a Windows program and runs OK, I think you should be able to use the .OBJ file that the earlier C-compiler produced. Just add it to your Fortran program and see if it links OK.

If there is name mismatch and the linker cannot find the C-functions/routines, do a DUMPBIN on the C .OBJ file to list the symbol table and see what the C routines are called and then match up your Fortran calls to reference the same names using the ALIAS atribute and to match the calling convention used by the C code. In order to do this, you will need to generate an interface block for your C functions/subroutines for use in your Fortran to let the Fortran compiler know what it is to expect (ALIAS and calling convention) when you reference a C function/routine that is to be linked from your C .OBJ file.
0 Kudos
Steven_L_Intel1
Employee
2,358 Views
Charles,

Microsoft never released a "Visual Studio 2000". It went from '98 to 2002.

To create a C static library project, use the Win32 project type and then the "Win32 Project" template. There you can select a static library. You will want to ensure that the run-time library settings are the same in the C and Fortran projects. In C/C++, this is under Code Generation.
0 Kudos
chstoyer
Beginner
2,358 Views
Charles,

Microsoft never released a "Visual Studio 2000". It went from '98 to 2002.

Sorry, the real name seems to be Visual Studio 6.0 Enterprise Edition.

To create a C static library project, use the Win32 project type and then the "Win32 Project" template. There you can select a static library. You will want to ensure that the run-time library settings are the same in the C and Fortran projects. In C/C++, this is under Code Generation.

Sorry, the real name seems to be Visual Studio 6.0 Enterprise Edition.

OK, I decided to try to go the way of including the obj file rather than creating a library for a single module, but I am still having problems with this and alsowith a library I am trying to use. Fortran looks for "_FLOATTOIBM" whereas OBJ compiled from the VS 2010 C++ compiler is called "FLOATTOIBM@16". I can add the underscore and then it is called "_FLOATTOIBM@16" and it is still not recognized. If I use the OBJ compiled with Visual C++ 6.0, it doesn't work either, it also has the @ 16.

For the library, the symbol searched for in the Fortran is "_KECHK32" but the lib file (I am trying to import from a DLL eventually) calls it "_KECHK32@4".

I am getting this information by looking at the obj or lib files with a HEX editor.

Going back to the CVF code, the "@16" is present in both calling and called OBJ files. I remember if you called a routine in CVF with the right name but wrong number of arguments, it would complain about not finding "SUB@32" or whatever. So the IVF does not differentiate on number of arguments?

The really important thing is the library DLL import. The C code can probably be implemented in Fortran if this turns out to be a nightmare, but there must be some way to call C from Fortran. I did look at the C compiler options and selected STD_CALL, which was not originally selected, but this did not help.

So it really seems to be the lack of the @ sign and the byte count of the argument list that is the problem.

I tried the !$DEC ALIAS FLOATTOIBM FLOATTOIBM@16 statement and that did not help either.

Any suggestions appreciated.

Thanks,

Charles

0 Kudos
TimP
Honored Contributor III
2,358 Views
Microsoft changed the preferred (default) ABI from STDCALL to CDECL right at the time CVF support was winding down (over a decade ago). As you indicated, the @ scheme went away with that change, and the systems don't mix. Unless you have some requirement to struggle with an ancient library for which you don't have source code, you should go with current techniques, including USE iso_c_binding for data type and function name consistency between ifort and MSVC or ICL. As Steve mentioned, there are examples of C interface in the documents.
0 Kudos
mecej4
Honored Contributor III
2,358 Views
Here is an example using, instead of the name mangling conventions of Intel Fortran and Microsoft C, the C-interoperability features of Fortran 200X:

The C-function that converts a 32-bit IBM 360 float, passed as a 32-bit word, returning a double:

[cpp]#include 
#include

struct ibm32{
unsigned frac : 24;
unsigned biasexp : 7;
unsigned s : 1;
};

double ibm2dble(struct ibm32 w){
double absval=ldexp((double)w.frac,4*(w.biasexp-64)-24);
printf(" IBM FLOAT FRAC = %08Xn IBM FLOAT EXPO = %08Xn IBM FLOAT SIGN = %1Xn",
w.frac,w.biasexp,w.s);
return w.s ? -absval : + absval;
}

/*
#include

union ibm32u{
struct ibm32 w;
long l;
};

main(){
union ibm32u uw;
uw.l=0xC276A000;
printf("%22.15e",ibm2float(uw.w));
}

*/
[/cpp]
The Fortran main program, which calls the C function using F2003 C-interoperability, and the example from http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture:

[fortran]program ibm2ieee
use iso_c_binding

interface
real(c_double) function ibm2dble(l) bind(C)
use iso_c_binding
integer(c_int32_t), value :: l
end function ibm2dble
end interface

integer(c_int32_t) :: l=Z'C276A000';

write(*,*)' Decimal value of IBM FLOAT word = ',ibm2dble(l)
end program ibm2ieee
[/fortran]
Compile the C function to an .OBJ file, and compile the Fortran code to an .EXE:

[bash]s:LANG:> cl /MD /c ibm32.c
s:LANG:> ifort /MD ibmdbl.f90 ibm32.obj
[/bash]
Run the program and compare the result to that in the Wikipedia article:

[bash]s:LANG>ibmdbl
IBM FLOAT FRAC = 0076A000
IBM FLOAT EXPO = 00000042
IBM FLOAT SIGN = 1
Decimal value of IBM FLOAT word = -118.625000000000
[/bash]
0 Kudos
chstoyer
Beginner
2,358 Views

Steve and Mecej4,

I was afraid there would not be good news.

I am sure I can find a way to recompile my C code to get it to work with IVF. Or find another way to convert between (I need to go both ways) IEEE and IBM float (single precision only).

But I do have an antique library (or did you say "ancient") for which I may not be able to get a modern replacement. This is the copy protection dongle interface and means I either stay in CVF for existing products or ask people all over the world to exchange keys for new ones at substantial expense to either me or them.

As you know, staying in CVF means nursing Win XP machines for the duration...

So, can I use the Interface

interface

double precision function ibm2dble(l) bind(C)

use iso_c_binding

integer,value :: l

end function ibm2dble

end interface


in some way to link with the existing KECHK32@4? If so, what binding would I use? STDCALL?

Are there examples of this?

Thanks,

Charles
0 Kudos
mecej4
Honored Contributor III
2,358 Views
From your comments I feel that with more awareness of the issues concerning mixed language projects you could make a better decision. Merely trying different combinations and compiler options without understanding the implications may, once in a while, solve or appear to solve the problem, but the number of combinations are too many to try and the results will always be open to suspicion.

You should read about and understand this point about C-interoperability from the standpoint of the Fortran standard:

it is a specification to follow with a definite pair of C and Fortran "processors", i.e., compilers, with guaranteed success, at least as to interoperability.

Intel Fortran and MSVC (or Intel C) are one such pair; GFortran and GCC are another such pair. On the other hand, it is almost possible to guarantee that an arbitrary pair, e.g., IFort and GCC or Absoft Fortran and ICC, will not work.

Typically, the C compiler of the defined pair knows nothing about and does not care about being called from or calling into Fortran -- there is no Fortran Interoperability section in the C standards. Therefore, the Fortran compiler in the defined pair has to produce .OBJ files that are suitable for linking with the C .OBJ files. Given the diversity of calling and name decoration conventions, it is understandable that there is only one specified compatible C compiler -- it is called the "Companion Processor" in the Fortran standards.

Similar comments can be made about compiler options such as /Gm and /iface:xxx. Typically, explicitly declared attributes such as _cdecl or _stdcall override compiler options. Neither the Fortran nor the C compiler does anything to confirm whether the other compiler was used with those options that are essential for interoperability; doing so falls on the the shouders of the programmer.

We do not know what the contents of this ancient library of yours are, beyond the IEEE <--> IBM-360 floating point conversions. Chances are very good that you can assemble a new library from pieces available freely. Just as 16-bit code no longer runs on W7, your old library will become unusable one of these days.

By the way, CVF 6.6 works fine in command-line mode on W7-X64. It is the GUI and installers that fail.
0 Kudos
anthonyrichards
New Contributor III
2,358 Views
You MUST supply a correct interface block to any external subroutine or function if you want to stand any chance of the compiler linking to it correctly.

Your attempt at using the 'ALIAS' attribute looks wrong.
It should be along the lines of the following (the @16 implies FOUR arguments are required). I'll assume that
it is a subroutine you are calling (just swap REAL(4) FUNCTION - or whatever type - for SUBROUTINE and END FUNCTION for END SUBROUTINE if it is a function call)

INTERFACE
Subroutine FLOATTOIBM(arg1,arg2,arg3,arg4)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_FLOATTOIBM@16' :: FLOATTOIBM
!DEC$ ATTRIBUTES REFERENCE :: arg1
!DEC$ ATTRIBUTES REFERENCE :: arg2
!DEC$ ATTRIBUTES REFERENCE :: arg3
!DEC$ ATTRIBUTES REFERENCE :: arg4
!Correctly define the argument types here...for example
REAL(4) arg1,arg2
INTEGER(4) arg3,arg4
END SUBROUTINE FLOATTOIBM
END INTERFACE

The 'ALIAS' specifies that the aliased name for the (automatic) uppercase Fortran name 'FLOATTOIBM' is '_FLOATTOIBM@16', and this is the name to be searched for when linking. It is important that the ALIAS anem agrees with the case of the symbol name in the C- object file, in ALL details.
0 Kudos
Steven_L_Intel1
Employee
2,358 Views
I want to clarify a few things in this thread.

1. If you can call something from CVF you can call it from Intel Visual Fortran. Period. At most you may need to construct a proper INTERFACE block.
2. The major change in calling between CVF and IVF is the default calling convention. CVF used STDCALL with string lengths immediately following the string address in the argument list, IVF uses C and puts the string lengths at the end. Both compilers have directives and options to do it the "other" way.
3. You should (almost) never add the @n suffix in an ALIAS. This is why we invented the DECORATE attribute. So looking at Anthony's example, one might write:

!DEC$ ATTRIBUTES STDCALL, REFERENCE, DECORATE, ALIAS:"FLOATTOIBM" :: FLOATTOIBM

The compiler will add the correct decoration, including leading underscore and trailing @n. Note that these naming conventions are different when building for x64 - the compiler will do the right thing.
4. Microsoft did not change calling conventions - the Win32 API as well as the interpretive languages use STDCALL almost exclusively, but the MS C compiler defaults to the C mechanism. If you want to call a STDCALL routine from C you need to add __stdcall to the routine header.
0 Kudos
chstoyer
Beginner
2,358 Views
Thanks to both of you guys for your help. A lot of food for thought hereI will play with the IEEE float to IBM float as it would be instructive. but I am sure that cat could be skinned another way. My real problem is talking to USB copy protection keys and if I can't get a solution from the key manufacturer, I am stuck.

I think if I can figure out how to get IBMtoFloat to work, I can get the key interface software to work.

I have never used any of this interface stuff, so this is new to me.

Again, I have source for the IBMtoFloat, so I am not stuck there, it is with the ancient library for which I have no source that is the problem.

I will come back and let you know if I get it to work.

BTW, here is the code for the IBM/IEEE code FYI

Charles

/* Assumes sizeof(int) == 4 */

extern void __stdcall _FLOATTOIBM(int from[], int to[], int n, int endian)

/**********************************************************************

float_to_ibm - convert between 32 bit IBM and IEEE floating numbers

***********************************************************************

Input:

from input vector

n number of floats in vectors

endian =0 for little endian machine, =1 for big endian machines

Output:

to output vector, can be same as input vector

***********************************************************************

Notes:

Up to 3 bits lost on IEEE -> IBM

IBM -> IEEE may overflow or underflow, taken care of by

substituting large number or zero

Only integer shifting and masking are used.

***********************************************************************

Credits: CWP: Brian Sumner

***********************************************************************/

{

register int fconv, fmant, i, t;

for (i=0;i

fconv = from;

if (fconv) {

fmant = (0x007fffff & fconv) | 0x00800000;

t = (int) ((0x7f800000 & fconv) >> 23) - 126;

while (t & 0x3) { ++t; fmant >>= 1; }

fconv = (0x80000000 & fconv) | (((t>>2) + 64) << 24) | fmant;

if(endian==0)

fconv = (fconv<<24) | ((fconv>>24)&0xff) |

((fconv&0xff00)<<8) | ((fconv&0xff0000)>>8);

}

to = fconv;

}

return;

}

extern void __stdcall _IBMTOFLOAT(int from[], int to[], int n, int endian)

/***********************************************************************

ibm_to_float - convert between 32 bit IBM and IEEE floating numbers

************************************************************************

Input::

from input vector

to output vector, can be same as input vector

endian byte order =0 little endian (DEC, PC's)

=1 other systems

*************************************************************************

Notes:

Up to 3 bits lost on IEEE -> IBM

Assumes sizeof(int) == 4

IBM -> IEEE may overflow or underflow, taken care of by

substituting large number or zero

Only integer shifting and masking are used.

*************************************************************************

Credits: CWP: Brian Sumner, c.1985

*************************************************************************/

{

register int fconv, fmant, i, t;

for (i=0;i

fconv = from;

if (fconv) {

/* if little endian, i.e. endian=0 do this */

if (endian==0) fconv = (fconv<<24) | ((fconv>>24)&0xff) |

((fconv&0xff00)<<8) | ((fconv&0xff0000)>>8);

fmant = 0x00ffffff & fconv;

if(fmant){

t = (int) ((0x7f000000 & fconv) >> 22) - 130;

while (!(fmant & 0x00800000)) { --t; fmant <<= 1; }

if (t > 254) fconv = (0x80000000 & fconv) | 0x7f7fffff;

else if (t <= 0) fconv = 0;

else fconv = (0x80000000 & fconv) |(t << 23)|(0x007fffff & fmant);

}

else fconv=0;

}

to = fconv;

}

return;

}

0 Kudos
chstoyer
Beginner
2,358 Views
I would like to thank all those who helped. I got both my C obj and the DLL import STDCALL to work.

For the C obj file (posted earlier) I used


INTERFACE
Subroutine FLOATTOIBM(arg1,arg2,arg3,arg4)
!DEC$ ATTRIBUTES STDCALL, DECORATE, ALIAS:"FLOATTOIBM" :: FLOATTOIBM
!DEC$ ATTRIBUTES REFERENCE :: arg1
!DEC$ ATTRIBUTES REFERENCE :: arg2
!DEC$ ATTRIBUTES VALUE :: arg3
!DEC$ ATTRIBUTES VALUE :: arg4
!Correctly define the argument types here...
REAL(4) arg1(:),arg2(:)
INTEGER(4) arg3,arg4
END SUBROUTINE FLOATTOIBM
END INTERFACE

Calling with

CALL FloatToIbm(SR%TRACES(1:SR%NSAMP,I),TRACE,%VAL(SR%NSAMP),%VAL(0))

and for the imported DLL, I used


INTERFACE
Subroutine KECHK32(arg1)
!DEC$ ATTRIBUTES DLLIMPORT :: KECHK32
!DEC$ ATTRIBUTES STDCALL, DECORATE, ALIAS:"KECHK32" :: KECHK32
!DEC$ ATTRIBUTES REFERENCE :: arg1
!Correctly define the argument types here...
TYPE SCB ! 80 bytes total., ALIAS:'_KECHK32'
SEQUENCE
INTEGER*2 err_code ! 000 Error Code1.
INTEGER*2 err_status1 ! 002 Error Status 1.
INTEGER*2 err_status2 ! 004 Error Status 2.
CHARACTER*1 func_code ! 006 Function Code.
CHARACTER*5 scb_id ! 007 SCB Identification, s/b "..?Z".
CHARACTER*9 prod_name ! 012 Product Name (ASCIIZ).
CHARACTER*3 reserved1 ! 021 Reserved.
INTEGER*4 prod_serial ! 024 Product Serial Number.
INTEGER*4 pin ! 028 Product ID Number (PIN).
INTEGER*2 ecl ! 032 Exec-Count Limit (ECL).
INTEGER*2 edl ! 034 Expire-Days Limit (EDL).
CHARACTER*9 expire_date ! 036 Expire-Date (ED) (ASCIIZ).
CHARACTER*16 user_data ! 045 User-Data.
CHARACTER*6 rom_serial ! 061 Device ROM Serial#.
CHARACTER*8 lasered_id ! 067 Device Lasered-ID (LID).
CHARACTER*5 reserved2 ! 075 Reserved.
END TYPE SCB
TYPE (SCB) arg1
END SUBROUTINE KECHK32
END INTERFACE

Calling with

CALL KECHK32(AZBLOCK)

Thanks again,

Charles

0 Kudos
Reply