Is this @sprintf (annoying) behaviour intended?

The crux is that I want to build a string programmatically with a variable number of elements and can’t find a way to do it with @sprintf

julia> bnds = [1,2,3];
julia> @sprintf("%d,%d,%d,",bnds...)
"1,2,3,"

julia> repeat("%d,",3)
"%d,%d,%d,"

julia> @sprintf(repeat("%d,",3),bnds...)
ERROR: LoadError: MethodError: no method matching Printf.Format(::Expr)
Closest candidates are:
  Printf.Format(::S, ::Vector{UnitRange{Int64}}, ::T) where {S, T} at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.7\Printf\src\Printf.jl:65
  Printf.Format(::AbstractString) at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.7\Printf\src\Printf.jl:81
Stacktrace:
 [1] var"@sprintf"(__source__::LineNumberNode, __module__::Module, fmt::Any, args::Vararg{Any})
   @ Printf C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.7\Printf\src\Printf.jl:895
in expression starting at REPL[29]:1

Yes. Because @sprintf is a macro, it can only act on the string literal it’s given. It can’t generate code for repeat(...) since the stuff repeat does is handled at run-time, not parse-time. You can use a for loop to do something similar, right? or map and join?

1 Like

But if I compute

fmt = repeat("%d,",3)
"%d,%d,%d,"

and pass the fmt, which is a string, the error is the same.

Yes, I can create my final sting some other way but it looks clumsy.

This thread discusses many alternatives for that: How to repeat the format in @printf? - #2 by heliosdrm

Thanks.

This problem is more general than this thread suggests. It is not limited to repeated format items. Surely this is a bug:

julia> using Printf

julia> @sprintf(“aaa %d bbb”, 3)
“aaa 3 bbb”

julia> fmt = “aaa %d bbb”
“aaa %d bbb”

julia> @sprintf(fmt, 3)
ERROR: LoadError: MethodError: no method matching Printf.Format(::Symbol)
Closest candidates are:
Printf.Format(::S, ::Vector{UnitRange{Int64}}, ::T) where {S, T} at /Applications/Julia-1.7.app/Contents/Resources/julia/share/julia/stdlib/v1.7/Printf/src/Printf.jl:65
Printf.Format(::AbstractString) at /Applications/Julia-1.7.app/Contents/Resources/julia/share/julia/stdlib/v1.7/Printf/src/Printf.jl:81
Stacktrace:
[1] var"@sprintf"(source::LineNumberNode, module::Module, fmt::Any, args::Vararg{Any})
@ Printf /Applications/Julia-1.7.app/Contents/Resources/julia/share/julia/stdlib/v1.7/Printf/src/Printf.jl:900
in expression starting at REPL[18]:1

No, that’s how macros work: they replace expressions into other expressions. The @sprintf macro expects as first argument a literal string, not a variable whose runtime value is a string: macros can’t possibly access runtime values of variables (unless you use tricks like @eval @sprintf($fmt, 3) which gets the job done but defeats the purpose of using a macro in the first place).

5 Likes

what you want:

julia> fmt = "aaa %d bbb"
"aaa %d bbb"

julia> Printf.format(Printf.Format(fmt), 3)
"aaa 3 bbb"
3 Likes

Thank you.

I’m surprised no one has put that into a function called “sprintf”. It would be more useful than the macro.

2 Likes