Recasting Fortran pointers

269 views
Skip to first unread message

Adrian Ferramosca

unread,
Nov 14, 2001, 9:47:34 AM11/14/01
to
I would like to enquire whether anyone has been able to performs some tricks with F90/F95 pointers to be able to recast them at
runtime. For example, I would like to set up the following data structure:

type Test
sequence
integer(4) :: ptype
type(xxx), pointer :: p
end type

where xxx is one of a number of predefined types. At runtime, if ptype is 0, then p points to a variable of type xxx0 while if
ptype is 1 it points to another variable different type xxx1.

I am using CVF 6.6.

Adrian

Michel OLAGNON

unread,
Nov 14, 2001, 10:02:37 AM11/14/01
to

Fortran pointers are not recastable. Their types are defined at compile time.
In your example, you need:
type Test


integer(4) :: ptype
type(xxx), pointer :: p

type(xxx1), pointer :: p1
type(xxx2), pointer :: p2
type(xxx3), pointer :: p3
.....
end type

Michel


--
Michel OLAGNON email : Michel....@ifremer.fr
http://www.ifremer.fr/metocean/group/michel/michel_olagnon.htm
http://www.fortran-2000.com/


Richard Maine

unread,
Nov 14, 2001, 11:19:25 AM11/14/01
to
"Adrian Ferramosca" <adr...@bjahouston.com> writes:

This is a form of polymorphism. In f2k there is support for this kind
of thing, but in f90/f95, you have to roll your own, which can be
quite messy. I used to have some code to do something for this with
TRANSFER. Can be done, but it is *VERY* messy. Whatever you can
imagine, it's worse than that. It also tends to break compilers.
Long ago, I redid my code that used the idea because I got tired of
continually debugging compilers.

You'd need to start with the "usual" trick for many "hacks" with
pointers. Make a derived type with a single pointer component.
This will will give you something where you can "get at" the
pointer itself instead of the target. If you write a pointer p
as an argument to intrinsics not specifically expecting a pointer,
what gets passed is the pointer's target; to work with the
pointer dope vector, you need to do something like the derived type
trick. Then you can TRANSFER to and from the derived type.

But a *BIG* complication is that pointers to different types may
take up different amounts of space. In practice, there are only
a few variations you will see, but in pinciple, every combination
of type, type parameters, and rank could be a different pointer
size. To accomodate that, you can't use your "type(xxx), pointer..."
because you don't know whether a pointer to type(xxx) is big enough.
Indeed, there is no fixed thing that you know apriori is big
enough. What I did is use an array of some arbitrary type. My
favorite arbitrary type is default integer. The array "wants" to
be allocatable, but that's not (yet) an option (unless you restrict
yourself to compilers supporting the allocatable stuff TR), so it
ends up being an array pointer. You need to do the TRANSFER twice;
once as an argument to SIZE to figure out how big the result is,
and then again after you have allocated a target of sufficient
size to hold the result. All very messy, impossible to follow
unless adorned with extensive comments, and prone to break
compilers. I think the SIZE(TRANSFER(...)) bit was one of the parts
that many compilers choked on, but its been a long time and I
forget all the details of the compiler bugs.

Basically, the integer pointer array is just a holder for the bits.
You always TRANSFER the bits out of it into a pointer of the
appropriate ttype before doing anything with them.

I can't really recommend that approach. I dropped it and went
back to some simple-minded schemes that I'd label as f77 style.
They were adequate for the need, mostly because the number of
targets for each type was quite modest. So for each type, I
made an array of pointers (using the "usual" trick again; I'm
assuming you've followed this group enough to understand it).
Where you have "type(xxx), pointer" above, I just substituted a
a scalar integer. That scalar integer is an index into the
array of pointers for the appropriate type. Not very "modern",
but it works fine, is reasonably easy to follow, etc. It helped
a lot that I was sure to have a relatively small number of
targets, so I could get by with making the arrays of pointers
statically sized. It also helped that targets were added and
deleted with relatively low frequency, so that the performance
issue of finding an "open" slot for a new one by searching
the array was not a problem.

I could probbaly drag out some sample code to illustrate if the
above words aren't enough, but probably not today. Other commitments
are about to call.

--
Richard Maine | Good judgment comes from experience;
email: my last name at host.domain | experience comes from bad judgment.
host: altair, domain: dfrc.nasa.gov | -- Mark Twain

Adrian Ferramosca

unread,
Nov 14, 2001, 12:40:24 PM11/14/01
to

"Richard Maine" <nos...@see.signature> wrote in message news:uen11pu...@altair.dfrc.nasa.gov...

> So for each type, I
> made an array of pointers (using the "usual" trick again; I'm
> assuming you've followed this group enough to understand it).

not sure what the "usual" trick is :(

> Where you have "type(xxx), pointer" above, I just substituted a
> a scalar integer. That scalar integer is an index into the
> array of pointers for the appropriate type. Not very "modern",
> but it works fine, is reasonably easy to follow, etc. It helped
> a lot that I was sure to have a relatively small number of
> targets, so I could get by with making the arrays of pointers
> statically sized. It also helped that targets were added and
> deleted with relatively low frequency, so that the performance
> issue of finding an "open" slot for a new one by searching
> the array was not a problem.
>
> I could probbaly drag out some sample code to illustrate if the
> above words aren't enough, but probably not today. Other commitments
> are about to call.

Great, whenever you have a spare moment...

Adrian


Jugoslav Dujic

unread,
Nov 14, 2001, 3:55:11 PM11/14/01
to
"Adrian Ferramosca" <adr...@bjahouston.com> wrote in message
news:l0yI7.2662$Oh1.31441@insync...

Since I happen to know that you use CVF, you might as well stick
to Cray pointers, which are broadly supported in CVF as an extension.
(it even supports Cray pointers to functions). Unfortunately,
it seems that it's not easy to isolate code using crays in a
separate subroutine(s) which "improves" portability.

They're especially useful for lpUserData stuffs which can
be frequently found in Windoze API:

TYPE(MyWindowProperties):: tMWP
!Associate address of tMWP with the window
i = SetWindowLong(hMyWnd, GWL_USERDATA, LOC(tMWP))

!... somewhere else, miles away, in the code:
TYPE(MyWindowProperties):: tMWP; POINTER(pMWP,tMWP)
!..et voila, all associated data are restored:
pMWP = GetWindowLong(hMyWnd, GWL_USERDATA)

Now, it may look like I'm advocating them -- I dislike the
syntax and they can be really dangerous in kids' hands --
but the truth is that I can't live without them in Win32 API
stuff which is C-based and frequently the address
you get may point to quite a various stuff.

Jugoslav
________________________
www.geocities.com/jdujic

Adrian Ferramosca

unread,
Nov 14, 2001, 4:07:39 PM11/14/01
to
Yes, I saw them and they did work. Unfortunately you can't use them in type definitions as far as I can tell.

Richard Maine

unread,
Nov 14, 2001, 5:10:59 PM11/14/01
to
"Adrian Ferramosca" <adr...@bjahouston.com> writes:

> "Richard Maine" <nos...@see.signature> wrote in message news:uen11pu...@altair.dfrc.nasa.gov...
> > So for each type, I
> > made an array of pointers (using the "usual" trick again; I'm
> > assuming you've followed this group enough to understand it).
>
> not sure what the "usual" trick is :(

Make a derived type with the pointer as a component, as in

type xxx_ptr_type
type(xxx_type), pointer :: p
end type

type(xxx_ptr_type) :: p_ptr

What is going to physically be in the p_ptr structure is the
"dope vector" of the pointer component - you could think of this as
the pointer itself instead of the target. It will typically have
things like the address and dimensions of the target. Details are
vendor-dependent, but you don't usually need to know them, except
at a conceptual level.

You can now make an "array of pointers" by


type(xxx_ptr_type) :: the_array(100)

This is really an array of 100 structures, each of which has
a pointer as it's sole component. The n'th pointer is the_array(n)%p.
Note that if you tried to declare an array of pointers directly
without this trick, you'd probably try

type(xxx_type), pointer :: wrong(100)

But this doesn't do what you want. This is a single pointer to
an array. This is not an array of pointers.

making a single-component derived type like this is the crux of
the "usual" trick.

My ugly transfer application is based on noting that the actual bits of
the pointer part are in the structure, and TRANSFER copies bits,
so something like

TRANSFER(p_ptr, an_object_of_some_other_type)

will copy the bits that you really want (the pointer itself). If
you had done just

type(xxx_type), pointer :: p

TRANSFER(p, an_object_of_some_other_type)

you'll end up copying the bits of the target of p instead of the
bits of the pointer; that isn't what you want.

>
> > Where you have "type(xxx), pointer" above, I just substituted a
> > a scalar integer. That scalar integer is an index into the

> > array of pointers for the appropriate type....
> > I could probbaly drag out some sample code...

> Great, whenever you have a spare moment...

Assuming that I'm not dragged away before I get it adequately copied
into here...

!--- Generic stuff applicable to multiple formats...
!--- (This is in a different module).
type gen_type
.... a bunch of stuff not pertinent to the question
!-- And there is a format_code elsewhere. Could be a component
!-- here, though it's actually in a separate structure for the
!-- application I pulled this from.
integer :: format_specific_index
!-- This provides a place for format specific data.
!-- I've slightly simplified an irrelevancy here;
!-- hope I didn't miss making comparable changes elsewhere.
end type

!-- Declarations specific to cmp4.
!-- There is simillar-looking stuff for the other formats.
!-- I'll omit the details of cmp4_type here - not relevant,
!-- except to note that it is very different from the other formats.

type cmp4_ptr_type
type(cmp4_type), pointer :: cmp4
end type

type(cmp4_ptr_type), save :: cmp4_ptrs(max_opens)
logical, save :: initialized = .false.

type(cmp4_type), pointer :: cmp4 !-- for scratch use only. Not saved.
....

!----- Here's where I allocate a new one.

!-- Make sure things are initialized in case this is the first call.
nullify(cmp4)
iostat = 0
if (.not. initialized) then
do i = 1 , size(cmp4_ptrs)
nullify(cmp4_ptrs(i)%cmp4)
end do
initialized = .true.
end if

!-- Find an unused "slot" and allocate it
alloc_loop: do i = 1 , size(cmp4_ptrs)
if (.not.associated(cmp4_ptrs(i)%cmp4)) exit alloc_loop
end do alloc_loop
if (i > size(cmp4_ptrs)) goto 8000
gen%format_specific_index = i
allocate(cmp4, stat=iostat)
if (iostat /= 0) goto 8000
cmp4_ptrs(i)%cmp4 => cmp4

!-- And then later (in another subroutine), when we want to
!-- get at the cmp4 data, we start with

cmp4 => cmp4_ptrs(gen%format_specific_index)%cmp4

Ack. I just got a call dragging me off, but I think I'm about
done anyway. The above code is a bit out of context, and I may
have messed up something trivial in attempting to edit out
distractions, but I hope it illustrates the general idea.
The gen structure contains information common to all of the
formats. The cmp4 atuff is just one of the possible types.

P.S. This stuff just *BEGS* for the object-oriented features of f2k.
I did this *BEFORE* I'd really figured out what object orientation was
about. After I finally got an explanation (from John Cuthertson) of
OOP that made sense to me, suddenly the light dawned that I'd been
doing that for years...I didn't know that the language could actually
help me instead of get in my way.

Jugoslav Dujic

unread,
Nov 14, 2001, 5:20:49 PM11/14/01
to
"Adrian Ferramosca" <adr...@bjahouston.com> wrote in message
news:E2BI7.2673$Oh1.31235@insync...

| Yes, I saw them and they did work. Unfortunately you can't use them in type
definitions as far as I can tell.
| Adrian

You're right; we did use them for interop with C shells but the fortran
code is ugly, since it has to include a local Cray pointer to dereference
address:

!-Fortran receiver---------------------------------------------------
integer function AddBaseBa1(tBaseBa1) result (ierror)

type(T_DMSI_BASE_BA1), intent(in):: tBaseBa1

type(T_DMSI_VOLT_RANGE):: tVoltRange(*); pointer (pVoltRange, tVoltRange)
integer:: i

iFrequency = tBaseBa1%iFrequency

pVoltRange = tBaseBa1%pVrange
do i=1, iBrVBaz
VBaz(i) = tVoltRange(i)%fVBase
Vmax(i) = tVoltRange(i)%fVmax
Vmin1(i) = tVoltRange(i)%fVmin1
Vmin2(i) = tVoltRange(i)%fVmin2
end do

ierror = 0

end function AddBaseBa1

!-Fortran header ------------------------------------------------

type T_DMSI_VOLT_RANGE
real fVbase ! O1.2
real fVmax ! O1.3
real fVmin1 ! O1.4
real fVmin2 ! O1.5
end type T_DMSI_VOLT_RANGE

type T_DMSI_BASE_BA1
integer(PTR_SIZE) pVrange ! Pointer to an array of iBrVBaz
T_DMSI_VOLT_RANGE structures
integer(2) iFrequency ! O1.6
end type T_DMSI_BASE_BA1

!-C header ------------------------------------------------------

typedef struct T_DMSI_VOLT_RANGE {
float fVbase; // O1.2
float fVmax; // O1.3
float fVmin1; // O1.4
float fVmin2; // O1.5
} DMSI_VOLT_RANGE;

typedef struct T_DMSI_BASE_BA1 {
DMSI_VOLT_RANGE* pVrange; // Pointer to an array of iBrVBaz
DMSI_VOLT_RANGE structures
short iFrequency; // O1.6
} DMSI_BASE_BA1;

Jugoslav
________________________
www.geocities.com/jdujic

Reply all
Reply to author
Forward
0 new messages