2

The name of the struct to instantiate will be passed by the caller to my program. Then I would need to instantiate the corresponding struct for the same for further processing.

For example, if the struct is defined like this

struct A end

and I have a function defined as

function load(struct_name::AbstractString)
    if struct_name == "A"
        return A()
    elseif struct_name == "B"
        return B()
    elseif ..... # and so on
    end
end

it will work. But is there a more direct way like return struct_name() instead of having n number of if else statements? I see that Julia supports reflection. How can that be used to support the above use case?

WebDev
  • 1,211
  • 11
  • 17
  • Considered a dictionary? Also, what is reflection? – Evgeny May 18 '19 at 06:28
  • 1
    @EPo Reflection basically allows inspection of defined types and methods at runtime without knowing the names of the types and methods at compile time. In languages such as Java, it also allows instantiation of objects. For example, see here: https://stackoverflow.com/questions/9886266/is-there-a-way-to-instantiate-a-class-by-name-in-java – WebDev May 18 '19 at 06:43
  • @EPo , how would I use a dictionary in this case? Note that I don't want to instantiate all the structs and keep them in the Dict , but only instantiate the one required as passed to the function. – WebDev May 18 '19 at 08:23
  • You can be calling the constructors based on dictionary lookup, but generally playing with this kind of dispatch is risky and usually indicates something is wrong about how other parts of the program (or data structures) are designed, so maybe worth another look why do you end up looking for such functionality in the context of your caller and how the result is used. – Evgeny May 18 '19 at 08:29
  • Please see small example below. – Evgeny May 18 '19 at 09:18
  • This is approximately a duplicate of https://stackoverflow.com/questions/34016768/julia-invoke-a-function-by-a-given-string/34023458#34023458 – StefanKarpinski May 21 '19 at 16:47

3 Answers3

3

I would recommend not doing it in production code, but you can do the following:

function load(struct_name::AbstractString)
    invoke(eval(Symbol(struct_name)),Tuple{})
end

strut_name via eval will get resolved in the global scope of the module.

It is safer to use a dictionary as @EPo suggested.

Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
  • Thanks @Bogumił Kamiński , this works as required. What are the precautions / drawbacks that need to be taken care of when using this? – WebDev May 18 '19 at 08:35
  • Actually using `Symbol` makes `eval` a lot safer in this case than `eval` in e.g. Python as we tell Julia to look for a binding to this symbol. However, other precautions apply - i.e. it is slow itself, it is not type stable, it gets resolved in global scope (and possibly you could have a binding to this name in local scope also). In general - in most cases it is a sign that you could use other design. – Bogumił Kamiński May 18 '19 at 11:23
1

An example of dictionary-based dispatch. Dict("a" => A, "b" => B)[tag] selects a constructor, and () calls it.

struct A end
struct B end

function dispatch(tag)
   return Dict("a" => A, "b" => B)[tag]()
end

@assert dispatch("a") == A()

If you care about default values to handle unexpected parameter, for example dispatch('zzz'), you can make recourse to get().

As a side note about risks of eval() there is a small collection of powerful warning references in a neighboring Python question. In short, eval() is a big security hole and a 'smell' (warning sign) for a questionable design of a program.

Evgeny
  • 4,173
  • 2
  • 19
  • 39
  • 1
    Thanks for your inputs! Going through the links, I understand that using eval() is a security risk for code injection kind of issues, where any string could be sent for instantiation. So, for now I will use the if else statements itself which does not have this issue, and also see if it is really required and there is no other alternative. – WebDev May 18 '19 at 10:01
  • A simple pattern to write that you could consider is `Dict(Symbol(fun)=>fun for fun in [sin, cos, abs])`. – Bogumił Kamiński May 18 '19 at 11:24
  • And to have strings as keys `Dict(string(Symbol(fun))=>fun for fun in [sin, cos, abs])`. – Bogumił Kamiński May 18 '19 at 12:06
1

You could use a macro instead:

julia> module Load
       export @load

       macro load(struct_name::Symbol)
           return :($(esc(struct_name))())
       end

       end
Main.Load

julia> using Main.Load: @load

julia> struct A end

julia> struct B end

julia> @load A
A()

julia> @macroexpand @load B
:(B())

julia> @load C
ERROR: UndefVarError: C not defined
Stacktrace:
 [1] top-level scope at none:0
HarmonicaMuse
  • 7,633
  • 37
  • 52