Hi!
I'm refactoring the code to generate the Dockerfiles for the official Nim Docker images: https://github.com/moigagoo/nimage
The main conceptual issue is that there're three flavors, two bases, and nine versions so far, and we want to be able to build an image for each permutation. To organize this mess, I put the code for flavors in three submodules, and each submodule implements the functions for each backend. So calling slim.ubuntu("0.18.0") builds the slim flavor for Nim version 0.18.0 based on Ubuntu. You get the idea.
Now we're getting to the actual issue I'm trying to solve. I want to be able to call the necessary function from the necessary module and pass the necessary version without using a huge case statement:
case flavor
of "slim":
case base
of "ubuntu": slim.ubuntu(version)
of "alpine": slim.alpine(version)
...
I believe there's a much better way in Nim to do that. I thought about using a template and use backticks to construct the identifiers:
template generate(flavor, base, version: string): untyped = `flavor`.`base`(`version`)
but that doesn't work, because the compiler interprets the dot notation as a function call rather than a module calling its member. Since there's no base function with this signature, I get undeclared routine error.
Any suggestions on how do I achieve what I want?
Like this?
type
TheObj = object
base: string
template getField(theobj, field: untyped): untyped = `theobj`.`field`
var theobj = TheObj(base: "Base")
echo theobj.getField(base)
Unfortunately, this doesn't seem to work for me.
theobj and field are strings in my case, not identifiers like in your example. This however seems easy to bypass.
Also, I cannot use
`.`field`()`` or
theobj`.`field`(arg)`` as I get ')' expected error.
And even if I put the parens after the template call, I still get and ambiguous call error:
import flavors / [slim, regular]
...
getField(slim, ubuntu)("0.18.0")
inim_1530782721.nim(11, 12) Error: ambiguous call; both slim.ubuntu(version: string, labels: openarray[tuple of (string, string)])[declared in path\to\flavors\slim.nim(2, 5)] and regular.ubuntu(version: string, labels: openarray[tuple of (string, string)])[declared in path\to\flavors\regular.nim(2, 5)] match for: (string, array[0..-1, empty])
I didn't test this
import macros
macro getField(obj, field: static[string]): untyped =
result = newDotExpr(newIdentNode obj, newIdentNode field)
ref: newDotExpr
Hmm, maybe be something like this?
import macros
macro getField2(obj, field: static[string]): untyped =
var
objident = newIdentNode obj
fieldident = newIdentNode field
result = quote do: `objident`.`fieldident`
macro getField(obj, field: static[string]): untyped =
result = newDotExpr(newIdentNode obj, newIdentNode field)
type
Obj = object
base: string
var obj = Obj(base: "base!")
echo "obj".getField("base")
echo "obj".getField2("base")
Can't use template because what you have are string instead of untyped identifier. But I agree using template should be simpler.
Thanks to the help! Since both variants involve constructing the dot expression by hand, I think I'll use the first one as the shorter one.
I agree using template should be simpler.
I think I've even seen some discussion about that... Can't remember where though...