Optional arguments to fortran functions

940 views
Skip to first unread message

Daniele Nicolodi

unread,
Nov 19, 2010, 1:12:57 PM11/19/10
to fwrap...@googlegroups.com
Hello,

I'm trying to wrap some functions defined in a fortran F95 module in
python. I'm quite ignorant of fortran, so I went for the easiest route
and applied what Ondrej describes in this post:
http://groups.google.com/group/fwrap-users/browse_thread/thread/5c2389366a5267a1#

I succeeded in building a simple wrapper, I had just to manually mangle
function names defined in the fortran module. However I do no know how
to handle functions arguments declared as optional in fortran.

Can someone suggest a strategy?

Looks like there is not a standard way to map optional fortran arguments
to C. How fwrap plans to do it?

Thanks. Cheers,
--
Daniele

Ondrej Certik

unread,
Nov 19, 2010, 7:25:07 PM11/19/10
to fwrap...@googlegroups.com

Since then I learned a way better way. Just use the iso_c_bindigs
fortran module, and do it like this:

https://github.com/hpfem/phaml/tree/master/src/phaml/

see here how I handle optional arguments:

https://github.com/hpfem/phaml/blob/master/src/phaml/simple.f90#L44
https://github.com/hpfem/phaml/blob/master/src/phaml/simple_wrapper.pyx#L52

this way is almost perfect, it's robust (using the fortran compiler to
handle the types + function names), and then cython to handle the
rest.

Ondrej

Daniele Nicolodi

unread,
Nov 20, 2010, 11:03:38 AM11/20/10
to fwrap...@googlegroups.com
On 20/11/10 01:25, Ondrej Certik wrote:
> Since then I learned a way better way. Just use the iso_c_bindigs
> fortran module, and do it like this:
>
> https://github.com/hpfem/phaml/tree/master/src/phaml/
>
> see here how I handle optional arguments:
>
> https://github.com/hpfem/phaml/blob/master/src/phaml/simple.f90#L44
> https://github.com/hpfem/phaml/blob/master/src/phaml/simple_wrapper.pyx#L52
>
> this way is almost perfect, it's robust (using the fortran compiler to
> handle the types + function names), and then cython to handle the

Thank for this updated example. However this does not help much with my
problem. I'm trying to wrap OptoCad: http://www.rzg.mpg.de/~ros/optocad.html
where I have functions definitions that look like:

subroutine OC_INIT (pform, psize, rot90, plot, pid, unit, psout, prt)
!
! Initialization for the OptoCad program package.
! psize or pform specify the dimensions (in meters) of the total
! plotting area on the paper.
!
character(*), optional, intent(in) :: pform, pid, psout, unit, prt
character :: string*16
integer, optional, intent(in) :: rot90, plot
integer :: ib, ie, inoc
real, optional, intent(in) :: psize(2)

In my fortran wrapper I can not declare arguments as optional, because it
is not allowed by iso_c_binding. I would like to avoid to copy the optional
arguments handling code (default values and so on) from the oiginal fortran
function to my cython wrapper. There is a nice solution? There is a way to
pass a value to the fortran function so that the argument is treated as
missing?

Thank you. Cheers,
--
Daniele

John McFarland

unread,
Nov 20, 2010, 12:03:11 PM11/20/10
to Fwrap Users
On Nov 20, 10:03 am, Daniele Nicolodi <dani...@grinta.net> wrote:
> Thank for this updated example. However this does not help much with my
> problem. I'm trying to wrap OptoCad:http://www.rzg.mpg.de/~ros/optocad.html
> where I have functions definitions that look like:
>
>     subroutine OC_INIT (pform, psize, rot90, plot, pid, unit, psout, prt)
> !
> !  Initialization for the OptoCad program package.
> !  psize or pform specify the dimensions (in meters) of the total
> !  plotting area on the paper.
> !
>     character(*), optional, intent(in) :: pform, pid, psout, unit, prt
>     character                          :: string*16
>     integer,      optional, intent(in) :: rot90, plot
>     integer                            :: ib, ie, inoc
>     real,         optional, intent(in) :: psize(2)
>
> In my fortran wrapper I can not declare arguments as optional, because it
> is not allowed by iso_c_binding. I would like to avoid to copy the optional
> arguments handling code (default values and so on) from the oiginal fortran
> function to my cython wrapper. There is a nice solution? There is a way to
> pass a value to the fortran function so that the argument is treated as
> missing?
>
> Thank you. Cheers,
> --
> Daniele

This is not "standard compliant", but the compilers I'm familiar with
(gfortran and g95) check for the presence of optional arguments by
checking that the argument pointer is non-null. So if you want to
simulate a missing optional argument, pass NULL.

John

Ondrej Certik

unread,
Nov 20, 2010, 1:50:42 PM11/20/10
to fwrap...@googlegroups.com

Thanks John. Let me know if anyones figures out some "standard
compliant" solution for this too.

Ondrej

Daniele Nicolodi

unread,
Nov 22, 2010, 9:22:11 AM11/22/10
to fwrap...@googlegroups.com
On 20/11/10 19:50, Ondrej Certik wrote:

>>> In my fortran wrapper I can not declare arguments as optional, because it
>>> is not allowed by iso_c_binding. I would like to avoid to copy the optional
>>> arguments handling code (default values and so on) from the oiginal fortran
>>> function to my cython wrapper. There is a nice solution? There is a way to
>>> pass a value to the fortran function so that the argument is treated as
>>> missing?
>>

>> This is not "standard compliant", but the compilers I'm familiar with
>> (gfortran and g95) check for the presence of optional arguments by
>> checking that the argument pointer is non-null. So if you want to
>> simulate a missing optional argument, pass NULL.
>
> Thanks John. Let me know if anyones figures out some "standard
> compliant" solution for this too.

Hello. I took some time to tackle the problem. I came up with an hack
that seems to work, at least in my tests. I'm learning fortran on the
way to solve this problem, so I post my solution here for comments.

Given a fortran function::

subroutine func(a, b, c)
integer, intent(in), optional :: a
character(*), intent(in), optional :: b
character(*), intent(in), optional :: c
if (present(a)) then
print *, 'a=', a
end if
if (present(b)) then
print *, 'b=', b
end if
if (present(c)) then
print *, 'c=', c
end if
end subroutine func

the thing that would end up in the most natural calling convection,
would be to have a fortran wrapper of the kind::

subroutine cc_func(a, b, c) BIND(C)
integer(c_int), value, intent(in) :: a
type(c_ptr), value, intent(in) :: b
type(c_ptr), value, intent(in) :: c

call func(a, c_f_string(b), c_f_string(c))

end subroutine cc_func

where c_f_string() is a fortran function that translated a NULL
terminated C string into a fortran string. It is defined in the file I
attach. This way in Cython it is possible to do something like::

cdef extern void cc_func(int, char *, char *)

def test():
cc_func(42, "foo", "bar")

However in this way it is not possible to use fortran optional arguments
facilities. To do so we need to give up the comfort of passing arguments
by value at the Cython level, and exploit the fact that NULL C pointers
and NULL fortran pointers are seen as missing optional values at the
fortran level. We can thus define a wrapper function like::

subroutine c_func(a, b, c) BIND(C)
integer(c_int), intent(in) :: a
type(c_ptr), intent(in) :: b
type(c_ptr), intent(in) :: c

character(len=c_strlen_safe(b)), pointer :: bf
character(len=c_strlen_safe(c)), pointer :: cf

bf => c_f_string_or_null(b)
cf => c_f_string_or_null(c)

call func(a, bf, cf)

if (associated(bf)) deallocate(bf)
if (associated(cf)) deallocate(cf)

end subroutine c_fun

and use the wrapper like this in Cython:

cdef extern void c_func(int *, char **, char **)

def test():
cdef int a = 25
cdef char *b = "foo"
cdef char *c = "bar"
c_func(&a, &b, &c)
# omit optional int argument a
c_func(NULL, &b, &c)
# omit optional char argument b
b = NULL
c_func(&a, &b, &c)

It works! However it looks quite an hack to me. Does someone has a
better idea on how to implement it?

Cheers,
--
Daniele

wrapper.f90

Dag Sverre Seljebotn

unread,
Nov 22, 2010, 9:39:31 AM11/22/10
to fwrap...@googlegroups.com

Hmm. Is it standard that when a Fortran pointer is "nullify"ed, then
passing it as the optional argument to a subroutine causes it to not be
"present"? Or does that result in undocumented behaviour (as far as
standards goes)? If the latter, this seems to be subject to the same
weaknesses as what Ondrej posted. Of course, in practice, it does work.


Dag Sverre

Daniele Nicolodi

unread,
Nov 22, 2010, 9:53:44 AM11/22/10
to fwrap...@googlegroups.com
On 22/11/10 15:39, Dag Sverre Seljebotn wrote:
> Hmm. Is it standard that when a Fortran pointer is "nullify"ed, then
> passing it as the optional argument to a subroutine causes it to not be
> "present"? Or does that result in undocumented behaviour (as far as
> standards goes)? If the latter, this seems to be subject to the same
> weaknesses as what Ondrej posted. Of course, in practice, it does work.

It is part of F2008 standard. I learned it from gfortran source code:

http://gcc.gnu.org/viewcvs/trunk/gcc/fortran/trans-expr.c?revision=166686&view=markup

line 143.

However I use this only for character(*) arguments. For other kind of
arguments I still use what Ondrej posted. I think it would be possible
to extend what I do for strings to other data types too, but the
wrapping would become much more complex.

For my use case I think I'll try to avoid to use think hack. It feels
quite clunky compared to the easy solution.

Cheers,
--
Daniele

Ondrej Certik

unread,
Nov 22, 2010, 2:48:50 PM11/22/10
to fwrap...@googlegroups.com

What weakness does my approach have? I use the standard
iso_c_bindings, and I don't use optional arguments. Or is the weakness
in the fact, that I need to repeat the default values in the wrappers?

As to passing NULL, indeed, from:
http://gcc.gnu.org/viewcvs/trunk/gcc/fortran/trans-expr.c?revision=166686&view=markup


/* Fortran 2008 allows to pass null pointers and non-associated pointers
144 as actual argument to denote absent dummies. For array descriptors,
145 we thus also need to check the array descriptor. */
146 if (!sym->attr.pointer && !sym->attr.allocatable
147 && sym->as && sym->as->type == AS_ASSUMED_SHAPE
148 && (gfc_option.allow_std & GFC_STD_F2008) != 0)


So maybe it is (or becomes eventually) standard. Interesting.

Ondrej

Dag Sverre Seljebotn

unread,
Nov 22, 2010, 3:14:35 PM11/22/10
to fwrap...@googlegroups.com

For practical purposes it is not a weakness, because all compilers would
make the sane choice of having the NULL pointer. I'm just saying that I
haven't seen evidence that it is standard, so the compiler doesn't have to.

In some situations it can't, e.g., it seems I am allowed to make an
argument "integer, value, optional :: x". If it is passed by value you
obviously can't pass a NULL pointer.

> As to passing NULL, indeed, from:
> http://gcc.gnu.org/viewcvs/trunk/gcc/fortran/trans-expr.c?revision=166686&view=markup
>
>
> /* Fortran 2008 allows to pass null pointers and non-associated pointers
> 144 as actual argument to denote absent dummies. For array descriptors,
> 145 we thus also need to check the array descriptor. */

> 146 if (!sym->attr.pointer&& !sym->attr.allocatable
> 147 && sym->as&& sym->as->type == AS_ASSUMED_SHAPE
> 148 && (gfc_option.allow_std& GFC_STD_F2008) != 0)


>
>
> So maybe it is (or becomes eventually) standard. Interesting.
>

Well, this only seem to cover Daniele's approach. I haven't seen
anywhere that this covers C interoperability. (Not that I looked).

I.e., if the Fortran compiler choose to represent null pointers by
0xFFFFFF internally, it doesn't invalidate the standard. All pointers go
through conversion functions back and forth anyway. For Fwrap, this
should be possible to work around by a few lines of Fortran that
probably compiles to nothing. For manually writtenm code one may not bother.

Dag Sverre

Reply all
Reply to author
Forward
0 new messages