LLNL / shroud

Shroud: generate Fortran and Python wrappers for C and C++ libraries
BSD 3-Clause "New" or "Revised" License
90 stars 7 forks source link

Add interface wrapper for functions with default arguments defined as class members #315

Closed CarvFS closed 1 year ago

CarvFS commented 1 year ago

Hello!

I have noticed one thing while defining member functions of a class containing default arguments.

If the Shroud script is just for the function, e.g.:

library: Tutorial
format:
  F_filename_suffix: F90
cxx_header: tutorial.hpp

declarations:
- decl: int Method1(int arg1)
- decl: int Method2(int arg1, int arg2 = 1, double arg3 = 0.5)

The Fortran wraper will have an interface wraper defined as:

    interface method2
        module procedure method2_0
        module procedure method2_1
        module procedure method2_2
    end interface method2

Thus, one may use the function as, for example, value = method2(arg1,optional_args).

However, if the functions is defined as class member, e.g.:

library: Tutorial
format:
  F_filename_suffix: F90
cxx_header: tutorial.hpp

- decl: namespace tutorial
  declarations:
  - decl: class Class1
    declarations:
    - decl: Class1(int arg1, int arg2 = 1, double arg3 = 0.5) +name(new)
    - decl: ~Class1() +name(delete)
    - decl: int Method1(int arg1)
    - decl: int Method2(int arg1, int arg2 = 1, double arg3 = 0.5)

The Fortran wrapper contains only the interface for the constructor:

    interface class1
        module procedure class1_new_0
        module procedure class1_new_1
        module procedure class1_new_2
    end interface class1

And the functions are defined as procedures inside the defined type:

        procedure :: method1 => class1_method1
        procedure :: method2_0 => class1_method2_0
        procedure :: method2_1 => class1_method2_1
        procedure :: method2_2 => class1_method2_2
        [...]
        generic :: method1 => method1_0, method1_1, method1_2
        generic :: method2 => method2_0, method2_1, method2_2

Thus one may use the functions in the main Fortran program as type-bound functions:

cptr = Class1(1, 2, 0.1) 

value1 = cptr%method1(1)
value2 = cptr%method2(1, optional_args)

In this case, method1 can also be used as class1_method1(cptr, 1). However this is not possible for method2. I have managed to solve this by manually adding the interface for method2:

    interface class1_method2
        module procedure method2_0
        module procedure method2_1
        module procedure method2_2
    end interface class1_method2

However, if one needs to run shroud again it will be needed to keep inserting the interface into the fortran wrapper. It seems there is no splicer to keep this additions in the splicer.f file.

Is there some shroud input I am missing for this case?

Thank you very much! Felipe Silva Carvalho

ltaylor16 commented 1 year ago

Thank you for reporting this. I've committed changes to the develop branch which will create a generic interface block for class methods. This was an oversight on my part. I was thinking that only the type-bound procedures would need the generic clause.

Some splicer blocks with the name additional_interfaces are available to manually add any additional interfaces needed. The spicer is added at the global level as well as each namespace and class scope.

CarvFS commented 1 year ago

Thank you very much for your reply! I will take a look on the modifications you have made :)

About the _additionalinterface block:

In my .F90 wrapper I have it placed as below

        type Class
            [class procedures]
        end type Class

        interface operator (.eq.)
            module procedure rism3d_eq
        end interface

        interface operator (.ne.)
            module procedure rism3d_ne
        end interface

        Interface
        [previous functions/subroutines]

        ! splicer begin namespace.tutorial.additional_interfaces
        ! splicer end namespace.tutorial.additional_interfaces
    end interface

    interface class_constructor
        module procedure Class_ctor_0
        module procedure Class_ctor_1
        module procedure Class_ctor_2
    end interface class_constructor

contains

I do not know if I got it correctly, but I was expecting this splicer block after the end interface line.

ltaylor16 commented 1 year ago

I replaced the additional_interfaces splicer with an additional_declarations splicer which is at the top level, outside of any other interface block. This will allow other interface blocks, generic interface, or any other declarations to be added.

    interface class1
        module procedure class1_new
    end interface class1

    ! splicer begin namespace.tutorial.additional_declarations
    ! splicer end namespace.tutorial.additional_declarations

contains

This change is now in the develop branch.

CarvFS commented 1 year ago

Thank you very much!!!