Skip to content

Commit 04026f6

Browse files
Merge pull request #46 from tshort/static_type
Add utilities to convert object models
2 parents ef7a5c5 + be8ec9d commit 04026f6

6 files changed

Lines changed: 143 additions & 1 deletion

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "StaticTools"
22
uuid = "86c06d3c-3f03-46de-9781-57580aa96d0a"
33
authors = ["C. Brenhin Keller and contributors"]
4-
version = "0.8.7"
4+
version = "0.8.8"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ Fortunately, going to all this trouble does have some side benefits besides comp
4848
* No GC means no GC pauses
4949
* Since we're only including what we need, binaries can be quite small (e.g. 8.4K for Hello World)
5050

51+
### Utilities
52+
53+
The utilities `static_type` and `static_type_contents` are utilities to help convert an object to something similar with fields and type parameters that are amenable to static compilation.
54+
55+
`static_type` is mainly useful for converting objects that are heavily paramaterized. The SciML infrastructure has a lot of this. The main objects like a `DiffEq.Integrator` has many type parameters, and by default, some are not amenable to static compilation. `static_type` can be used to convert them to forms that can help numerical code to be statically compiled.
56+
57+
For the default rules, `Array`s are converted to `MallocArray`s, and `String`s are converted to `MallocString`s. The default rules can be extended or redefined by using multiple dispatch and a context variable. Note however that these `MallocArray`s and `MallocString`s must be `free`d when you are done with them.
58+
5159
## Examples
5260

5361
### Compiled command-line executables

src/StaticTools.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ module StaticTools
3939
include("staticrng.jl")
4040
include("ziggurat.jl")
4141

42+
# Utilities
43+
include("static_type.jl") # Convert types and objects to more static-friendly versions
44+
4245
# Types
4346
export StaticString, MallocString, StringView, AbstractStaticString # String types
4447
export MallocArray, MallocMatrix, MallocVector # Heap-allocated array types
4548
export StackArray, StackMatrix, StackVector # Stack-allocated array types
4649
export ArrayView
4750
export SplitMix64, Xoshiro256✴︎✴︎, BoxMuller, MarsagliaPolar, Ziggurat # RNG types
51+
export StaticContext, DefaultStaticContext # Context for `static_type`
4852

4953
# Macros
5054
export @c_str, @m_str, @mm_str
@@ -62,4 +66,5 @@ module StaticTools
6266
export unsafe_mallocstring, strlen # String management
6367
export printf, printdlm, parsedlm, argparse # File parsing and formatting
6468
export static_rng, splitmix64, xoshiro256✴︎✴︎, rand!, randn! # RNG functions
69+
export static_type, static_type_contents # Utilities
6570
end

src/static_type.jl

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
abstract type StaticContext end
2+
abstract type DefaultStaticContext <: StaticContext end
3+
4+
struct DefaultCtx <: DefaultStaticContext end
5+
6+
"""
7+
```julia
8+
static_type(ctx::StaticContext, x)
9+
static_type(x)
10+
```
11+
12+
Returns an object similar to `x` with contents converted based on rules
13+
specified by `ctx`. `static_type` can be used for types or for objects.
14+
15+
For the default case, this converts `Array`s to `MallocArray`s and
16+
`String`s to `MallocString`s.
17+
18+
To define your own rules, create a new `StaticContext` and then define
19+
two versions of `static_type` for each type you would like to convert.
20+
One converts the value, and one converts the type. Here is the builtin
21+
example for converting Arrays:
22+
23+
```
24+
struct MyCtx <: StaticContext end
25+
static_type(ctx::MyCtx, x::Array) = MallocArray(x)
26+
static_type(ctx::MyCtx, ::Type{Array{T,N}}) where {T,N} = MallocArray{T,N}
27+
```
28+
For this context struct, inherit from `StaticTools.DefaultStaticContext`
29+
to build on the defaults, or inherit from `StaticTools.StaticContext`
30+
to define rules from scratch.
31+
32+
`static_type` is mainly useful for converting objects that are heavily
33+
paramaterized. The SciML infrastructure has a lot of this. The main
34+
objects like a `DiffEq.Integrator` has many type parameters, and by
35+
default, some are not amenable to static compilation. `static_type`
36+
can be used to convert them to forms that can help numerical code to
37+
be statically compiled.
38+
39+
`static_type` cannot convert all objects automatically. It transforms
40+
all type parameters and the contents of each field in an object
41+
(recursively). But, some objects do not define a "fully specified"
42+
constructor. In some cases, another method, `static_type_contents`
43+
can help by returning the components to help for a manual invocation
44+
of the constructor.
45+
46+
Note that any `Malloc`-objects created through this function must still be
47+
`free`d manually if you do not wish to leak memory.
48+
"""
49+
static_type(x) = static_type(DefaultCtx(), x)
50+
static_type(ctx::DefaultStaticContext, x::Array) = MallocArray(x)
51+
static_type(ctx::DefaultStaticContext, ::Type{Array{T,N}}) where {T,N} = MallocArray{T,N}
52+
static_type(ctx::DefaultStaticContext, x::Vector{Vector{T}}) where {T} = MallocArray(MallocArray.(x))
53+
static_type(ctx::DefaultStaticContext, ::Type{Vector{Vector{T}}}) where {T} = MallocVector{MallocVector{T}}
54+
static_type(ctx::DefaultStaticContext, x::Tuple) = tuple((static_type(ctx, y) for y in x)...)
55+
static_type(ctx::DefaultStaticContext, x::String) = MallocString(x)
56+
static_type(ctx::DefaultStaticContext, ::Type{String}) = MallocString
57+
58+
# version for types including parameters
59+
function static_type(ctx::StaticContext, ::Type{T}) where {T}
60+
(!isconcretetype(T) || length(T.parameters) == 0) && return T
61+
return T.name.wrapper{(static_type(ctx, p) for p in T.parameters)...}
62+
end
63+
64+
function static_type(ctx::StaticContext, x::T) where T
65+
length(fieldnames(T)) == 0 && return x
66+
newtypes, newfields = static_type_contents(ctx, x)
67+
if length(newtypes) > 0
68+
return T.name.wrapper{newtypes...}(newfields...)
69+
else
70+
return T.name.wrapper(newfields...)
71+
end
72+
end
73+
74+
"""
75+
```julia
76+
static_type_contents(ctx::StaticContext, x)
77+
static_type_contents(x)
78+
```
79+
80+
Returns a tuple with:
81+
82+
* a vector of type parameters for `x` transformed by `static_type`
83+
* a vector of the contents of the fields in `x` transformed by
84+
`static_type`
85+
86+
Results can be useful for defining objects that do not define a
87+
fully specified constructor.
88+
"""
89+
static_type_contents(x) = static_type_contents(DefaultCtx(), x)
90+
function static_type_contents(ctx::StaticContext, x::T) where T
91+
newtypes = [static_type(ctx, p) for p in T.parameters]
92+
newfields = [static_type(ctx, getfield(x, i)) for i in 1:fieldcount(T)]
93+
return newtypes, newfields
94+
end
95+

test/runtests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ using LoopVectorization
2222
@static if GROUP == "Integration" || GROUP == "All"
2323
@testset "StaticCompiler" begin include("teststaticcompiler.jl") end
2424
end
25+
26+
@static if GROUP == "Utilities" || GROUP == "All"
27+
@testset "static_type" begin include("teststatic_type.jl") end
28+
end
29+

test/teststatic_type.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
struct X{A,B,C}
3+
a::A
4+
b::B
5+
c::C
6+
end
7+
8+
x = X([1,2,3], 3, "hello")
9+
xt = static_type(x)
10+
xtt = static_type(typeof(x))
11+
12+
@test xt.a isa MallocArray
13+
@test xt.b isa Int
14+
@test xt.c isa MallocString
15+
@test xtt.parameters[1] == MallocVector{Int}
16+
17+
types, fields = static_type_contents(x)
18+
19+
@test types[1] == MallocVector{Int}
20+
@test fields[1] isa MallocVector
21+
@test fields[1][1] == 1
22+
23+
x = X(1, X([1,2], 1, "hello"), "hello")
24+
xt = static_type(x)
25+
26+
@test xt.b.a isa MallocArray
27+
@test xt.b.c isa MallocString
28+
29+

0 commit comments

Comments
 (0)