Adding to an array of characters in Fortran - arrays

I'm trying to write a class procedure that adds a new character to an array of characters, but keep stumbling across "different character length in array constructor" errors (compiling with GFortran), even when the characters lengths are, as far as I can see, the same.
Here's my function:
subroutine addToArray(this, newElement)
class(MyClass), intent(inout) :: this
character(len=*), intent(in) :: newElement
character(len=256) :: tempElement
character(len=256), dimension(:), allocatable :: temp
tempElement = newElement ! Needed otherwise newElement is of the wrong size
allocate(temp(size(this%charArray)+1) ! Make the char array bigger by 1
temp = [this%charArray, tempElement]
call move_alloc(from=temp, to=this%charArray)
end subroutine
This results in the error Fortran runtime error: Different CHARACTER lengths (538976288/256) in array constructor. However, if I print len(this%charArray) or len(tempElement), they are both 256 characters long. So where is the 538976288 coming from?
I'm typically calling this procedure using something like myObject%addToArray('hello'). this%charArray is declared in the type definition as character(len=256), dimension(:), allocatable :: charArray, and allocated using allocate(this%charArray(0)).

It appears it is an error which I reported to GCC more than a year ago https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70231
The workaround is to compile with optimizations at least -O1.
If it is not the same error, an exact reproduction case is needed including the compilation flags and all relevant details.

Related

Fortran cast 2D Array to 1D array and pass it

I am working with Fortran 2008 with the Intel 17 Compiler.
I am getting a 3D (x,y,z) assumed shape array in a subroutine and want to pass xy slices as a 1D array to another function that multiplies it with a matrix:
SUBROUTINE xy_gyro_average_3d(this,infield,barfield)
class(xy_GyroAverager_t) :: this
REAL,DIMENSION(:,:,:), INTENT(IN),contiguous,TARGET :: infield
REAL,DIMENSION(:,:,:,:,:), INTENT(OUT),contiguous,TARGET :: barfield
INTEGER :: n,m,k,li0,lj0
type(Error_t) :: err
REAL,DIMENSION(:),POINTER :: inptr,outptr
li0=SIZE(infield,1)
lj0=SIZE(infield,2)
DO n=1,this%ln0
DO m=1,this%lm0
DO k=1,this%lk0
inptr(1:li0*lj0) => infield(:,:,k)
outptr(1:li0*lj0) => barfield(:,:,k,m,n)
CALL this%gyromatrix(k,m,n)%multiply_with(invec=inptr,&
& outvec=outptr,err=err)
if (err%err) call write_err(err)
END DO ! k
END DO ! m
END DO ! n
END SUBROUTINE xy_gyro_average_3d
The multiply_with routine looks (simplified) like this:
SUBROUTINE multiply_with(this,invec,outvec,err)
CLASS(Matrix_t) :: this
real, dimension(1:), intent(IN) :: invec
real, dimension(1:),intent(OUT) :: outvec
CLASS(Error_t),INTENT(OUT) :: err
CALL this%multiply_with__do(invec,outvec,err)
END SUBROUTINE multiply_with
I am getting a warning about the pointer allocation and then an error about the types of actual and dummy argument not matching:
src/GyroAverager.F90(294): warning #8589: Fortran 2008 specifies that if a bound remapping list is specified, data target must be of rank one. [INFIELD]
inptr(1:SIZE(infield(:,:,k))) => infield(:,:,k)
----------------------------------------------^
src/GyroAverager.F90(295): warning #8589: Fortran 2008 specifies that if a bound remapping list is specified, data target must be of rank one. [BARFIELD]
outptr(1:li0*lj0) => barfield(:,:,k,m,n)
----------------------------------^
src/GyroAverager.F90(297): error #6633: The type of the actual argument differs from the type of the dummy argument. [INPTR]
CALL this%gyromatrix(k,m,n)%multiply_with(invec=inptr,&
-------------------------------------------------------------^
src/GyroAverager.F90(298): error #6633: The type of the actual argument differs from the type of the dummy argument. [OUTPTR]
& outvec=outptr,err=err)
Now I think the warning should not be a problem as long as the arrays infield and barfield are marked as contiguous (is there any way to do this to get rid of the warning messages though?).
What exactly is the problem with the error message? The pointers inptr and outptr have the same dimension as the dummy arguments invec and outvec.
But I read that if you pass a pointer but do not receive it as a pointer the array it is pointing to is passed instead. So in the end is it still passed as a 2D array instead of a 1D or is the problem somewhere else?
It was working before when the header of the multiply_with routine looked like this:
SUBROUTINE multiply_with(this,invec,outvec,err)
CLASS(Matrix_t) :: this
class(*), dimension(1:),intent(IN) :: invec
class(*), dimension(1:),intent(OUT) :: outvec
CLASS(Error_t),INTENT(OUT) :: err
and then the routines called by multiply_with would get the type via a select type later. But I reduced the code as we are always working with real arrays.
What is the correct way to do this?

Use variable name for argument-derived bounds of local arrays

With fortran, I am running in situations where I have multiple local variables whose size is derived from input parameters in a somewhat verbose manner, e.g.
program pbounds
contains
subroutine sbounds(x)
integer,intent(in) :: x(:,:)
integer y1(size(x,1)/3,size(x,2)/2)
integer y2(size(x,1)/3,size(x,2)/2)
integer y3(size(x,1)/3,size(x,2)/2)
! ... do some stuff
end subroutine sbounds
end program pbounds
This seems overly verbose as I keep repeating the size expression. Additionally, when a change is needed – e.g. when it turns out that I need a y4 and change size(x,1)/3 to size(x,1)/4 – for real-world code that doesn't look quite as neat, it is easy to miss one of the previous variables.
In my real code, other examples include declarations with sizes coming from verbose, repetitive composite type fields, such as
type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(obj%subfield%nmax, obj%subfield%xmax)
...
Is it possible to define a name for the size expressions, without resorting to preprocessor macros or allocatable arrays?
The things I have tried
With allocatable variables, we can use a local variable
as name for the size expressions, but we split the declaration
of the local arrays over two lines each.
program pboundsx
contains
subroutine sboundsx(x)
integer,intent(in) :: x(:,:)
integer,allocatable :: y1(:,:),y2(:,:),y3(:,:)
integer s(2)
s = [ size(x,1)/3, size(x,2)/2 ]
allocate(y1(s(1),s(2)))
allocate(y2(s(1),s(2)))
allocate(y3(s(1),s(2)))
! ... do some stuff
end subroutine sboundsx
end program pboundsx
With preprocessor macros we can give the size expression a name,
but at the cost of adding multiple preprocessor lines, that
disturb the indentation pattern among other things.
program pboundsm
contains
subroutine sboundsm(x)
integer,intent(in) :: x(:,:)
#define s1 (size(x,1)/3)
#define s2 (size(x,2)/2)
integer y1(s1,s2)
integer y2(s1,s2)
integer y3(s1,s2)
#undef s1
#undef s2
! ... do some stuff
end subroutine sboundsm
end program pboundsm
With a second subroutine we can make the sizes an explicit
parameter, but this is probably the most verbose and obscure
solution; even more so considering that in real-world code 'x'
isn't the only parameter.
program pboundss
contains
subroutine sboundss(x)
integer, intent(in) :: x(:,:)
call sboundss2(x,size(x,1)/3,size(x,2)/2)
end subroutine sboundss
subroutine sboundss2(x,s1,s2)
integer, intent(in) :: x(:,:), s1, s2
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
end subroutine sboundss2
! ... do stuff
end program pboundss
If it was allowed to mix declarations and initialization, the solution would be simple – but it is not:
program pboundsv
contains
subroutine sboundsv(x)
integer,intent(in) :: x(:,:)
integer s1 = size(x,1)/3, s2 = size(x,2)/3 ! INVALID DECLARATION
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
! ... do stuff
end subroutine sboundsv
end program pboundsv
If the compiler allows (*), it may be an option to include the subroutine body entirely into a block (= a new scope) and mix declarations and assignment:
program pboundsv
contains
subroutine sboundsv(x)
integer,intent(in) :: x(:,:)
integer s1, s2
s1 = size(x,1)/3 ; s2 = size(x,2)/3
block
integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
! ... do stuff
endblock
endsubroutine
end program
(*) But, this is Fortran >95, and Oracle studio fortran 12.5 still cannot compile it (very sadly)... (gfortran and ifort seem OK).
A partial solution - while specification statements cannot depend on the value of a local variable(**), they can depend on previous specifications for other local variables. For example:
subroutine sbounds(x)
integer,intent(in) :: x(:,:)
integer y1(size(x,1)/3,size(x,2)/2)
integer y2(size(y1,1),size(y1,2))
integer y3(size(y1,1),size(y1,2))
! ... do some stuff
end subroutine sbounds
...
type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(size(arr1,1), size(arr1,3))
In some cases this can make the logical structure of your declarations clearer - "the extent of this dimension of this variable is the same as the extent of this dimension of that variable", which might be a more relevant message to a reader of the code than the specific expression that calculates the extent.
** Note that it is various restrictions on specification and constant expressions that is the real issue with your last block of code. You can quite happily mix declarations with initializations and other declarations in Fortran (they are just specification statements), what you cannot do is mix specification statements with executable statements (block constructs and the like aside). Specification expressions cannot depend on the value of a local variable, in part because it otherwise becomes difficult to ensure a deterministic ordering, while constant expressions cannot depend on the value of any variable, because constant expressions are supposed to be constant (and able to be evaluated at compile time).

error #6366: The shapes of the array expressions do not conform

I create a program to solve two-body problem by runge-kutt method. I was faced with the problem: when I call the function which return the ELEMENT of two-dimension array from expression, which must give the ELEMENT of two-dimension array too (sorry for confusion with terms), I get this message:
error #6366: The shapes of the array expressions do not conform.
[X1]
X1(DIM,i)=X1(DIM,i-1)+0.5D0*ABS(i/2)*H*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu)
I have a external interface for this function and it's apparently that compiler consider this as a function.
I must clarify some things:
Yes, it's not quite Fortran, it's preprocessor Trefor, which is used by astronomers in Moscow University (I'm just a student). This language is very similar to fortran, but a bit closer to C (Semi-colons for example), which is studied by many students.
Runge-Kutta method can be briefly written as:
We have initial value problem
dy/dt=f(t,y), y(t0)=y0
y is unknown vector, which contains 12 components in my case (3 coordinates and 3 velocities for each body)
next step is
y(n+1)=y(n)+1/6*h*(k1+2k2+2k3+k4), t(n+1)=t(n)+h
where
k1=f(tn,yn),
k2=f(tn+1/2h,yn+h/2*k1)
k3=f(tn+1/2h,yn+h/2*k2)
k4=f(tn+1/2h,yn+k3)
I.e. in my code X1,2 and V1,2 and K_1,2 should be vectors, because each of them must be have 3 spatial components and 4 components for each 'order' of method.
The full code:
FUNCTION K1(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_1);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_1;
real(8) :: B1;
real(8) :: R;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B1(NDIM, ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_1(NDIM,ORD);
IF (nu>=2) THEN;
B1(DIM,i)=V1(DIM,i);
ELSE;
R=((X1(1,i)-X2(1,i))**2.D0+(X1(2,i)-X2(2,i))**2.D0+(X1(3,i)-X2(3,i))**2.D0)**0.5D0;
B1(DIM,i)=Fgauss*M2*(X2(DIM,i)-X1(DIM,i))/((R)**3.D0);
END IF;
K_1(DIM,i)=B1(DIM,i);
RETURN;
END FUNCTION K1;
FUNCTION K2(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_2);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_2;
real(8) :: B2;
real(8) :: R;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B2(NDIM,ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_2(NDIM,ORD);
IF (nu>=2) THEN;
B2(DIM, i)=V2(DIM,i);
ELSE;
R=((X1(1,i)-X2(1,i))**2.D0+(X1(2,i)-X2(2,i))**2.D0+(X1(3,i)-X2(3,i))**2.D0)**0.5D0;
B2(DIM, i)=Fgauss*M1*(X2(DIM,i)-X1(DIM,i))/((R)**3.D0);
END IF;
K_2(DIM,i)=B2(DIM, i);
RETURN;
END FUNCTION K2;
PROGRAM RUNGEKUTT;
IMPLICIT NONE;
Character*80 STRING;
real(8) :: M1,M2,Fgauss,H;
real(8) :: R,X1,X2,V1,V2;
integer :: N,i,DIM,NDIM,maxnu,ORD;
integer :: nu;
PARAMETER(NDIM=3,ORD=4,maxnu=2);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD);
Dimension :: V1(NDIM,ORD),V2(NDIM,ORD);
INTERFACE;
FUNCTION K1(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_1);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_1;
real(8) :: R;
real(8) :: B1;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B1(NDIM, ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_1(NDIM,ORD);
END FUNCTION K1;
FUNCTION K2(DIM,i,X1,X2,V1,V2,NDIM,ORD,nu,maxnu)RESULT (K_2);
integer,intent(in) :: i,DIM,nu;
real(8) :: K_2;
real(8) :: R;
real(8) :: B2;
real(8),intent(in) :: X1,X2,V1,V2;
COMMON/A/M1,M2,Fgauss,H;
integer,intent(in) :: NDIM,ORD,maxnu;
Dimension :: B2(NDIM,ORD);
Dimension :: X1(NDIM,ORD),X2(NDIM,ORD),V1(NDIM,ORD),V2(NDIM,ORD);
Dimension :: K_2(NDIM,ORD);
END FUNCTION K2;
END INTERFACE;
open(1,file='input.dat');
open(2,file='result.res');
open(3,file='mid.dat');
READ(1,'(A)') STRING;
READ(1,*) Fgauss,H;
READ(1,*) M1,M2;
READ(1,*) X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1);
READ(1,*) X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1);
WRITE(*,'(A)') STRING;
WRITE(3,'(A)') STRING;
WRITE(3,'(A,2G14.6)')' Fgauss,H:',Fgauss,H;
WRITE(3,'(A,2G14.6)')' M1,M2:',M1,M2;
WRITE(3,'(A,6G17.10)')' X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1):',X1(1,1),X1(2,1),X1(3,1),V1(1,1),V1(2,1),V1(3,1);
WRITE(3,'(A,6G17.10)')' X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1):',X2(1,1),X2(2,1),X2(3,1),V2(1,1),V2(2,1),V2(3,1);
R=((X1(1,1)-X2(1,1))**2.D0+(X1(2,1)-X2(2,1))**2.D0+(X1(3,1)-X2(3,1))**2.D0)**0.5D0;
N=0;
_WHILE N<=100 _DO;
i=2;
_WHILE i<=ORD _DO;
DIM=1;
_WHILE DIM<=NDIM _DO;
X1(DIM,i)=X1(DIM,i-1)+0.5D0*ABS(i/2)*H*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu);
X2(DIM,i)=X2(DIM,i-1)+0.5D0*H*ABS(i/2)*K2(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,2,maxnu);
V1(DIM,i)=V1(DIM,i-1)+0.5D0*H*ABS(i/2)*K1(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,1,maxnu);
V2(DIM,i)=V2(DIM,i-1)+0.5D0*H*ABS(i/2)*K2(DIM,i-1,X1(DIM,i-1),X2(DIM,i-1),V1(DIM,i-1),V2(DIM,i-1),NDIM,ORD,1,maxnu);
DIM=DIM+1;
_OD;
i=i+1;
_OD;
_WHILE DIM<=NDIM _DO;
X1(DIM,1)=X1(DIM,1)+1.D0/6.D0*H*(K1(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,2,maxnu)+2.D0*K1(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K1(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K1(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
X2(DIM,1)=X2(DIM,1)+1.D0/6.D0*H*(K2(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
V1(DIM,1)=V1(DIM,1)+1.D0/6.D0*H*(K1(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,1,maxnu)+2.D0*K1(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,2,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,2,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,2,maxnu));
V2(DIM,1)=V2(DIM,1)+1.D0/6.D0*H*(K2(DIM,1,X1(DIM,1),X2(DIM,1),V1(DIM,1),V2(DIM,1),NDIM,ORD,1,maxnu)+2.D0*K2(DIM,2,X1(DIM,2),X2(DIM,2),V1(DIM,2),V2(DIM,2),NDIM,ORD,1,maxnu)+2.D0*K2(DIM,3,X1(DIM,3),X2(DIM,3),V1(DIM,3),V2(DIM,3),NDIM,ORD,1,maxnu)+K2(DIM,4,X1(DIM,4),X2(DIM,4),V1(DIM,4),V2(DIM,4),NDIM,ORD,1,maxnu));
_OD;
R=((X1(1,5)-X2(1,5))**2.D0+(X1(2,5)-X2(2,5))**2.D0+(X1(3,5)-X2(3,5))**2.D0)**0.5D0;
N=N+1;
write(2,'(A,1i5,6g12.5)')' N,X1(1,1),X1(2,1),X1(3,1),X2(1,1),X2(2,1),X2(3,1):',N,X1(1,1),X1(2,1),X1(3,1),X2(1,1),X2(2,1),X2(1,1),X2(2,1),X2(3,1);
_OD;
END PROGRAM RUNGEKUTT;
Please, help, it seems, I don't understand something in using functions!
M.S.B. is on the right track, but I think there's enough here to figure out the problem. As noted, function K1 returns a two-dimension array. But all of the other operands in the expression are scalars (well, I don't know what H is, but it likely doesn't matter.) What ends up happening is that the expression evaluates to an array, the scalars getting expanded as needed to match up. You then end up with assigning an array to a scalar, and that's the cause of the error.
I am not familiar enough with Runge-Kutta to be able to suggest what you want instead. But it is likely that you want the function to return a scalar, not an array.
Are you calculating a scaler? If I understand what you are trying to do, the function returns a 2D array, but you only assign to one element of it. Why not have the function return a scaler value instead of an array?
The array message is about an inconsistency between the shapes of arrays in the expression. You haven't shown all of the declarations so we can't figure that out.
Coding style tips: 0) Is there a typo? Should it be Function K1? 1) Semi-colons aren't necessary on the end of each line. Fortran isn't C. 2) At least to me, your code would be more readable if you put all of the declarations pertaining to each variable on one line, instead of separate lines for type, intent and dimension. For example:
real, dimension (NDIM,ORD), intent (in) :: X1
EDIT after the edit of the question:
The machine written code is ugly.
It is clear that you need to do the calculation for all the dimensions. The question is where. The code shows the loops containing the function call rather than the function containing the loops. With this overall design it would make sense that you calculate a single element of the output array (i.e., a scaler variable) and have that be the function return instead of having the function return an array. For this design, it makes little sense to return a 2D array containing only a single used element. And since your statement in the main program expects a scaler, you are getting the error message from the compiler. So redesign your function to return a scaler.
And it looks that you are calling K1 with the actual argument being single elements when arrays are expected. For example, you have X1(DIM,i-1) as a third argument when the function expects an array of size X1(NDIM,ORD). This also a problem, as an inconsistency in actual (i.e., call) and dummy arguments (i.e., function). If function K1 is to do the work of selecting the appropriate array elements, you need to pass it the entire array. If the call is to select the appropriate array elements, then rewrite K1 to have scalers instead of arrays as input arguments. You need a consistent design.

C-Fortran character string interoperability

Good day. Sorry for maybe not so understandable definition of my problem and maybe some inaccuracies - I'm just starting to try myself in programming. Still, I'll try my best to explain everything plain.
I have mathematical DLL written in Fortran.
For example, there is a function. This function is used to parse name of log file into the dll to watch for the calculations.
integer function initLog(
* int_parameter,
* char_parameter,
* char_parameter_length,
* )
*bind(C, name = "initLog");
use, intrinsic :: ISO_C_BINDING;
!DEC$ATTRIBUTES DLLEXPORT::initLog
integer(C_INT), value :: parameter;
character(C_CHAR), intent(in) :: char_parameter(char_parameter_length);
integer(C_INT), value :: char_parameter_length;
...
some_other_variable = char_parameter(1:1)(1:char_parameter_length);
end function;
Usually I use MATLAB to work with the dll and, thus, have to use .mex files to call my functions directly from MATLAB. Inside the .mex file I have some interface code written in C that provides the interface between MATLAB and dll. For example, C interface for the function mentioned is:
int doSmth(const int int_parameter,
const char* char_parameter,
const int char_parameter_length,);
And then I use loadLibrary and GetProcAddress to get the function. And this works fine.
However, now I need to create .exe test file in Fortran which would use my dll. So, I have to link my dll to exe by linking it to an import .lib library. Another option for this executable is to take the name of the log file via command line as parameter. So, first I tried to pass the logfile filename just from within the exe file, like this:
program test
use dll_name;
use ifport;
implicit none;
...
integer :: log_init_status;
...
log_init_status = init_log(2, 'logfile.log', len('logfile.log'));
...
end program
This works fine in release, but returns a "severe (664): Out of range: substring ending position '11' is greater than string length '1'" mistake in debug. But at first I didn't find this bug and kept on writing the code. This is what I've got now:
program test
use dll_name;
use ifport;
use ISO_C_BINDING;
implicit none;
...
character*255 :: log_flag_char;
integer(C_INT) :: log_flag;
character*255 :: filename;
character(C_CHAR) :: log_filename;
integer(C_INT) :: log_filename_length;
....
call getarg(5, log_flag_char);
read(log_flag_char, *) log_flag;
call getarg(6, log_filename);
log_filename_length = len(log_filename);
log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);
...
end program
This worked fine, but took only 1 first character of the log_filename ("C:\abcd\logfile.log" is transformed into "C"). If I change
character(C_CHAR) :: log_filename;
to
character(C_CHAR) :: log_filename(255);
, I get 2 problems: first, I have the length of my log_filename equal to 255 (can be fixed by trim though), and second - and the main - I again get "severe (664): Out of range: substring ending position '255' is greater than string length '1'".
If I change
log_init_status = analyticsLogInit(log_flag, log_filename,
log_filename_length);
to
log_init_status = analyticsLogInit(log_flag, C_LOC(log_filename),
log_filename_length);
, I get the error about the dummy argument type differ than the actual one.
I myself have a feeling that the 664 error shown comes from this line in dll:
some_other_variable = char_parameter(1:1)(1:char_parameter_length);
. I should write in my exe something like
character*255 :: log_filename;
and not
character :: log_filename(255);
But how can I parse it with (C_CHAR) used?
I realise that all this is quite messy and that it all comes from the leak of understanding, but this is my almost first serious experience in programming.
I only glanced over your question, but one thing to take note of is the way a character variable or named constant is declared. You can provide two type parameters: length and kind. If you don't use the corresponding keyword in the declaration, the first parameter specifies the length, and the second (if present) specifies the kind.
So if you want to declare a character variable of length 255 and kind C_CHAR, you can do so in any of the following ways:
character(len=255, kind=C_CHAR) :: log_filename
character(255, kind=C_CHAR) :: log_filename
character(255, C_CHAR) :: log_filename
character(kind=C_CHAR, len=255) :: log_filename
character(kind=C_CHAR) :: log_filename*255
The following syntax on the other hand (which is the one you used), declares a character variable of length C_CHAR, whatever value that may be.
character(C_CHAR) :: log_filename
Oh, and the next syntax declares an array of 255 elements, each element being a character variable of length C_CHAR.
character(C_CHAR) :: log_filename(255)
So the conclusion is, that one should take some time to study the peculiarities of declaring character entities in fortran.

keeping array limits in fortran during subroutine call

I have the following program
module test
contains
subroutine foo()
integer, allocatable :: a(:)
allocate(a(-5:5))
call bar(a)
print *, a
end subroutine
subroutine bar(a)
integer, intent(out) :: a(:)
a = 0
a(-4) = 3 ! here
a(2) = 3
end subroutine
end module
program x
use test
call foo()
end program
In the line marked with "here" I am doing something wrong. The fact is that when I receive the array a (in the caller allocated from -5 to +5), the callee uses conventional numbering (1 to n), meaning that assigning -4 I am doing an out of boundary assignment. How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?
The type of dummy argument that you are are using in the subroutine, with the dimension specified with a colon, is called "assumed shape". This name is the clue -- Fortran passes only the shape and not the lower and upper bounds. The lower bound is assumed to be one unless you override it as shown in the answer by kemiisto. If the lower bound is not fixed, you can pass an argument to use as the lower bound.
Later addition: a code example if the lower dimension isn't known at compile time:
subroutine example (low, array)
integer, intent (in) :: low
real, dimension (low:), intent (out) :: array
There are two common options:
As kemisto wrote, you pass a second argument. This was common in F77-style code. You can not use the LBOUND trick! It has to be passed as an integer.
You declare the argument to be a pointer, which includes the entire array descriptor. Then the bounds of the array in the subroutine are the same as in the calling scope. Of course you may lose on optimization this way.
How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?
Not sure but according to the standard you can specify the lower bound for an assumed-shape array.
subroutine bar(a)
integer, intent(out) :: a(-5:)

Resources