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

String Arrays and XML

Intel_C_Intel
Employee
1,325 Views
A couple of 'Has anyone ever...?' questions:

1) ... read an XML file using FORTRAN? Any recommendations? I didn't particularly want to use COM or anything (MSXML), but neither did I want to hardcode the file 'structurre' in my parsing code.

2) ... passed an array of strings from FORTRAN to C/C++? How was this achieved? I think the method we use is highly inelegant and I'm sure there must be a better way.

Thanks for your thoughts,

Dan
0 Kudos
5 Replies
Jugoslav_Dujic
Valued Contributor II
1,325 Views
1) There's^H^H used to be a project started by Arjen Markus at sourceforge. Attached is the gzip I downloaded at the time it was there. Still, it's just a basic attempt -- MSXML is far more powerful (I used it from Delphi and it was rather plain sailing).

2) Is the following more elegant ? :-):
type t_cstring
  character(747), pointer:: s => NULL()
end type t_cstring
type (t_cstring):: cs(42)
character(85), target::    str(42)
...
!str must be NULL-terminated, of course.
do i = 1, 23
   cs(i)%s => str(i)
end do
...
!Interface block to c_routine, of course
call c_routine(cs)
...
//==8<=========================
void c_routine(char** cs)
{
  for (int i=0; i++; i<23) 
     printf("%s", cs); 
}
The idea is to use that:
- CHARACTER(), POINTER:: in CVF is implemented as a 4-byte pointer, i.e. the same as in C
- In Fortran, an array of pointers cannot be directly achieved, but one has to pack them into an array of derived types
- If c_string is used only for transport, you can just use 1 instead of 747. Otherwise, if you intend to manipulate cs%s directly, it has to be of sufficient length and ALLOCATEd accordingly per each element of array cs.

There are variations on the theme, e.g. using array of integers(int_ptr_kind()) and retrieving strings' addresses using LOC().

Jugoslav

[Mentioned gzip in the followup. Author's e-mail address is arjen.markus wldelft.nl so you can contact him directly -- he's a friendly guy :-)].
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,325 Views
...here it is
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,325 Views
(Italic from the bottom of the post comes from angle-bracketed cs{i} in sprintf -- I should've used a different loop variable ;-))
0 Kudos
Intel_C_Intel
Employee
1,325 Views
Jugoslav, thank-you. The XML stuff looks pretty neat.

As far as the string arrays go - I might have to have a play with your method before I understand it enough to say if its an improvement (I think it is ! ;o) )

Current in the C layer we'd allocate some new chars to an LPSTR (say strAlloc) and blank them all to spaces. Then call the FORTRRAN thus:

Num = GET_USER_FUNCTIONS(strAlloc, &lDum);

			//------------------------------------
			// Now put into a SafeArray for return
			//------------------------------------

			if (Num > 0)
			{
				SAFEARRAYBOUND rgsabounds[] = { {Num, 0} };
				vArray.Create(VT_VARIANT, 1, rgsabounds);

				COleVariant Element;

				strTmp = strAlloc;

				//----------------------------
				// Add data to first dimension
				//----------------------------

				for (long i = 0; i < Num; i++)
				{
					memcpy (Name, strTmp, Lens);
					StripSpaces(Name, SLEN);
					Name[SLEN] = '�';
					strTmp += SLEN;
					Element.Clear();
					Element = Name;

					vArray.PutElement (&i, &Element);
				}

				delete[] strAlloc;

				return (vArray.Detach ());
			}


Its the safe array construction that seems most clumsy (these are COM calls). Chopping things up into fixed length strings, tagging on a NULL....lots of scope for errors, especially if you need to change string lengths.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,325 Views
I'd suggest something like:
char** lpStrings;  //pointer to array of strings

//allocate only the array of pointers
lpStrings = malloc(MAX_STRINGS*sizeof(char*))
GET_USER_FUNCTIONS(lpStrings, &nStrings)
for (int j=0; j);
FREE_STRINGS(lpStrings, &nStrings);
free(lpStrings);
//==8<=============
subroutine GET_USER_FUNCTIONS(cs, nStrings)
type(C_String):: cs(*)
integer, intent(out)::        nStrings
nStrings = 42
do j=1,42
  allocate(cs(j)%s)  !LEN(cs%s) must be substantial to
                     ! hold the longest one
  cs(j)%s = trim(something)//char(0)
end do
end subroutine
!------------
subroutine FREE_STRINGS(cs, nStrings)
type(C_String):: cs(*)
integer, intent(in)::        nStrings
do j=1,nStrings
   deallocate(cs(j)%s)
end do
end subroutine
I used the rule "whoever allocates, must deallocate" -- C allocates & frees lpString*, and Fortran allocates & frees lpSting** (= cs(j)%s) -- so I had to introduce additional FREE_STRINGS. Your mileage may vary, as you can change the point of allocation (e.g. move it from Fortran to C) -- it depends on "who knows" the desired length.

Note that, although it'd be even more elegant, you cannot use an array component to obtain different-width strings -- Fortran POINTER to an array is not the same as void*/char*/int*.

Jugoslav
0 Kudos
Reply