From: Enthalpy on
Hello!

Thanks for all the answers, they're stimulating. My initial post was
not so "direct" because I was trying to investigate the options to
carry out my task. Let me say that I'd like to keep as efficient as
possible my code together with the ability to select at runtime the
set of equations from a number of implemented procedures. So I'd
discard (at least for now) the fascinating option to use/write an
interpreter as suggested by Glen and Jugoslav because of the
detrimental effect on the performance and (not least) because of the
complexity that it would imply for my limited coding abilities.

The solutions suggested by Eli and Arjen are also interesting but not
cross-platform. I also store these options for a potential "plan B",
since I'm coding on a GNU/Linux box but I'm planning to make the
program available also for Windows OSs (with the minimum effort,
hopefully).

So I went back on my initial idea to use pointers to select at runtime
the necessary items (unknowns, equations, parameters) and then I
bumped into the necessity to use the pointers to procedures F03
feature. I'm on Ubuntu Karmic 9.10 and I use the GNU compilers
(gfortran and g95) at these version

$ gfortran -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
4.4.1-4ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.4/
README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/
usr --enable-shared --enable-multiarch --enable-linker-build-id --with-
system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-
threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-
suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug
--enable-objc-gc --enable-targets=all --disable-werror --with-
arch-32=i486 --with-tune=generic --enable-checking=release --
build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9)

$ g95 -v
Using built-in specs.
Target:
Configured with: ../configure --enable-languages=c
Thread model: posix
gcc version 4.0.3 (g95 0.91!) Feb 26 2008


Unfortunately, gfortran (at the version packaged on Ubuntu Karmic)
doesn't support procedure pointers, so I have just g95 to play with. I
think I need an /array/ of procedure pointers, but I unpleasantly
realised they are not supported by the F03 standard since the
DIMENSION attribute is not applicable together with the PROCEDURE one
(correct me if I'm wrong, I'd be pleased to learn that). Exemplifying,
it should *not* be valid the following line:

PROCEDURE (prototype), DIMENSION(:), POINTER :: fp

I'm really disappointed that even F03 is missing this feature. I found
an "ACM SIGPLAN Fortran Forum" article with a suggestive title "Note
on creating an array of procedure pointers" (http://doi.acm.org/
10.1145/1520752.1520753) but unfortunately I'm just a student and I
cannot afford the price of the paper (my ACM web account seems not to
be enough); if someone would be so generous to email it to me, I would
be very grateful to him/her...

Anyway I tried another solution: to encapsulate in a derived type the
procedure pointer and then build an array of the aforementioned
derived type:

TYPE :: arr_fptr
PROCEDURE (prototype), POINTER :: fp
END TYPE arr_fptr

ABSTRACT INTERFACE
FUNCTION prototype()
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15,100)
REAL(dp) :: prototype
END FUNCTION prototype
END INTERFACE

TYPE(arr_fptr), DIMENSION(5), SAVE :: funcp

but it fails at the first assignment

funcp(1)%fp => maci_mflow_tg() ! maci_mflow_tg is function with no
actual/dummy arguments (parameters are passed via module association).
It is an implemented equation.

with the following error: "Any component to the right of a parent or
component of nonzero rank must not have the POINTER attribute."

Finally I came out with the solution to use a linked list of pointers
to procedures (please note this is just a *crude* version, without the
ISTAT clause --- for example --- and other good programming practices
that I'll add once the code will work correctly):

TYPE :: eqns_list
PROCEDURE (prototype), POINTER :: fp
TYPE(eqns_list), POINTER :: next
END TYPE eqns_list

ABSTRACT INTERFACE
FUNCTION prototype()
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15,100)
REAL(dp) :: prototype
END FUNCTION prototype
END INTERFACE

TYPE(eqns_list), POINTER, SAVE :: head, tail, temp

ALLOCATE(head)
tail => head
NULLIFY(tail%next)
tail%fp => maci_mflow_tg()

ALLOCATE(tail%next)
tail => tail%next
NULLIFY(tail%next)
tail%fp => maci_Tc()

ALLOCATE(tail%next)
tail => tail%next
NULLIFY(tail%next)
tail%fp => maci_lvlv()

ALLOCATE(tail%next)
tail => tail%next
NULLIFY(tail%next)
tail%fp => maci_pmec()

ALLOCATE(tail%next)
tail => tail%next
NULLIFY(tail%next)
tail%fp => maci_mu()

(snip)

temp => head
DO
i = 1
IF ( .NOT. ASSOCIATED(temp) ) EXIT
funcv(i) = temp%fp()
i = i + 1
temp => temp%next
END DO

(snip)

But even this very reasonable option ends up in a compile time error:

tail%fp => maci_mflow_tg()
1
Error: Different types in pointer assignment at (1)

(snip)

Could you help me with that, please?
Do you have any suggestion to improve the efficiency of the code I
posted above?
Is there a better way to achieve the same goal preserving efficiency
and portability?

Thank you very much.


PS: The unknowns are selected using a derived type:

TYPE :: arr_ptr
REAL(dp), DIMENSION(:), POINTER :: p
END TYPE arr_ptr

TYPE(arr_ptr), SAVE :: xp

ALLOCATE( xp%p(5) )

xp%p(1) = lvlv
xp%p(2) = ps
xp%p(3) = pc
xp%p(4) = mu
xp%p(5) = Tc

Again, just a crude implementation, refinement is necessary if the
code proves to work.

From: James Van Buskirk on
"Enthalpy" <epagone(a)email.it> wrote in message
news:bb55b7e1-cf18-4238-96a0-7faad68232b5(a)x12g2000yqx.googlegroups.com...

> Anyway I tried another solution: to encapsulate in a derived type the
> procedure pointer and then build an array of the aforementioned
> derived type:

> TYPE :: arr_fptr
> PROCEDURE (prototype), POINTER :: fp
> END TYPE arr_fptr

> ABSTRACT INTERFACE
> FUNCTION prototype()
> INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15,100)
> REAL(dp) :: prototype
> END FUNCTION prototype
> END INTERFACE

> TYPE(arr_fptr), DIMENSION(5), SAVE :: funcp

> but it fails at the first assignment

> funcp(1)%fp => maci_mflow_tg() ! maci_mflow_tg is function with no
> actual/dummy arguments (parameters are passed via module association).
> It is an implemented equation.

> with the following error: "Any component to the right of a parent or
> component of nonzero rank must not have the POINTER attribute."

Unless maci_mflow_tg is a function returning a procedure pointer
result, you want

funcp(1)%fp => maci_mflow_tg

above, without the parentheses.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


From: Tobias Burnus on
Hello,

Enthalpy wrote:
> gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9)
>
> Unfortunately, gfortran (at the version packaged on Ubuntu Karmic)
> doesn't support procedure pointers

That's not completely true. GCC 4.4.x supports procedure pointers -
except for functions returning procedure pointers and for
procedure-pointer components. (The latter is what you use.) GCC 4.5 also
supports the remaining bits - including PASS.

> TYPE :: arr_fptr
> PROCEDURE (prototype), POINTER :: fp
> END TYPE arr_fptr
> ABSTRACT INTERFACE
> FUNCTION prototype()

Add a NOPASS in the PROCEDURE statement as "prototype" does not have
"type(arr_fptr)" as first argument. While g95 does not seem to care,
gfortran 4.5, ifort 11.1 and the standard do ;-)

Tobias
From: Enthalpy on
James,

This is the maci_mflow_tg function:

REAL(dp) FUNCTION maci_mflow_tg()
maci_mflow_tg = eta_m_t * eta_is_t * Ts * (ONE - ( pa/ps )**( (k-ONE)/
k ) ) - ( Tc-Ta )/eta_m_c
END FUNCTION maci_mflow_tg

I tried to "point" it without the parentheses

TYPE :: arr_fptr
PROCEDURE (prototype), POINTER :: fp => NULL()
END TYPE arr_fptr

ABSTRACT INTERFACE
FUNCTION prototype()
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(15,100)
REAL(dp) :: prototype
END FUNCTION prototype
END INTERFACE

TYPE(arr_fptr), DIMENSION(5), SAVE :: funcp

(snip)

funcp(1)%fp => maci_mflow_tg
funcp(2)%fp => maci_Tc
funcp(3)%fp => maci_lvlv
funcp(4)%fp => maci_pmec
funcp(5)%fp => maci_mu

and it compiles correctly, many thanks! Deleting the parentheses
corrects also the linked list "version" :D .

Just a question, still. How can I nullify the procedure pointer, when
needed (could it be necessary, indeed??)?
Compilation fails either if I use the

funcp(:)%fp => NULL()

and

NULLIFY( funcp(:)%fp )

lines. Output of g95:

funcp(:)%fp => NULL()
1
Error: Component to the right of a part reference with nonzero rank
must not have the POINTER attribute at (1)

By the way, the url at the head of your post returns a 502 error; what
is that link about?


Tobias,

On Mar 29, 9:01 am, Tobias Burnus <bur...(a)net-b.de> wrote:

(snip)

> That's not completely true. GCC 4.4.x supports procedure pointers -
> except for functions returning procedure pointers and for
> procedure-pointer components. (The latter is what you use.) GCC 4.5 also
> supports the remaining bits - including PASS.

thanks for pointing out this; trying to shorten the previous post my
sentences became not so clear and prone to misunderstandings.

> > TYPE :: arr_fptr
> >    PROCEDURE (prototype), POINTER :: fp
> > END TYPE arr_fptr
> > ABSTRACT INTERFACE
> >    FUNCTION prototype()
>
> Add a NOPASS in the PROCEDURE statement as "prototype" does not have
> "type(arr_fptr)" as first argument. While g95 does not seem to care,
> gfortran 4.5, ifort 11.1 and the standard do ;-)
>
> Tobias

I thought the PASS/NOPASS attribute was necessary just when the
CONTAINS statement is present in a derived type definition (Chapman,
"Fortran 95-2003 for scientists and engineers (3rd ed.)"); am I wrong?


All,

some posts ago I've got no solution to my problem, instead now I have
(potentially) two options: the linked list and the array of derived
type procedure pointers. Which one do you suggest me to implement
according to (sorted by importance):
1. performance;
2. reliability;
3. code maintenance (in other words, clarity);
4. neatness.

Thanks again for your patience.


Emanuele
From: James Van Buskirk on
"Enthalpy" <epagone(a)email.it> wrote in message
news:3ab7e1de-e269-483b-8c6b-f9eb90acd698(a)30g2000yqi.googlegroups.com...

> Just a question, still. How can I nullify the procedure pointer, when
> needed (could it be necessary, indeed??)?
> Compilation fails either if I use the

> funcp(:)%fp => NULL()

Yeah, vector pointer assignment and nullification don't work.
You could write out a DO loop that nullifies the pointers one at a
time, or a subroutine that accepts funcp(:) as an INTENT(OUT)
argument:

subroutine nullp(fparg)
type(arr_fptr), intent(out) :: fparg(:)
end subroutine nullp

And then when you call nullp(funcp) default initialization that
you have written will nullify everybody (I think). But you
probably would prefer something like:

funcp = arr_fptr(NULL()))

which should do the trick via intrinsic assignment. No time
to test today, though.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end