aradi / fypp

Python powered Fortran preprocessor
http://fypp.readthedocs.io
BSD 2-Clause "Simplified" License
188 stars 30 forks source link

RFC: emitter directive #3

Closed dev-zero closed 3 years ago

dev-zero commented 6 years ago

This directive can be used to generate additional output from Fortran using Fypp. Not sure about allowing arbitrary shell commands, therefore as a separate commit for now. Due to the usage of unicode literals u'' for Python2 and 3 compatibility, this breaks Python 3.2, which is imho ok by now.

aradi commented 6 years ago

Could you describe a real-life scenario, where such a feature would be desirable? I sort of fail to understand, why such an extension would be useful.

Breaking 3.0-3.2 compatibility is a severe problem, but as far as I could see, we could fix that with an easy workaround.

dev-zero commented 6 years ago

This can be used together with a build system and a simple executable which collects the output to implement a couple of things not directly supported by Fortran:

  1. Generating a linker version script. Since gfortran does not support the visibility hidden directive, the only way to prevent symbols from being exported when creating a shared library is to version the interface explicitly using linker version script. Maintaining this information separately is error prone, using this function one could write a Fypp definition to export the public functions:

    SUBROUTINE my_func bind(C) ${make_public('my_func')}$

  2. Compile-time registry. In C++ you can have a singleton object acting as a registry for build-time optional modules/plugins: if the module is built, it registers itself with the registry by calling the registry function in global space, which will be executed before main() is entered. Fortran does not allow this (for good reason). Using such an emitter directive, one could generate another Fortran file/function to initialize the optional modules.

I haven't seen Python versions 3.0-3.2 out in the open for a long time and their last service releases are 4+ years ago. Plus, users who have such an old Python version could always use an older version of Fypp.

oschuett commented 6 years ago

I just wanna add my two cents, because I have a pretty bad feeling about this change.

First, it breaks the simple 1:1 translation of .fypp into .F files. After all, Fypp is supposed to be a preprocessor. Furthermore, it will lead to a wild mixture of Fortran/Fypp/C++ code. That can't be good.

It seems all the information you need is actually already contained in the Fortran code, right? So, maybe the better solution would be to parse the Fortran code. If you find parsing Fortran too cumbersome then you could also leave some magic comments, ie. pragams, behind.

Maybe also the novel gfortran -fc-prototypes could be helpful?

aradi commented 6 years ago

Thanks a lot, now I see the uses cases you had in mind. Especially, the example with the compile-time registry I find very interesting. I was just recently struggling with the lack of the according Fortran functionality, as it would be very handy for the new Fypp-based unit-test system I am working on currently.

I would like to contemplate about this a little bit more, but I have at the moment three major concerns:

And as for Python compatibility: I already had once in the beginnings to downgrade Fypp (to Python 2.6) in order to make some new users happy :wink: I really do not want to break compatibility to any Python releases newer than 10 years (3.2 is only 7 years old...) without very-very good reasons.

dev-zero commented 6 years ago

The advantage of having it as a pre-processor macro (whether for cpp or fypp) is that should gfortran (or any other compiler) at some point support attributes like

SUBROUTINE my_func bind(C) __attribute__ ((visibility ("default")))

I can simply change what Fypp emits at that point and directly use the appropriate compiler support, see also https://gcc.gnu.org/wiki/Visibility for different compiler support.

As for the parallel build: a simple lock by the called executable should be sufficient (wrapping the command with flock being the easiest one).

@oschuett please read my text carefully: I only used "static code execution" in C/C++ as an example when implementing a pattern, nothing about adding C++ to whatever. And yes, I would like to avoid parsing Fortran (even though there is a maintained pure-python parser available) or inventing yet another markup.

oschuett commented 6 years ago

@dev-zero, while I see that your use-case is reasonable, we have to consider how people can abuse such a feature, because they will. And side effects are a very well known can of worms.

For the registry, I would also suggest to use a separate script which searches for plugins and then generates the initialization code. This would keep the logic in a single place and you avoid all the issues with parallel or partial builds.

aradi commented 3 years ago

Is there any interest in discussing this PR further? Otherwise, I'd close it.