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

How to copy a deferred size character array to a deferred type character array?

MZ2
New Contributor I
1,280 Views

I am working on converting an exe to a dll. I am passing strings from c# to the dll, and the strings that are coming from c# are specified as follows:

character(len=1), dimension(*), intent(in) :: fileName

I am also passing an integer that gives the length of each string. This seems to work as expected, and I can easily iterate through the characters in the string using the passed length.

As I see it, the character declaration says essentially this: "this is a deferred length character array where each character is 1 byte in length, and the array itself can be any length."

The problem is that some of the code that I am interfacing with was written by another programmer. Though I consider my Fortran skills perhaps intermediate to advanced, I am having trouble grasping how to handle this situation.

The character variable that I need to copy the string to is defined as a deferred type variable as follows:

CHARACTER(:), ALLOCATABLE :: string_data

So this declaration, as far as I can tell, says something completely different, and it says this is a single character where the character itself can be any number of bytes in length.

NOTE: The array is subsequently allocated to the length of the string that it is expected to hold.

If I try to do a simple copy from the string that is coming from c# such as

string_data = fileName(1:fileNameLength)

The compiler gives me an error that says that the shapes of the arrays do not match. This, to me anyway, seems to make sense if I am correct about the meanings of the declaration statements. However, if I set that string to a string constant such as

string_data = '1234567890xyz'

it compiles fine, and works as expected.

If I change

CHARACTER(:), ALLOCATABLE :: string_data

to something that would match like the following:

CHARACTER(len=1), ALLOCATABLE :: string_data(:)

Not only does it give me hundreds of errors when I compile, there is also a chance that this will break a number of different programs since the code where this is declared is used for several other projects. So, I would prefer, if possible, not to change that code.

The closest I have come to getting this to work is with the following code:

read(string_data, *) fileName(1:fileNameLength)

However, this only reads the first character from "fileName", and I have am having difficulty determining a format specifier that will correctly convert the data types between the two.

Anyone have any suggestions as to how to handle this?

Thanks in advance!

0 Kudos
1 Solution
MZ2
New Contributor I
1,280 Views

OK, well figured that out quicker than I expected.

string_data = transfer(fileName(1:fileNameLen), string_data)

Thanks.

View solution in original post

0 Kudos
4 Replies
MZ2
New Contributor I
1,281 Views

OK, well figured that out quicker than I expected.

string_data = transfer(fileName(1:fileNameLen), string_data)

Thanks.

0 Kudos
JVanB
Valued Contributor II
1,281 Views

You might also perform allocation on assignment, does ifort still require a command line switch for this to work?

string_data = transfer(filename,repeat('A',fileNameLen))

 

0 Kudos
IanH
Honored Contributor II
1,281 Views

Some terminology notes:

character(len=1), dimension(*), intent(in) :: fileName

The above declares an assumed size array of type character with length one.  The array (number of elements) assumes the size of the corresponding actual argument, if required the value of that corresponding size must be communicated separately.

CHARACTER(:), ALLOCATABLE :: string_data

The above declares an allocatable scalar of type character with deferred length.  The length is specified when the scalar is allocated.

One way of copying the data from an array of size some_value to a scalar of length some_value is to use assignment in a loop.

! Assumed size dummy array of type character with length one (the default).
! (slightly different form of declaration, with same meaning, just for example.)
CHARACTER, INTENT(IN) :: fileName(*)   
! The size of above array/length of resulting scalar specified elsewhere 
! (perhaps it is another dummy)
INTEGER :: some_value
! Allocatable scalar of type character with deferred length.
CHARACTER(:), ALLOCATABLE :: string_data
! An integer index.
INTEGER :: i

! Specify the length of the scalar.
ALLOCATE(CHARACTER(some_value) :: string_data)

! Copy the characters using a boring do loop.
DO i = 1, some_value
  string_data(i:i) = fileName(i)
END DO

! Or, depending on your source preferences, copy using multiple assignment (obsolescent syntax in F2015)
! FORALL (INTEGER :: j = 1:some_value) string_data(j:j) = fileName(j)

In the (possible future) case where the kind of the character array might be different from the kind of the character scalar, the assignment will do the appropriate conversion, you won't get that with TRANSFER.

0 Kudos
Kevin_D_Intel
Employee
1,281 Views

To Repeat Offender's question: Beginning with the 17.0 compiler (PSXE 2017 release) a compiler option ( /assume:realloc_lhs ) is *no* longer needed. In 17.0 the default changed to match the Fortran 2003 standard so that allocatable arrays are automatically (re)allocated on intrinsic assignment as needed. As noted in the 17.0 RNs, to revert to the older behavior, specify /assume:norealloc_lhs or the new /nostandard-realloc-lhs compiler option.

0 Kudos
Reply