I've recently inherited Fortran code that used to be built with an older version of the Intel Visual Fortran compiler. There's a section of code that used to compile, but now throws an error #6633 'The type of the actual argument differs from the type of the dummy argument.'
The problem is when a function called READ_AND_CONVERT is called with REAL*4 DATA_ARRAY(*), but in READ_AND_CONVERT that parameter is declared as INT*2. I think it really just wants the address of the DATA_ARRAY.
Is there a way to pass the address of the DATA_ARRAY, even though they're of different types?
Here is READ_AND_CONVERT:
SUBROUTINE READ_AND_CONVERT (MX, N)
C=======================================================================
C Reads Integer*2 Data Array and Converts it to Real*4.
C
C This is a service routine called by subroutines
C READ_XYZ_2, READ_XYZ_4, READ_XYZ_ALL and READ_XYZ_FULL
C=======================================================================
C
IMPLICIT NONE
C
INCLUDE 'XYZ.FOR'
INCLUDE 'COMMON_XYZIO.FOR'
INCLUDE 'COMMON_HDR.FOR'
C
C-----------------------------------------------------------------------
C Local Parameters
C-----------------------------------------------------------------------
C
LOGICAL BB_FOUND
INTEGER*2 MX, MY
INTEGER*4 N, J
REAL*4 YJ, BB
C
DIMENSION MX(*), MY(2)
EQUIVALENCE (YJ, MY(1))
C
C-----------------------------------------------------------------------
C
CALL GET_REAL_PARAMETER ('XYZ$_OFFSET', BB, BB_FOUND)
C
READ (LUGIN) (MX(J), J = 1,N)
C
IF (BB_FOUND) THEN
DO J = N, 1, -1
YJ = (SCALE_FACTOR * MX(J)) + BB
MX(2*J) = MY(2)
MX(2*J-1) = MY(1)
END DO
ELSE
DO J = N, 1, -1
YJ = SCALE_FACTOR * MX(J)
MX(2*J) = MY(2)
MX(2*J-1) = MY(1)
END DO
END IF
C
RETURN
END
Found a solution here:
Basically disable the warning... by setting Properties | Fortran | Diagnostics | Check Routine Interfaces [change from Yes to No]
The article also shows how to do casting, in their example of a complex array to a real array:
use ISO_C_BINDING
complex(8), allocatable :: c(:)
real(8), pointer:: p(:)
allocate(c(N))
call C_F_POINTER(C_LOC(c), p, [2*N])
call donothing(N, p)
There are directives in Intel Fortran which disable the argument type check for a given routine and for a given argument. To disable the checks for all your code is dangerous!
!DEC$ ATTRIBUTES NO_ARG_CHECK :: ARGUMENT_NAME
source: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/288896
Related
I try to introduce complex-valued array and variables in Fortran. For the following code
program
integer :: i , j !two dimensional real array
real(dp) :: m1(3,2)
complex(dp) :: a1(3,2),a2(3,2), c0, c1
!assigning some values to the array numbers
c0 = (1.0_dp, 0.0_dp)
c1 = (0.000000001_dp, 0.0_dp)
do i=1,3
do j = 1, 2
a1(i,j) = c0*i*j
end do
end do
do i=1,3
do j = 1, 2
a2(i,j) = c0*i*j + c1
end do
end do
do i=1,3
do j = 1, 2
if (dabs( dreal(a1(i,j)) - dreal(a2(i,j))) > 1.e-6 then
write (*,*), 'warning',i,j, a1(i,j), a2(i,j)
end if
end do
end do
write (*,*), a1(1,1), a2(1,1)
end program
ifort gives me
complex(dp) :: a1(3,2), a2(3,2)
-----------^
why complex(dp) requires compile-time constant and how to fix it? Thank you.
The kind parameter dp must be a constant, see Fortran - setting kind/precision of a variable at run time
However, you do not have the same problem as in the link, you did not try to define dp at all! First, you must use IMPLICIT NONE, it is absolutely necessary for safe programming and the biggest problem with your code. Then it will tell you that the type of dp is not declared.
You just define the kind constant in one of the usual ways, as a constant. The most simple is:
program main
!NECESSARY!
implicit none
integer, parameter :: dp = kind(1.d0)
Note that I named the program main, you have to name your program. Or just omit the program.
More about defining real kinds can be found at Fortran 90 kind parameter
Another note: Forget dabs() and dreal(). dabs() is an old remnant of Fortran 66 and dreal() is not standard Fortran at all. Just use abs and either dble() or better real( ,dp).
I have some functions written in Fortran that take a structure as an argument, but the caller has the data stored in an INTEGER*4(2) array. In order to avoid the copy between the two data structures, I'm wondering if the following implementation of a C++-like reinterpret_cast is valid according to the specification:
STRUCTURE /TimeStamp/
INTEGER*4 secondsSinceEpoch
INTEGER*4 nanos
END STRUCTURE
STRUCTURE /reinterpret_cast/
UNION
MAP
INTEGER*4, POINTER :: array(:)
END MAP
MAP
TYPE (TimeStamp), POINTER :: tstamp
END MAP
END UNION
END STRUCTURE
SUBROUTINE set_time(timeArg)
INTEGER*4, TARGET :: timeArg(2)
RECORD /reinterpret_cast/ time
time % array => timeArg
time % tstamp % secondsSinceEpoch = 12
time % tstamp % nanos = 0
END
Is this implementation of the set_time method guaranteed to work (e.g., set the values of timeArg(1) and timeArg(2))?
No, your function is not guaranteed to work by the Fortran standard and many compilers will refuse the syntax altogether. I am not sure whether Fortran pointers are allowed in the DEC structures and if yes, whether you can union them. They (structure and union and record) were designed before Fortran pointers were put into the standard and are strongly discouraged for new code, but it is quite possible Intel allowed Fortran pointers in allowed them.
Much easier (at least for me) way is to use Fortran standard type(c_ptr) which is basically the C void * pointer.
SUBROUTINE set_time(timeArg)
USE, INTRINSIC :: ISO_C_BINDING
INTEGER(c_int_32), TARGET :: timeArg(2)
type(TimeStamp), POINTER :: tstamp
CALL c_f_pointer(c_loc(timeArg), tstamp)
tstamp % secondsSinceEpoch = 12
tstamp % nanos = 0
END
I also changed the INTEGER*4 because it is also not standard conforming and not guaranteed to be C-interoperable.
Do note that the address of the target dummy argument is valid only in the subroutine unless the actual argument is pointer or target.
What you are looking for is the F90-standard function TRANSFER. It interprets the bit representation of the operand as if it was of the same type of another variable (the "mold"). Thus, this:
USE ISO_FORTRAN_ENV ! For the REALnn and INTnn constants
REAL(REAL32) r
INTEGER(INT32) i
r = 1.0
i = TRANSFER(r, i) ! The second "i" here is unevaluated, just gives the type
Is equivalent to this:
float r = 1.0;
int32_t i;
i = *reinterpret_cast<int*>(&f);
Note that the REALnn and INTnn constants are from Fortran 2008, so your compiler might not have them. I just used them as examples to make sure that the types were compatible, since just like in C, the standard does not say precisely how big a "default real" or "default integer" are.
As an example, I frequently use this function when creating Fortran-based MEX functions in Matlab, since the Matlab interface with Fortran is based on F77 and does not allow you to use pointers to Matlab memory directly, unlike the C interface. I use the TRANSFER function and the ISO_C_BINDING module (F2003) to cast the "integer" (actually a C pointer) Matlab gives me to the Fortran type C_PTR, to a Fortran pointer. Like this:
USE ISO_C_BINDING ! For C_PTR and related functions
INTEGER(INT32), POINTER :: arrayPtr(:)
mwSize n ! This is a type defined in the Matlab-Fortran interface
mwPointer myMatlabArray = ... ! So is this
TYPE(C_PTR) cPtrToData
! Cast the returned C pointer to the data (Matlab interface returns an integer type)
cPtrToData = TRANSFER(mxGetData(myMatlabArray), cPtrToData)
! Since Fortran arrays/pointers have size information, get the length
n = mxGetNumberOfElements(myMatlabArray)
CALL C_F_PTR(cPtrToData, arrayPtr, [n]) ! Associate the Fortran ptr
array(3:7) = ... ! Do whatever, no need to copy
Which is the rough equivalent to the C version:
mxArray* myMatlabArray = ...; //
mwSize n = mxGetNumberOfElements(myMatlabArray);
int* arrayPtr = (int*)mxGetData(myMatlabArray);
array[3] = ... // Do whatever, no need to copy
So in both cases these MEX functions could be called with Matlab array of Matlab type int32.
I was doing an svd decomposition of a square matrix A, with A=U S Vdag, and in the fortran code, the line reads
lwork = -1
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
lwork = int(work(1));deallocate(work); allocate(work(lwork))
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
When I compiled with gfortran, it went through with no error or warning. However when I run the program, it shows error with message:
" ** On entry to ZGESVD parameter number 11 had an illegal value "
I could not figure out what went wrong.
For reference, the definitions of the parameters:
type cmatrix
integer(4) d
complex(8), allocatable :: m(:,:)
end type
type (cmatrix) A,U,Vdag
allocate(A%m(dim,dim),U%m(dim,dim),Vdag%m(dim,dim))
A%d = dim; U%m = dim; Vdag%d = dim
real(8) S(dim)
Thanks in advance!
Xiaoyu
p.s. It should be mentioned that such a program runs smoothly when compiled with ifort, but gfortran gives an runtime error as shown above
--- Problem solved!
It seems that the issue lies in how ifortran and gfortran allocates memory. I defined in the code USV type which:
type USV
integer is_alloc
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
When initializing by
type(USV) Test_usv(:)
allocate(Test_usv(3)),
the value of is_alloc is 0 using intel fortran compiler, while arbitrary number for gfortran. I need to use this value as a criterion for allocating U V matrices:
if (is_alloc.eq.0) then
allocate(U%m(dim,dim))
end if
The fundamental problem is not a difference between ifort and gfortran. Your approach to the initialization of the variables isn't valid Fortran. Unless you initialize a variable with a declaration, assignment statement, etc., its value is undefined. One way to fix this would be to add a default initialization to the type definition:
type USV
integer is_alloc = 0
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
Another approach would be to not track the allocation status yourself and to rely upon the intrinsic function that Fortran provides for this purpose:
if (.NOT. allocated (U%m) ) allocate(U%m(dim,dim))
P.S. Best practice is not to rely upon specific numeric values for kinds. The kind values are arbitrary and are not necessarily the number of bytes of the type. Some compilers use the number of bytes, others don't. One method to specify the number of bytes and to have portable code is to use types provided by the ISO Fortran environment:
use, intrinsic :: ISO_FORTRAN_ENV
integer(int32) :: d
real(real64), allocatable :: S(:)
The types are named after the number of bits. A list of the available types is in the "Intrinsic Modules" chapter of the gfortran manual.
Is there a way to check whether aliasing occurs in a Fortran subroutine, or at least to tell the compiler to issue a warning?
Consider this (rather simplistic) example:
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine
end module
program test
use alias
integer :: a, b
a = 1 ; b = 2
call myAdd(a, b, b)
print *, b, 'is not 3'
end program
Here, the result is set to zero in the subroutine. If the same variable is given as input and output, the result is (obviously) wrong. Is there a way to capture this kind of aliasing at run-time or at compile-time?
Yes, gfortran will detect some aliasing with the compiler option -Waliasing, however, the arguments must have intents in and out. It won't work with your example because you have declared argument c as intent(inout). In this example you can simply change the intent to out since the input value of c is not used. They try the compiler option! gfortran outputs:
alias.f90:17.16:
call myAdd(a, b, b)
1
Warning: Same actual argument associated with INTENT(IN) argument 'b' and INTENT(OUT) argument 'c' at (1)
I don't know of options in g95 or gfortran to detect the aliasing error at compile or run time. The programmer is responsible for avoiding such an error. You can pass the intent(in) arguments as expressions to ensure that they are not changed within the subroutine, as shown below.
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine myadd
end module alias
program test
use alias, only: myadd
integer :: a, b
a = 1 ; b = 2
call myAdd((a),(b),b) ! parentheses around arguments a and b
print*, b,"is 3"
call myAdd(a, b, b)
print*, b,"is not 3"
end program test
I have a main program in Fortran. I am using Intel Visual Fortran XE 2011 on Visual Studio 2010. I would like to use a function which is coded in C++. The function I'm using is getting several arrays (input - set from the main fortran program) and use them to form an output array (to be returned to the main fortran program).
I've taken the following steps:
1)I created a Fortran project with the Fortran main program and module and I set it as "startup project".
2)I created a C++ project of type "static library".
3)I added $(IFORT_COMPILERvv)\compiler\lib\ia32 as explained here http://software.intel.com/en-us/articles/configuring-visual-studio-for-mixed-language-applications
The C++ static library is build with no problem.
The errors I get is about the declaration of the real(8) variables in the fortran program.
I get the following two errors for all real(8) declarations, i.e. 6 errors in total:
error #5082: Syntax error, found '(' when expecting one of: :: %FILL , TYPE BYTE CHARACTER CLASS DOUBLE DOUBLECOMPLEX DOUBLEPRECISION ...
error #5082: Syntax error, found '::' when expecting one of: ( * , ; [ / = =>
Here is the code I used:
Main Fortran Program:
Program Fort_call_C
use iso_c_binding
implicit none
interface
subroutine vec_sum_c(a,b,c) bind (C, name = "vec_sum_c")
use iso_c_binding
implicit none
real(8) (c_double), intent (in), dimension (*) :: a,b
real(8) (c_double), intent (out), dimension (*) :: c
end subroutine get_filled_ar
end interface
integer:: i
integer (c_int)::m
real(8)(c_double),dimension(:):: a, b, c
open(unit=10, file="input_arrays.txt",status="unknown")
read(10,*) m
allocate(a(m),b(m),c(m))
do i=1,m
read(10,*)a(i),b(i)
end do
close(10)
call vec_sum_c(m,a,b,c)
do i=1,m
print*, c(i)
end do
pause
end program
And the C++ function is:
extern"C" void vec_sum_c(int *m, double *a, double *b, double *c){
int mm = *m;
for(int i=0;i<=m-1;i++){
c[i]=a[i]+b[i];
}
}
Could anybody please help me with this issue?
And would you please let me know if the idea of sending a whole array from a fortran program to a c++ routine is a safe or problematic (better-to-be-avoided) attempt?
Your Fortran syntax is out. You have the real kind twice. Try
REAL(C_DOUBLE), INTENT(IN), DIMENSION(*) :: a, b
etc.
C_DOUBLE is a named constant. It happens to have the value 8 with that processor.
Also:
you are missing the argument m in the Fortran interface body for the C function.
you change your mind about the name of the subroutine in the Fortran interface body between the opening and closing statement!
Your C++ for loop less than equal compares against m, that should probably be mm.
There are no inherent problem sending whole arrays in this manner.
I had only managed to pass the value of a varible from C
function to fortran function.
I have here pasted the two source files namely main.c and fortran.f
You can use these two files in microsoft visual studio 10. After
doing all the settings in visual studio as suggested in page http://software.intel.com/en-us/articles/configuring-visual-studio-for-mixed-language-applications, you need to make another change as;
go to the project property of C/C++ static library ;
go to C/C++
go to Code Generation
set the Runtime Library to Multi-threaded Debug (/MTd)
now you can build the program....
main.c:
#include <stdio.h>
#include <malloc.h>
void testc(double **pa, double **p)
{
double b;
double *a, *c;
int m;
c = (double*) malloc(sizeof(double));
*c = 10;
*p = c;
a = (double*) malloc(sizeof(double)*5);
a[0]=1.23;
a[1]=2.46;
a[2]=3.69;
a[3]=4.11;
a[4]=7.21;
*pa=a;
for (m=0;m<5;m++)
{
b=a[m];
b=b+1.0;
a[m]=b;
}
}
fortran.f:
program test
use iso_c_binding
implicit none
interface
subroutine testc(pa, m) bind(c)
use iso_c_binding
type(c_ptr):: m
type(c_ptr):: pa
end subroutine testc
end interface
type(c_ptr) :: pa
type(c_ptr) :: m
real(c_double),pointer::fpa(:)
real(c_double),pointer::fm(:)
call testc(pa,m)
call c_f_pointer(pa, fpa, [5])
call c_f_pointer(m, fm, [1])
print *, fm(1)
print*, fpa(1)
pause
end program test