- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
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
Link Copied
5 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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 ? :-):
- 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 :-)].
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
...here it is
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
(Italic from the bottom of the post comes from angle-bracketed cs{i} in sprintf -- I should've used a different loop variable ;-))
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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:
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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'd suggest something like:
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
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; jI 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.); 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
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
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page