Reputation: 341
For an older Fortran project, I tried to create a modern logger. It was written object-oriented very similar to python's logging library.
The last thing that bothers me is formating, I will explain.
My logger looks very similar to this library, however, it did not meet my requirements so I wrote my own. Basically my logging library looks something like this:
lg = Logger("super logger")
... other settings ...
lg%log("DEBUG", "A cool message to log", rank)
but with this logger, I lost all of the formatting capabilities of the write statement. Basically in the code string formatting is done by write statements such as:
write (stdout, '(7X,A,f12.6)') "The great number is ", number
So now I have to have two lines of code for every output statement and auxiliary variable:
write (message, '(A,f12.6)') "The great number is ", number
lg%log("DEBUG", message, rank)
which is not very convenient nor nice.
I was trying to do a formatting function, but I guess it is not possible with a variable number of arguments (like in C) e.g:
function fm(fmt, ...) result(message)
I believe I cannot do a C workaround either because when I specify the binding interface I have to specify arguments.
The only solution I have now (except the two-line example) is to overload some method for different variables and create something like streams in c++.
my question is do you have a better idea? Ideally, if you know about a option how to format string in one line.
Upvotes: 2
Views: 893
Reputation: 7395
I also use a similar approach (with veryeverie) in my local library, i.e. define X_to_str()
for X = integer, real, etc, overload them to to_str()
, and use //
if necessary. In my case, format
is made an optional argument with the default value given in X_to_str()
, so that I can just write myfunc( "hello" // to_str(777) )
etc (e.g. with i0
for the default).
Another approach might be to overload //
to concatenate string + non-string arguments, like the following (though I've never used this in my codes up to now...)
module str_mod
implicit none
interface operator(//)
module procedure cat_int, cat_real
end interface
character(20) :: fmt_int = "(i0)"
character(20) :: fmt_real = "(g0.6)"
contains
function cat_int( x, y ) result(res)
character(*), intent(in) :: x
integer, intent(in) :: y
character(:), allocatable :: res
character(20) sy
write( sy, fmt_int ) y
res = trim( x ) // " " // trim(adjustL( sy ))
end
function cat_real( x, y ) result(res)
character(*), intent(in) :: x
real, intent(in) :: y
character(:), allocatable :: res
character(20) sy
write( sy, fmt_real ) y
res = trim( x ) // " " // trim(adjustL( sy ))
end
end module
program main
use str_mod
implicit none
print *, "hello" // 1.23 // " world" // 777
end
Upvotes: 1
Reputation: 2981
You can wrap the conversion to string in a function, like
module io
interface str
module procedure str_real
end interface
contains
function str_real(input,format) result(output)
real(dp), intent(in) :: input
character(*), intent(in) :: format
character(:), allocatable :: output
! It's probably possible to do the allocation and re-allocation more cleanly.
allocate(output(100))
write(output, format) input
output = trim(output)
end function
end module
which you can then use with your logger as
lg%log("DEBUG", "The great number is "//str(number,'(f12.6)'), rank)
You'll need separate str
functions for each type you want to do this with.
Upvotes: 2