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

V12 character string allocation

nvaneck
New Contributor I
1,037 Views
Before V12 I used the trick of a character string pointer to an allocated string array to accomplish character string allocation. This is disallowed in V12, so I'm changing to the 2003 string allocation method, but I can't get it to allocate more than 1 character.

I used:

CHARACTER(LEN=:), ALLOCATABLE:: LINE_IN,RUNFILE

ALLOCATE (CHARACTER(LEN=5000) :: RUNFILE,LINE_IN)

which the compiler accepts, but whenLINE_IN is passed to a routine when it is defined as

COMMAND_LINE*(*), the length seems to be 1, so I get an exception when I try to put something in it.

What is the correct way to do this?

Thanks

Neal Van Eck

0 Kudos
11 Replies
Steven_L_Intel1
Employee
1,037 Views
This should work - can you show a small but complete example that fails?
0 Kudos
nvaneck
New Contributor I
1,037 Views

Well, further investigation uncovers the problem, for which I don't have a good solution.

When I make the allocation and set the string to 5000 blanks, I'm ok. Later I attempt to blank it out with the statement LINE_IN=' '.

But this no longer works, as the string is shortened to one character. So maybe I need to alwaysblank it to the maximum each time I want to use it?This is awkward as I then have to continually blank it before reading line after line into the string instead of just reading a new line over the last. There may be a way around this; does reading a line into it always reallocate the string? (inefficient?). Even when read by a subroutine that takes it as an argument?

Right now, within the subroutine I'm almost always adding to the line (not wanting to change the allocated length). Does this then force a reallocation as well? The following line is typical of what causes trouble (says ILEN is beyond lenght od COMMAND_LINE.

21 ILEN=LEN_TRIM(COMMAND_LINE)+1

COMMAND_LINE(ILEN:ILEN)=' '

I don't want any reallocation; I want to allocate it once and have it remain that lenght.

0 Kudos
Steven_L_Intel1
Employee
1,037 Views
Well, that's the way the language is specified. If you know it will be 5000 characters, declare it as CHARACTER(5000), ALLOCATABLE.
0 Kudos
nvaneck
New Contributor I
1,037 Views
Thanks.

So it doesn't work like allocating arrays. Are you saying there is another form I should be using? Is

CHARACTER(5000), ALLOCATABLE

legit? How do you then allocate it. Sorry to be obtuse, but the help files don't say anything about character string allocation in the evaluation version I installed (nor did they install in VS 2005), so I'm thrashing here a little trying to fgure it out. I can always just statically allocate large strings, but I wanted to go to the latest standard to take advantage of character stirng allocation, butperhaps it does me no good, and I lose facility going to V12.

0 Kudos
Steven_L_Intel1
Employee
1,037 Views
You can have allocastable scalars. Just write ALLOCATE(var). But LEN(:) changes length with each assignment.
0 Kudos
nvaneck
New Contributor I
1,037 Views

Ok, Thanks, Steve.

That can be useful in other places for me. Another problem has cropped up, though.

It seems that one cannot use somthing like CHARACTER*32000 RECORD for a record of arbitrary length in a subroutine, where the record length is passed to the subroutine separately.It passes the compile but fails when referenced. Is there any way to accomplish this in the V12 ? I can't use an exact length vector here as the record source varies with different calling routines. Is this only a problem with bounds checking on?

0 Kudos
Steven_L_Intel1
Employee
1,037 Views
I'd need an example - don't understand what you are doing. What exactly is this "record"? Maybe you need polymorphism and type extension - not sure. Or maybe an adjustable or assumed-shape array rather than a character string.
0 Kudos
nvaneck
New Contributor I
1,037 Views

Ok:

SUBROUTINE FPUT(DCB,RECORD,L,FATAL)

USE SPACE

IMPLICIT NONE

INTEGER(4) IO_STATUS,L

INTEGER(4), PARAMETER:: ICR=13,LF=10

TYPE (FCB) DCB

CHARACTER RECORD*32767

LOGICAL FATAL

IF (DCB%RECFM .NE. 0) THEN !RECFM=F

WRITE(DCB%HANDLE,IOSTAT=IO_STATUS) RECORD(1:DCB%LRECL)

ELSE !RECFM=U

WRITE(DCB%HANDLE,IOSTAT=IO_STATUS) RECORD(1:L),ACHAR(ICR),ACHAR(LF)

ENDIF

IF (IO_STATUS.NE.0) THEN

CALL FERROR(FILES(DCB%INDEX)%DSNAME,IO_STATUS)

FATAL=.TRUE.

ENDIF

END

The record can be anything the calling programs need to write--from a simple text line to a data output record. In the case that cropped up, the calling programmed passed string of length 512 characters with l=36. It seems to work in release mode without bounds checking on, but I haven't checked allt he output yet.

0 Kudos
Steven_L_Intel1
Employee
1,037 Views
Maybe do it like this:

[fortran]subroutine fput (DCB,RECORD,L,FATAL)
...
INTEGER, INTENT(IN) :: L
INTEGER(1), INTENT(IN), DIMENSION(L) :: RECORD
!DEC$ ATTRIBUTES NO_ARG_CHECK :: RECORD
...
WRITE(DCB%HANDLE,IOSTAT=IO_STATUS) RECORD, ACHAR(ICR),ACHAR(LF)
...[/fortran]
An alternative using only standard code is to pass C_LOC(record) to an argument that is of type C_PTR (from ISO_C_BINDING) and then use C_F_POINTER to "cast" it to something of the desired type and size.
0 Kudos
rwg
Novice
1,037 Views

Why don't you simply use the following declaration?
CHARACTER(*) RECORD

LEN(RECORD) gives youN if the calling programs passes a character declared with CHARACTER(N) RECORD so you also can test fort toolarge L or DCB%RECFM inside FPUT.

0 Kudos
nvaneck
New Contributor I
1,037 Views

That's what I ended up doing....I can't remember why I had it the original way--lost in time.

0 Kudos
Reply