shroud icon indicating copy to clipboard operation
shroud copied to clipboard

cpp_if not taken in account for associated method

Open clementFoyer opened this issue 5 years ago • 4 comments

When trying to generate wrapper for the following C++ code, I end up with an error at link time when linking against the Fortran module.

C++ is like:

class Foo {
  public:
    void baz() {};
    void bar() {};
};

I do not want to expose the symbol bar if ENABLE_BAR is not defined. So the YAML is such as:

declarations:
  - decl: class Foo
    declarations:
      - decl: void baz()
      - decl: void bar()
        cpp_if: if defined(ENABLE_FOOBAR)

The result is such as:

    type foo
        type(SHROUD_foo_capsule) :: cxxmem
        ! splicer begin class.Foo.component_part
        ! splicer end class.Foo.component_part
    contains
        procedure :: baz => foo_baz
        procedure :: bar => foo_bar
        procedure :: get_instance => foo_get_instance
        procedure :: set_instance => foo_set_instance
        procedure :: associated => foo_associated
        ! splicer begin class.Foo.type_bound_procedure_part
        ! splicer end class.Foo.type_bound_procedure_part
    end type foo

    [... removing operators ...]

    interface

        subroutine c_foo_baz(self) &
                bind(C, name="TES_foo_baz")
            import :: SHROUD_foo_capsule
            implicit none
            type(SHROUD_foo_capsule), intent(IN) :: self
        end subroutine c_foo_baz

#ifdef ENABLE_BAR
        subroutine c_foo_bar(self) &
                bind(C, name="TES_foo_bar")
            import :: SHROUD_foo_capsule
            implicit none
            type(SHROUD_foo_capsule), intent(IN) :: self
        end subroutine c_foo_bar
#endif

        ! splicer begin class.Foo.additional_interfaces
        ! splicer end class.Foo.additional_interfaces
    end interface

contains

    subroutine foo_baz(obj)
        class(foo) :: obj
        ! splicer begin class.Foo.method.baz
        call c_foo_baz(obj%cxxmem)
        ! splicer end class.Foo.method.baz
    end subroutine foo_baz

#ifdef ENABLE_BAR
    subroutine foo_bar(obj)
        class(foo) :: obj
        ! splicer begin class.Foo.method.bar
        call c_foo_bar(obj%cxxmem)
        ! splicer end class.Foo.method.bar
    end subroutine foo_bar
#endif

    [... Removing useless function implementation ...]

end module foo_mod

In the case where ENABLE_BAR is not defined, the module will not be able to expose the foo%bar as foo_bar subroutine will not be defined, nor the c_foo_bar interface.

If I define the following FORTRAN program:

program fortran_test
    use foo_mod
    implicit none

    type(foo) :: f

    call f%baz
#ifdef ENABLE_BAR
    call f%bar
#endif
end program fortran_test

and try to link it to the module, I have the following error risen:

wrapfFoo.F:30:17:

         procedure :: bar => foo_bar
                 1
Error: 'foo_bar' must be a module procedure or an external procedure with an explicit interface at (1)

One solution would be to add a check to node.cpp_if when defining the procedure in the type definition in the wrapfFoo.f file generated, in order to add the preprocessor directives.

clementFoyer avatar May 29 '19 14:05 clementFoyer

Thank you for the detailed bug report. I created a branch, cpp-fortran, which adds a test and uses cpp_if for type-bound procedures. Let me know if the code will compile and load now.

ltaylor16 avatar May 29 '19 18:05 ltaylor16

Works like a charm, thanks.

Some other /error/ (or feature?) is that considering the following addition to the YAML file:

declarations:
  - decl: class B
    cxx_header: Bar.hpp
    cpp_if: ifdef USE_B

in typeslib.h, the wrapping structure will always be defined (potentially it uses symbols for nothing).

It seems to be some issues with how cxx_header is taken in account as well. When I tried the following YAML:

library: testlib
declarations:
  - decl: namespace testing
    declarations:
    - decl: void func1()
      cxx_header: <stdio.h>

The cxx_header is never taken in account and the requested headers don't appear in the wrapper header file. Neither it is when declaring within the namespace node. In order to be included, it has to be declared on the global level, or in a class node. It may be an issue as for the original example, where I would like to have the declaration of a function depending on cpp_if, and if this function is to be wrapped, include the required header files. The same issue applies to the templates as well. If the #include <stdio.h> was to be potentially guarded by a cpp_if clause, that would be perfect :)

Thank you very much.

clementFoyer avatar May 30 '19 13:05 clementFoyer

Upon further research/reading, the last point seems to be overturned by using splicer_code.c.class.{classname}.CXX_definitions. However, an automatisation of this process would be nice, although left as a request for improvment.

EDIT: change declarations for definitions as the effect should be in the .cpp file, and doesn't need to be propagated/shown outside it.

clementFoyer avatar May 30 '19 14:05 clementFoyer

I added the use of cpp_if in the typeslib.h file. I think by conditionally compiling it, it makes it noticeable if it gets accidentally used.

I had not considered the idea of using cxx_header in a function declaration. You're correct that it's only used with global and class. I'll take a look at what would be involved to use it at any level. Like you said, it would be useful combined with cpp_if.

ltaylor16 avatar May 31 '19 08:05 ltaylor16