I’m writing a program to compute the halftone function, and I need to write a struct to a file because it takes a long time to compute. It consists of an OffsetVector of any real floating-point type. I’ve figured out how to turn any number between 0 and 1, whether Float64 or BigFloat, into a bytestring which I can write to a file, but I need to write the type to the start of the file, so that I can construct the HalftoneApprox when reading it from the file. I could write some elseifs checking whether it’s a Float16, Float32, Float64, or BigFloat, but if someone ever creates the types Float80 (which exists in x86(64)) and Float128 (which exists in POWER), the code will fail. How can I write a type to a file so that I can read it from the file and construct the same type, at least if the architecture has it?
Serialization would help?
You could also print the type and values to a file and then read the contwnts back to a string and parse it to recover the type (and values) with Meta.parse .
If you only want to support types in Base, the lowtech solution is to simply write the type name in the file and reconstruct it with getfield(Base, Symbol(type_name)).
Otherwise you need to look into packages like JLD2 or BSON. The Serialization stdlib can be used but it has a number of caveats when it comes to format stability.
Be aware that most solutions, except the first one, have the potential to execute arbitrary code when parsing the file, which may or may not be a real problem.
To nitpick this needs to be combined with eval, and then also has the potential to execute arbitrary code.
I’ve done this with JLD2.jl and it works pretty well.
I looked at the source code of Serialization; it has an array of types and stuff. I don’t want to use Serialization because I’m writing just one parametrized type to a file, not an arbitrary data structure. I don’t want to run the risk of someone passing a file containing a data structure that could harm the program.
How do I get the type name as a String?
Serialization sounds like the right solution for you.
I don’t understand why this is a blocking issue.
string gets how a symbol or type is printed, which is reliable but not a guarantee of the necessary information to parse into a type. nameof is the API for accessing the internal name::Symbol field of types, but that won’t include the parameters and can look quite different from how it’s printed e.g. nameof(typeof([1])) == :Array vs. string(typeof([1])) == "Vector{Int64}".
I think commenters are making some assumptions about your package that may not be true. It could help to elaborate how you plan this file to be used e.g. when it’s generated, if different users are expected to trade files to save each other time, API on loading files, exact security concerns. The description so far suggests caching results, which may not need file management at all.
I’ve succeeded in using string to get the name of a type, then turning it back into a type with getfield. It’s a bit more complicated if the type is BFloat16, since that’s in Core, not Base, but I can figure it out.
The files may be passed from one user to another. All programs that take input from an untrusted source (here, another user) must run security checks on the input, unless what they do is completely general (such as encrypt or try to compress the data). I’d also like the file to be small; Serialization being general, could make the file bigger than it needs to be. My marshal function turns a Float64 into usually nine bytes; the extra byte is there in case someone makes a HalftoneApprox with a BigFloat with more than 256 bytes of precision. I’ll omit the 0th and last numbers in the OffsetVector because they’re always 0 and 1 respectively (and marshal crashes if fed a 1 or anything bigger).
I’ve posted the link to the repo on MAA, hoping someone can find a closed-form expression, but that may take years.