Closed dev-zero closed 3 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.
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:
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')}$
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.
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?
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:
I agree with @oschuett that it definitely goes beyond the scope of a usual preprocessor and breaks the simplicity of just transforming between Fypp-enriched Fortran and normal Fortran. It would allow the users to create "side-effects", which could be a pain to debug if something goes wrong.
You could easily use special comments and parse them with a very simple algorithm to achieve exactly the same goal,
!LINKER:PUBLIC:myfunc
subroutine myfunc() bind(c)
...
so there is not too much gain to do it with Fypp.
Finally, there is the problem of parallel compilation: As far as I can see, it would be quite complicated to make sure, you collect all registered modules properly, if the files are compiled in parallel (which they usually should). So, enabling to call an external script via Fypp would encourage a practice, which is very fragile and error-prone.
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.
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.
@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.
Is there any interest in discussing this PR further? Otherwise, I'd close it.
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.