Skip to content

Commit

Permalink
support ConstructionBase: constructorof and setproperties (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
aplavin authored Nov 20, 2024
1 parent dd01296 commit 6567009
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/StructArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include("utils.jl")
include("collect.jl")
include("sort.jl")
include("lazy.jl")
include("constructionbase.jl")
include("tables.jl")

# Implement refarray and refvalue to deal with pooled arrays and weakrefstrings effectively
Expand Down
17 changes: 17 additions & 0 deletions src/constructionbase.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using ConstructionBase

# (named)tuple eltypes: components/fields are all that's needed for the StructArray() constructor
ConstructionBase.constructorof(::Type{<:StructArray{<:Union{Tuple, NamedTuple}}}) = StructArray

# other eltypes: need to pass eltype to the constructor in addition to components
ConstructionBase.constructorof(::Type{<:StructArray{T}}) where {T} = function(comps::CT) where {CT}
# the resulting eltype is like T, but potentially with different type parameters, eg Complex{Int} -> Complex{Float64}
# probe its constructorof to get the right concrete type
ET = Base.promote_op(constructorof(T), map(eltype, fieldtypes(CT))...)
StructArray{ET}(comps)
end

# two methods with the same body, required to avoid ambiguities
# just redirect setproperties to constructorof
ConstructionBase.setproperties(x::StructArray, patch::NamedTuple) = constructorof(typeof(x))(setproperties(getproperties(x), patch))
ConstructionBase.setproperties(x::StructArray, patch::Tuple) = constructorof(typeof(x))(setproperties(getproperties(x), patch))
42 changes: 42 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ using StaticArrays
using TypedTables: Table
using DataAPI: refarray, refvalue
using Adapt: adapt, Adapt
using ConstructionBase: constructorof, setproperties, getproperties, getfields
using JLArrays
using LinearAlgebra
using Test
Expand Down Expand Up @@ -529,6 +530,47 @@ end
@test_throws ArgumentError StructArray(a=[1, 2], b=[3])
end

@testset "ConstructionBase" begin
# first, check the required invariants
@testset for obj in Any[
StructArray(([1, 2, 3],)),
StructArray((Int[],)),
StructArray(([1, 2, 3], 4:6)),
StructArray(a=[1, 2, 3]),
StructArray(a=[1, 2, 3], b=4:6),
StructArray([1+2im, 3+4im, 5+6im]),
StructArray(ComplexF64[]),
]
# constructorof of getfields returns the same object
@test constructorof(typeof(obj))(getfields(obj)...) === obj

# setproperties with getproperties, or with empty properties, returns the same object
@test setproperties(obj, getproperties(obj)) === obj
if getproperties(obj) isa Tuple
@test setproperties(obj, ()) === obj
else
@test setproperties(obj, (;)) === obj
end
end

# now, check less trivial cases: reconstruction with different property types and names
s = StructArray(a=[1, 2, 3])
@test constructorof(typeof(s))((a=1:3,)) === StructArray(a=1:3)
@test constructorof(typeof(s))((b=1.0:3.0,)) === StructArray(b=1.0:3.0)

s = StructArray(a=[1, 2, 3], b=4:6)
@test setproperties(s, a=10:12)::StructArray === StructArray(a=10:12, b=4:6)
@test_throws ArgumentError setproperties(s, ccc=10:12)

s = StructArray(([1, 2, 3], 4:6))
@test setproperties(s, (10:12,))::StructArray === StructArray((10:12, 4:6))

s = StructArray([1+2im, 3+4im, 5+6im])
@test constructorof(typeof(s))((re=10:0.1:10.2, im=[1,2,3]))::StructArray == StructArray{ComplexF64}((10:0.1:10.2, [1,2,3]))
@test constructorof(typeof(s))((10:0.1:10.2, [1,2,3]))::StructArray == StructArray{ComplexF64}((10:0.1:10.2, [1,2,3]))
@test setproperties(s, re=10:0.1:10.2)::StructArray == StructArray{ComplexF64}((10:0.1:10.2, [2,4,6]))
end

@testset "complex" begin
a, b = [1 2; 3 4], [4 5; 6 7]
t = StructArray{ComplexF64}((a, b))
Expand Down

0 comments on commit 6567009

Please sign in to comment.