How to parameterize the name of a struct?

I would like to define in an automatic way a struct like:

struct MyStruct
   some_field
end

where the struct name “MyStruct” can be read from a string array. I found https://github.com/cstjean/QuickTypes.jl, but from my understanding this only uses parameterization for the struct fields.

Like so maybe:

julia> names = [:A, :B]
2-element Array{Symbol,1}:
 :A
 :B

julia> for n in names
         @eval struct $n
                 some_field
               end
       end

julia> A(1)
A(1)
3 Likes

To add on this solution:

julia> names = ["A", "B"]
2-element Array{String,1}:
 "A"
 "B"
julia>   for n in names
            s=Symbol(n)
            @eval struct $s
                  some_field
                  end
            end
1 Like

Is the name of the struct important? Could a NamedTuple be of use to you? https://docs.julialang.org/en/v1/manual/types/#Named-Tuple-Types-1

5 Likes

Many thanks for helping me! @Daniel_Berge, I want to use multiple dispatch in order to call certain methods based on the argument type. To be more specific, I want to write some generic high level wrappers for lab instruments. For example:

function set_voltage(psu::Agilent, volt = 0)
write(psu.obj, "SpecificAgilentCommand")
end

function set_voltage(psu::Tek, volt = 0)
write(psu.obj, "SpecificTekCommand")
end

I know this could be handled easily with if/else, but I want to try a different way.
“psu” can contain fields like:

struct psu
   name
   address
   obj
end

and the “obj” field can be initialized using https://github.com/BBN-Q/Instruments.jl and the “address” field.
Having multiple types of equipment it becomes cumbersome to hardcode manually all the structs with instruments names, so I wanted a way to auto-generate a struct based on the instrument that I need, in order to specialize a method.
I think code is much more readable like this and anyone can add a new method for a different instrument easily.

But then you should use a type parameter, on which you can dispatch just as well:

struct Int{Kind}
  name
  address
  obj
end

f(ins::Inst{:Aglient}) = 1
f(ins::Inst{:Tek})  = 2

If you need something instrument specific you can always put that in a field containing a Dict or NamedTuple.

3 Likes

This does not seem to work (Version 1.3.0) (Edit: yes it does, fixed a typo!):

julia> struct Inst{Kind}
         name
         address
         obj
       end

julia> a=Inst{:Agilent}("foo",2,3)
Inst{:Agilent}("foo", 2, 3)

julia> f(i::Inst{:Agilent}) = 1
f (generic function with 1 method)

julia> f(a)
1

@mauro3, your last solution is even simpler! Thank you!
@KajWiik, I think there is a typo in your code(Agilent vs Aglient). Your example worked for me in 1.3.1

DOH! :flushed:

For anyone interested, this is how the code looks so far:

using Instruments
import Instruments: ResourceManager, GenericInstrument, connect!, disconnect!, write, read, query

mutable struct INSTR{instr_name}
    name
    address
    obj
end
# Generic instrument constructor
INSTR(instr_name, address) = INSTR{instr_name}(instr_name, address, GenericInstrument())

connect!(rm, instr)     = connect!(rm, instr.obj, instr.address)
disconnect!(instr)      = disconnect!(instr.obj)
disconnect!(rm, instr)  = disconnect!(instr.obj)

function set_instr_state!(rm, x...; act = disconnect!)
    for instr in x
        act(rm, instr)
    end
end
function set_volt(psu::INSTR{:Agilent}, volt = 0)
    # write(psu.obj,"some instruction 1")
    println("Agilent was called!")
end
function set_volt(psu::INSTR{:Tek}, volt = 0)
    # write(psu.obj,"some instruction 2")
    println("Tek was called!")
end

# instantiate obj
rm = ResourceManager()
my_psu1 = INSTR(:Agilent, "GPIB0::14::INSTR")
my_psu2 = INSTR(:Tek, "GPIB0::7::INSTR")
set_instr_state!(rm, my_psu1, my_psu2; act = connect!) #this will error if no instruments availale
# USAGE
set_volt(my_psu2)
# Disconnect everything
set_instr_state!(rm, my_psu1, my_psu2)

Actual communication not tested yet, but I don’t expect any issues.
Thank you all, again, for your help!

3 Likes