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

CHARACTER*(*) Problem

HKort
Beginner
2,119 Views
Hi,
I have trouble using the CHARACTER*(*) string definition. The problem is as follows and the code is attached below. I have a routine that receives a string from a third party software package written in C that defines the string as shown in the program below. I need to convert this for use in fortran, which is what the routine idl_2_fort is supposed to do. In the main routine the variable FNAME, which will hold the filename in fortran, is defined as CHARACTER*200. However, when I pass is into the idl_2_fort routine, where the corresponding variable FORTSTR is defined as CHARACTER*(*), I find on debug that the actual string length in the routine is CHARACTER*1.

My understanding is that the *(*) is supposed to adopt the length of the string from the routine that passes the variable. I found a fix using the definition "character fortstr*f_len", but I would like to know why *(*) did not work the way I expected it to. Can anyone enlighten me please.

Thanks,
Haje


subroutine test(...)
structure /string/
integer*4 slen
integer*4 stype
integer*4 s
end structure

character*200 fname
integer*4 flen
record /string/ fln
...
fname=''
call idl_2_fort(%val(fln.s),fln.slen,fname,200)
...
return
end

subroutine idl_2_fort(idlstr, strlen, fortstr, f_len)

c purpose: copies an idl string to a fortran character string.

integer*4 strlen
character*(*) idlstr
character*(*) fortstr
integer*4 f_len

c if the idl string is smaller then copy the entire string into
c the fortran string, otherwise truncate it.

if(strlen .le. f_len )then
fortstr(1:strlen)=idlstr(1:strlen)
else
fortstr(1:f_len)=idlstr(1:f_len)
endif

return
end
0 Kudos
12 Replies
TimP
Honored Contributor III
2,119 Views

This depends on the C function passing the string length, as well as the address, in the order required by the particular Fortran compiler in use. You should find examples in the ifort documentation. The alternative is to make the Fortran search for a null character, if it is a null terminated string.
0 Kudos
Steven_L_Intel1
Employee
2,119 Views
The use of CHARACTER*(*) requires that the caller pass the string length separately. Where that length is passed is implementation-dependent. Intel Fortran expects it to be as a "hidden argument" after of all of the declared arguments, as an address-sized integer passed by value. What does your C call look like?
0 Kudos
HKort
Beginner
2,119 Views
Steve,
the third party C code is private, so I can't see that code. The API documentation describes how the structure that contains the string is defined which I show in my code. I forgot to indicate that the structure FLN is the one that is passed from the private C code into the routine I call test (subroutine test (fln,...)). Does this mean that I do not have sufficient information to use the *(*) declaration?

It is interesting to note that when bounds checking is turned of the routine actually does work, but it is not solid programming work.

Haje
0 Kudos
Steven_L_Intel1
Employee
2,119 Views
Ah, I really should learn to read posts more closely.

I gather that fln%s is, on the C side, a pointer to a string. If so, C determines the length by appending a NUL terminator. Fortran does not look for this. You will have to specify the REFERENCE attribute on the idlstr argument, declare it with some maximum length, and use INDEX to find the NUL, then use that to determine the length.
0 Kudos
HKort
Beginner
2,119 Views
Hi Steve,
you are correct that fln%s is a pointer (I should have mentioned that). From your response I learn that there is a solution to the problem, but I am not quite sure how to implement what you described. I hate to abuse your time, but if it is not too much work, is there an example you can provide that does what your propose?

Thanks again,
Haje
0 Kudos
Steven_L_Intel1
Employee
2,119 Views
Is slen from the structure the length of s? If so, then simply replace the character*(*) of idlstr with character*1000 or something large enough. Otherwise, still use 1000 and then compute the length like this:

integer ilen
ilen = index(idlstr,char(0)) - 1

Now ilen has the "C length" of idlstr. But from reading the code, I suspect you already have the length in strlen. When you view idlstr in the debugger it may seem to be too long, but that's ok.




0 Kudos
HKort
Beginner
2,119 Views
Steve,
thanks. The variable IDLSTR actually gets properly transfered and the *(*) definition properly transfers a string of length *200. It is the FORTSTR vartiable, defined as *200 in the higher level routine, but set to fname='' prior to passing into idl_2_fort, which gets turned into *1. (I may have sent you on the wrong path.)

Haje
0 Kudos
Steven_L_Intel1
Employee
2,119 Views
Oh, right. The problem here is that you have an argument mismatch, so the length for FORTSTR is in a different place than where the called routine expects it. I was glossing over that before but now see that it needs fixing.

So do this. In the caller, declare:

CHARACTER(1000) IDLSTR
POINTER (P_IDLSTR, IDLSTR)
...
P_IDLSTR = FLN%S

Now pass IDLSTR rather than FLN%s.

0 Kudos
HKort
Beginner
2,119 Views
Steve,
It looks like a solution is just ahead and I even understand it :-). The data type POINTER is a FORTRAN 95(?) feature. Can this be addressed with pure F77 language as well?

Haje
0 Kudos
Steven_L_Intel1
Employee
2,119 Views
This particular usage of POINTER is an extension, not the same as F90 POINTER. Your old code is hardly "pure F77" - STRUCTURE, RECORD and %VAL are all extensions.
0 Kudos
HKort
Beginner
2,119 Views
Steve,
The lines between the standards are blurred in my memory. So I already crossed thelines befoer, so I will stick with the POINTER definition. Thanks for all the explanations today. I learned something.

Haje

PS: Are you gonna have a party for your 10000th posting? :-)

0 Kudos
Steven_L_Intel1
Employee
2,119 Views
Eh, they'll probably switch to new forum software just as I hit 9999.
0 Kudos
Reply