stdlib
stdlib copied to clipboard
optval and module dependencies
So there have been a few discussions about optval before:
- https://github.com/fortran-lang/stdlib/issues/524
- https://github.com/fortran-lang/stdlib/issues/448
The former thread established the performance implications of sprinkling optval
here and there are likely negligible. The latter thread established that merge
cannot be used as a replacement due to it's behavior interacting with the present
function, and also being unsuitable for variables of type character(:), allocatable
. However, I think one comment from @zoziha is worthy of more attention:
Because merge is a standard function, we can reduce optval module dependencies by using it(merge). It is convenient to replace them with the conditional expression syntax (F202X) in the future.
In my opinion the stdlib modules should be as independent as reasonably possible. This enables users to simply copy the sources of the modules they want to use, without having to climb up the module dependency tree. Now I realize this is a non-issue for those using stdlib via fpm, but some users might prefer to preserve their current build methods.
For source files that require the preprocessor anyways, it would be straightforward to provide a preprocessor version of optval instead:
#:def optval(lhs,opt,default)
${lhs}$ = ${default}$
if (present(${opt}$)) ${lhs}$ = ${opt}$
#:enddef
An alternative variation is
#:def optval(lhs,opt,default)
if (present(${opt}$)) then
${lhs}$ = ${opt}$
else
${lhs}$ = ${default}$
end if
#:enddef
however this one can result in some false positive warning about non-initialized arguments.
[Edit: hopefully the macros are correct now...]
I like the idea of using the pre-processor for internal use of optval
#:def optval(lhs,opt,default)
lhs = default
if (present(opt)) lhs = opt
#enddef
Would this not require the variables to be called exactly lhs
, opt
, and default
? Or does fypp know that this is a function call and to substitute variables?
Now I realize this is a non-issue for those using stdlib via fpm,
Eventually fpm should tree-shake, i.e. build only the modules used by the project and not the whole stdlib, so this may in the future have an effect on fpm build as well.
good point @ivan-pi
I use a similar approach in my own code with fypp
:
#:def myfunction(var2,var4,var3)
(var1(${var2}$)#{if var3 != '0' }#+${var3}$#{endif}#)*var5#{if var4 != '0' }#+${var4}$#{endif}#
#:enddef
where var2..4
can be names of variables or integer
(e.g.,0)
It is called in the code as:
result = $(myfunction( myvar1, myvar2, 0)}$
this would be available for all files if stored in common.fypp
of stdlib
Forgive me, I only glanced quickly through the fypp docs. I think the following macro is correct:
#:def optval(lhs,opt,default)
${lhs}$ = ${default}$
if (present(${opt}$)) ${lhs}$ = ${opt}$
#:enddef
and can be called as follows:
subroutine demo(left)
integer, intent(in), optional :: left
integer :: l
@:optval(l, left, 1)
end subroutine
producing the output
$ fypp test_optval.fypp
subroutine demo(left)
integer, intent(in), optional :: left
integer :: l
l = 1
if (present(left)) l = left
end subroutine
The downside is the assignment is hidden in the macro which makes it a bit cryptic.
I see. And it can't be used in expressions, correct?
I see. And it can't be used in expressions, correct?
Not in the form proposed by @ivan-pi because the macro will be replaced by the two lines before compilation. However, I cannot think about a way to do that, without conditional expressions or intrinsics.
Correct, an expression implies having a function or using the F202X syntax:
l = present(opt) ? opt : default
This can in principle be wrapped in a macro:
#:def optval(opt,default)
present(${opt}$)) ? ${opt}$ : ${default}$
#:enddef
which can be used as an expression using a similar syntax to the one Jeremie showed:
l = @{optval(left, 1)}@
but IMO it doesn't feel worth the effort.
I'm not sure if fypp macros can be overloaded, however you could pull both versions off in the same macro by using either the positional or keyword arguments that fypp supports.
On the other side, are we not proposing a shortcut for a function (optval
) we find useful but that we don't want to use because the compilers is not able to inline it properly by default? This might give a bad image of optval
to the stdlib
users (and to some extends, also to the committee)
Personally I'm not worried too much about the performance penalty. As long as not used in a performance-critical context I think it's handy. On the other hand with the new F202X conditional syntax I think optval
becomes superfluous.
The reason I wanted to suggest a preprocessor alternative was merely as a means of reducing inter-dependencies between modules, allowing users to "pick-n-play". You could argue that downloading three modules (kinds, optval, selection) instead of two (kinds, selection) is not a big difference.