Not even sure the title is correct… No time should be spent trying to infer usefulness from the below, totally contrived as MWE. However, it does make the point about the problem I’m having in my real code.
I have a module, Data, which has a parametric composite struct, DataContainer. I don’t include any using statements in Data (that relate to the parametric types of DataContainer), even though in reality the parametric types are indeed just one of a number of other modules (Type1.jl and Type2.jl): the specific choice is decided on at run time by user conditions. By design Data simply does not know which one, so using/importing/including is not meaningful/possible within the Data module. The Data module implements a function, Allocate (called from Driver), which takes an instance of DataContainer, and for one of DataContainers fields, data, I need to call another function within Allocate, called AllocateSub. AllocateSub takes an instance of the field type, data, as one of its arguments. AllocateSub is implemented in the module that defines the parametric field type (Type1.jl and Type2.jl). Problem is julia wants me to “using” the module (which I can’t as a I don’t know which one it is).
Is there a way in code (maybe via reflection) to obtain the module that holds the relevant function given just the field type of a parametric struct?
How do I get round this? I repeat the solution here, that I’m looking for, is not adding using statements into Data module. My code fails with V1 commented in, if you comment that out and comment in the two blocks relating to V2 it works: V2 is suboptimal for me. As I note in the comments of Driver.jl. I am happy for Driver to know about the parametric types, but I don’t want Data to have to know. This is a question to learn how to do this: in reality this is happening a lot in my code as I’m separating specific types from interfaces that use the types, so I need to resolve this.
The main user code works with interface type modules (like Data), and only the setup code (a more complex version of Driver) makes the decision once, which parametric types (Type1 or Type2) to instantiate the interface type (Data) with. The rest of the code then runs with the interface types and the functions associated with the interface. In short if this can be done, I’d like to avoid where possible a discussion on a different design, I only want to re-write my code if this is not achievable: that is if there is no way round having to add a bunch of using statement within the interface type (Data module).
Thanks,
Andy
Can’t find a way to upload a file, so pasted them here:
Type1.jl
module Type1
export Something
struct Something
name::String
field::Vector{Int64}
Something(n::String) = new(n, [])
end
#
function AllocateSub(d::Something, size::Int64)
resize!(d.field, size)
end
#
end
Type2.jl
module Type2
export SomethingElse
struct SomethingElse
name::String
field::Vector{Float64}
SomethingElse(n::String) = new(n, [])
end
#
function AllocateSub(d::SomethingElse, size::Int64)
resize!(d.field, size)
end
#
end
Data.jl
module Data
export DataContainer, Allocate
# comment in when using V2 only
# using Type1
# using Type2
struct DataContainer{DataType}
name::String
data::Vector{DataType}
DataContainer{DataType}(name::String) where {DataType} = new(name, [])
end
function Allocate(data::DataContainer, outerSize::Int64, innerSize::Int64)
resize!(data.data, outerSize)
T = eltype(data.data)
for i in 1:outerSize
data.data[i] = T("stuff")
end
#
# V1) want this to work, comment out when using V2
#
for i in 1:outerSize
AllocateSub(data.data[i], innerSize)
#something like, function_that_gives_module_that_owns_T.Allocate(data.data[i], innerSize)
end
#
# V2) comment out the above, use this instead, also comment in two usings above
# for i in 1:outerSize
# if T isa Something
# Type1.AllocateSub(data.data[i], innerSize)
# elseif T isa SomethingElse
# Type2.AllocateSub(data.data[i], innerSize)
# end
# end
end
end
Driver.jl
module Driver
#
thisdir = dirname(@__FILE__())
any(path->path == thisdir, LOAD_PATH) || push!(LOAD_PATH, thisdir)
#
using InteractiveUtils
#
using Data
#
# happy for this module to be aware of these two types,
# not happy for Data module to be aware of them
using Type1
using Type2
#
function DoStuff(user::Bool)
if user
data = DataContainer{Something}("Something")
elseif !user
data = DataContainer{SomethingElse}("SomethingElse")
end
Allocate(data, 5, 3)
@show data
end
DoStuff(true)
DoStuff(false)
end