Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
146 Views

character array construction with variable length

Here is the code:

program main
	implicit none
	character(:), allocatable :: i
	write(*,*)["1","1"//"2"]
	i="2"
	write(*,*)["1","1"//i]
end program

I expect this two write will give the same results. But the first one gives ["1 ","12"] while the second one truncate "1"//i to "1". I know this may due to the fact that at compile time compiler can not estimate the length of "1"//i so it make the length with the longest length it can measure (which is one in this case). I think this is quite unexpected and compiler should warn about it or just raise an error.

0 Kudos
24 Replies
Highlighted
Valued Contributor III
145 Views

Try this:

   character(len=:), allocatable :: i

   write(*,*) [ character(len=2) :: "1","1"//"2" ]

   i="Hello World!"
   write(*,*)[ character(len=len_trim(i)+1) :: "1","1"//i ]

 

0 Kudos
Highlighted
Beginner
145 Views

Thanks for response!

What I wonder is that is it ok to let the code I mentioned above compiled without any warning/error?

0 Kudos
Highlighted
Black Belt
145 Views

You might submit this as a feature request for gfortran compatibility:

$ gfortran -Wall zy.f90
zy.f90:4:43:

                             write(*,*)["1","1"//"2"]
                                           1
Error: Different CHARACTER lengths (1/2) in array constructor at (1)

0 Kudos
Highlighted
Beginner
145 Views

But gfortran doesn't raise error for second write statement neither. By the way, I like what ifort does for the first write statement.

0 Kudos
Highlighted
145 Views

The standard says that all "ac-values" in a character array constructor have the same length. You can specify what that length is to be with a CHARACTER(n):: prefix in the constructor, as FortranFan shows. As an extension, when all the values are compile-time constants, Intel Fortran will use the length of the longest value. If not all the values are constants, then it picks the length of the first value. Either usage is nonstandard.

If you ask ifort for standards checking, it will complain:

t.f90(4): warning #8208: If type specification is omitted, each ac-value expression  in the array constructor of type CHARACTER must have the same length type parameters.   ['1']
        write(*,*)["1","1"//"2"]
-------------------^

Since we have an extension that defines an interpretation, it isn't an error for us in the way gfortran treats it.

The second case is not detectable at compile-time as the variable could be length zero.

0 Kudos
Highlighted
Beginner
145 Views

While with standards checking enabled, the second case is still without warning. My suggestion is that it should also warn in second case as "i" could be any length other than zero!

0 Kudos
Highlighted
145 Views

But it could also be zero. We don't give warnings or errors unless we can prove that there's a problem. A compiler is required to have the ability to diagnose syntax that does not conform to numbered syntax rules or constraints, but the second example violates neither.

0 Kudos
Highlighted
Beginner
145 Views

I see. Thanks again!

0 Kudos
Highlighted
145 Views

Something related:

program character_len_array
    implicit none
    integer :: i
    character(len=:), dimension(:), allocatable :: ca
    ca = ['a','bb'] ! works
    print *,ca  ! works
!dir$ if (.false.)
    do i=1,ubound(ca) ! error #6512: A scalar-valued expression is required in this context.
      print *,ca(i)
    end do
!dir$ else
    do i=1,2
      print *,ca(i)
    end do
!dir$ endif
 end program character_len_array
--------------
 a bb
 a
 bb

Steve,

print *,ca(i), size(ca(i)) !  produces error

however

print *,ca(i), len(ca(i))

produces

 a bb
 a            2
 bb           2

So the [] array constructor worked, but not as desired (different lengths), I know that this behavior is following Fortran specifications.

This one is interesting:

 program character_len_array
    implicit none
    integer :: i
    character(len=:), dimension(:), allocatable :: ca, ca2
    character(len=:), allocatable :: ca2mold
    ca = ['a','bb'] ! works
    print *,ca  ! works
!dir$ if (.false.)
    do i=1,ubound(ca) ! error #6512: A scalar-valued expression is required in this context.
      print *,ca(i)
    end do
!dir$ else
    do i=1,2
      print *,ca(i), len(ca(i))
    end do
!dir$ endif
    allocate(ca(2),mold=ca2mold)
    ca2(1) = 'aaaa'
    ca2(2) = 'bbbbbbbb'
    do i=1,2
      print *,ca2(i), len(ca2(i))
    end do
   
 end program character_len_array
-------------------
 a bb
 a            2
 bb           2
forrtl: severe (151): allocatable array is already allocated
Image              PC        Routine            Line        Source
libifcoremdd.dll   0F6FDA35  Unknown               Unknown  Unknown
character_len_arr  01101625  _MAIN__                    31  character_len_array.
f90
character_len_arr  01101B7F  Unknown               Unknown  Unknown
character_len_arr  011044E9  Unknown               Unknown  Unknown
character_len_arr  0110462D  Unknown               Unknown  Unknown
kernel32.dll       76E5338A  Unknown               Unknown  Unknown
ntdll.dll          77389902  Unknown               Unknown  Unknown
ntdll.dll          773898D5  Unknown               Unknown  Unknown

Compiled ok, aborted during runtime

The intention of the ca2 was to avoid using a user defined type, containing a pointer to a character(len=:) :: p, then using ca2(i)%p

---

gripe: can someone get the webmaster to NOT auto upcase I (lower case i). It is a pain in the ass to work around the upcase when writing code examples in the body of the post. Everyone can understand when i use a lower case I for me. Disambiguating I from l is problematic (lower case I from lower case L).

Jim Dempsey

0 Kudos
Highlighted
145 Views

i don't think the web site is uppercasing i. maybe your web browser is doing it.

Jim, in your first example, ['a','bb'] works because of our extension I described previously. As your later code shows, we extend 'a' to 'a '.

The use of UBOUND(ca) gets an error because this has an array result. You want UBOUND(ca,DIM=1) instead.

What do you think is a problem with that last example? When you get to the allocate, ca has already been allocated at line 6.

0 Kudos
Highlighted
145 Views

Me bad, I should have allocated ca2, not ca

...
    allocate(ca2(2),mold=ca2mold)
    ca2(1) = 'aaaa'
    ca2(2) = 'bbbbbbbb'
    do i=1,2
      print *,ca2(i), len(ca2(i))
    end do
----
             0
            0

It didn't produce what I wanted (I guess I got what I deserved).

I was expecting the reallocate lhs to redefine the len=: of each element of the array ca2. However, I guess the specification requires all elements of the (character) array to be of equal length, iow the LEN property is that of the array and not that of the (each) element.

I think this is what Zoudong was looking for. He (she?) will have to accept using LEN= at worst case requirement, or use an array of pointers with the extra coding effort. Use of FPP preprocessor with appropriately #defined macros may eliminate the extra coding effort.

 program character_len_array
    implicit none
    integer :: i
    type character_pointer
        character(len=:), allocatable :: cp
    end type character_pointer

    type(character_pointer), dimension(:), allocatable :: ca
    
    ca = [ca, character_pointer("a")]
    do i=1,size(ca)
      print *,ca(i)%cp,len(ca(i)%cp)
    end do
    ca = [ca, character_pointer("bb")]
    do i=1,size(ca)
      print *,ca(i)%cp,len(ca(i)%cp)
    end do
#define ca(n) CA(n)%cp
    do i=1,size(CA) ! note case difference
      print *,ca(i),len(ca(i))
    end do
  end program character_len_array
--------------
 a 1
 a 1
 bb 2
 a 1
 bb 2

Jim Dempsey

0 Kudos
Highlighted
145 Views

Better example:

 program character_len_array
    implicit none
    integer :: i
    type character_pointer
        character(len=:), allocatable :: cp
    end type character_pointer

    type(character_pointer), dimension(:), allocatable :: ca
   
    ca = [character_pointer("Short message")]
    ca = [ca, character_pointer("Longer message than the First message")]
    
#define ca(n) CA(n)%cp

    do i=1,size(CA) ! note case difference
      print *,ca(i),len(ca(i))
    end do
  end program character_len_array
--------------
 Short message 13
 Longer message than the First message 37

Jim Dempsey

0 Kudos
Highlighted
145 Views

I forgot to mention, the use of FPP macros will preclude you from using the macro name in the debug session. In the above example, the two are intuitively interchangeable. More complex macros would be a bit more cumbersome for debugging.

(character_pointer is a misnomer, character_message, might be better)

Jim Dempsey

0 Kudos
Highlighted
145 Views

jimdempseyatthecove wrote:

Me bad, I should have allocated ca2, not ca

...
    allocate(ca2(2),mold=ca2mold)
    ca2(1) = 'aaaa'
    ca2(2) = 'bbbbbbbb'
    do i=1,2
      print *,ca2(i), len(ca2(i))
    end do
----
             0
            0

It didn't produce what I wanted (I guess I got what I deserved).

You allocate ca with a shape of [2] and specify ca2mold for mold=. Since ca2mold is unallocated, the program is immediately nonconforming:

F2008 6.7.1.2 - emphasis mine
5 If source-expr is a pointer, it shall be associated with a target. If source-expr is allocatable, it shall be allocated. 

What apparently happens is that zero is used as the "length type parameter" and therefore the assignments are ignored.

0 Kudos
Highlighted
145 Views

So there is no practical way to do what I did in #13 without the use of the UDT (and corresponding requirement of %memberVariable).

It is interesting (to me) that character_pointer("SomeText") implicitly converts the argument where

characterVar = ca(i)

does not. No symmetry. A user defined copy operator would be required. This would not satisfy LEN(ca(i)), with no macro assist.

Jim Dempsey

0 Kudos
Highlighted
145 Views

Fortran does not have the concept of an array where each element has different type parameters (length is a type parameter). Using a type is the only way to do this. Similarly, Fortran does not have the concept of an array of pointers - again you need a user-defined type.

character_pointer("SomeText") is a structure constructor. With a component that is allocatable, you're effectively doing an ALLOCATE with SOURCE= the value and this will allocate to the length of the value.

I'm not sure what you're asking about "characterVar" since that isn't in any of the examples so far. If that's ALLOCATABLE, CHARACTER(:), it will get reallocated on assignment to the length of the value being assigned, but from the examples here, ca is a derived type so unless you have user-defined assignment for that combination, such an assignment is not allowed.

0 Kudos
Highlighted
Black Belt
145 Views

jimdempseyatthecove wrote:

So there is no practical way to do what I did in #13 without the use of the UDT (and corresponding requirement of %memberVariable).

It is interesting (to me) that character_pointer("SomeText") implicitly converts the argument where

characterVar = ca(i)

does not. No symmetry. A user defined copy operator would be required. This would not satisfy LEN(ca(i)), with no macro assist.

Jim Dempsey

You can write a function that has a dummy argument of the derived type `character_pointer` (or whatever you want to call it) and stick it behind a generic interface named LEN.  The LEN intrinsic will still be available for real CHARACTER arguments.

You can write UDDTIO procedures for that derived type.

In combination with things like defined assignment and other defined operations, the requirement for %memberVariable becomes pretty nominal. 

(Several examples of "string" types with this sort of stuff are already publicly available, so, if they suit, the amount of new code writing that needs to be done may be very small.)

I would not use the preprocessor for this sort of stuff.

0 Kudos
Highlighted
145 Views

Thanks IanH.

Do you have a link to your favorite string types?

I too would prefer not to use the preprocessor due to portability and debugging issues.

BTW, I think it was an unfortunate naming to use UDDTIO since the name seems to imply related to I/O (READ/WRITE). The naming does not infer the section can be used for other purposes (the document should include some clear-cut examples).

Jim Dempsey

0 Kudos
Highlighted
145 Views

Um, UDDTIO is just for I/O - it has no other purposes. Ian was just saying that you can treat these "variable string types" as normal variables in READ/WRITE with a suitable UDDTIO procedure.

0 Kudos