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
?
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?
Taboo? They are quite easy to misuse but for this case it seems like a perfect tool.