Imagine I have a
struct A
a::Int
b::String
end
I want to stringify it like this:
string(A.a, A.b)
However, I want to do this WITHOUT prior knowledge of the fieldnames of A. This is quite difficult to do in a type-stable way. It would be much easier if for any type one could generate a tuple Val{:a}(), Val{:b}()
where a and b are fieldnames. Then the fieldnames would be known to the compiler. Through the magic of tuples, it would in fact be quite easy to do type stably:
inner_type(::Val{T}) where T = T
function stringify(object)
value_fields = get_value_fields(typeof(object))
string(map(value_fields) do value_field
getindex(object, inner_type(value_field))
end...)
end
Is this possible somehow?
julia> @generated f(obj) = :(string($((:(getfield(obj, $i)) for i in 1:fieldcount(obj))...)))
f (generic function with 1 method)
julia> f(1im)
"01"
julia> f((1, 2, 2.4))
"122.4"
julia> @code_warntype f(1im)
Variables:
#self# <optimized out>
obj::Complex{Int64}
Body:
begin
#= REPL[4]:1 =#
# meta: location REPL[4] @generated body
return $(Expr(:invoke, MethodInstance for #print_to_string#245(::Void, ::Function, ::Int64, ::Vararg{Int64,N} where N), :(Base.#print_to_string#245), :($(QuoteNode(nothing))), :($(QuoteNode(Base.print_to_string))), :((Main.getfield)(obj, 1)::Int64), :((Main.getfield)(obj, 2)::Int64)))::String
# meta: pop location
end::String
julia> @code_warntype f((1, 2, "", 3))
Variables:
#self# <optimized out>
obj::Tuple{Int64,Int64,String,Int64}
Body:
begin
#= REPL[4]:1 =#
# meta: location REPL[4] @generated body
return $(Expr(:invoke, MethodInstance for #print_to_string#245(::Void, ::Function, ::Int64, ::Vararg{Any,N} where N), :(Base.#print_to_string#245), :($(QuoteNode(nothing))), :($(QuoteNode(Base.print_to_string))), :((Main.getfield)(obj, 1)::Int64), :((Main.getfield)(obj, 2)::Int64), :((Main.getfield)(obj, 3)::String), :((Main.getfield)(obj, 4)::Int64)))::String
# meta: pop location
end::String
?
4 Likes
julia> @generated function stringify(x::T) where {T}
return :(string($([:(getfield(x, $name)) for name in fieldnames(T)]...)))
end
stringify (generic function with 1 method)
julia> struct Foo
a
b
end
julia> stringify(Foo("hello ", "bramtayl"))
"hello bramtayl"
EDIT: @yuyichao was quick on the draw and beat me to it
Isn’t there some sort of taboo on generated functions?
1 Like
Taboo? They are quite easy to misuse but for this case it seems like a perfect tool.