I I'm trying to pass non-trivial type as a static[] argument to a macro, but it fails to compile as soon as I add seq field to the type. I can pass IgnoredArgs if it has int, or string field, but adding seq[] immediately leads to a compilation error. What is wrong with my code, or is there any way to pass static sequence (of strings) to the macro without having to go through deserialization from the AST.
import std/[macros, sequtils]
type
IgnoredArgs* = object
arg: int
arg2: string
# argq: seq[int] # Error: type mismatch: got 'array[0..-1, empty]' for '[]' but expected 'seq[int]'
# args1: seq[string] # got 'array[0..-1, empty]' for '[]' but expected 'seq[string]'
# args: seq[tuple[procname: string, args: seq[string]]] # got 'array[0..-1, empty]' for '[]' but expected 'seq[tuple[procname: string, args: seq[string]]]'
func staticCmd*(args: static[IgnoredArgs] = IgnoredArgs()) = discard
macro cmd*(ignoreArgs: static[IgnoredArgs] = IgnoredArgs()): untyped = discard
This works if you don't need default arguments.
proc pass(a : IgnoredArgs) : IgnoredArgs =
a
macro cmdImpl(ignoreArgs: static[IgnoredArgs]): untyped =
discard
template cmd*(ignoreArgs : static[IgnoredArgs]): untyped =
cmdImpl(pass ignoreArgs)
cmd(IgnoredArgs())
You could also do this if you don't have a non static overload.
macro cmdImpl(ignoreArgs: static[IgnoredArgs]): untyped =
discard
template cmd*(ignoreArgs : IgnoredArgs = IgnoredArgs()): untyped =
cmdImpl(ignoreArgs)
You additionally do this
func cmpImplFunc(ignoreArgs: static[IgnoredArgs]) =
discard
macro cmdImpl(ignoreArgs: static[IgnoredArgs]): untyped =
discard
template cmd*(ignoreArgs : IgnoredArgs{`const`|lit} = IgnoredArgs()): untyped =
cmdImpl(ignoreArgs)
cmpImplFunc(ignoreArgs)
const
x = IgnoredArgs()
cmd
cmd(x)
#cmd(IgnoredArgs()) #unfortuately doesn't work here as
#IgnoredArgs() is neither a const or lit here
#but it counts as a lit up above
Thanks, I think this solution works best for my use case. I can extend argument template to include nkCall, since I actually intended to implement a helper proc to build ignoreArgs argument.
import std/[macros, sequtils]
type
IgnoredArgs* = object
arg: int
arg2: string
argq: seq[int]
args1: seq[string]
args: seq[tuple[procname: string, args: seq[string]]]
func cmpImplFunc(ignoreArgs: static[IgnoredArgs]) =
discard
macro cmdImpl(ignoreArgs: static[IgnoredArgs]): untyped =
echo ignoreArgs
func initArgs(arg2: string = "default", other: openarray[(string, seq[string])] = @[]): IgnoredArgs =
IgnoredArgs(arg2: arg2, args: toSeq(other))
template cmd*(ignoreArgs : IgnoredArgs{`const`|lit|nkCall} = initArgs()): untyped =
cmdImpl(ignoreArgs)
cmpImplFunc(ignoreArgs)
cmd(initArgs("test"))
cmd(initArgs("test", {"a": @["b"]}))
cmd()
block:
const args = initArgs()
cmd(args)
A static seq[T] is really an array if you think about it
Looks like this is mostly correct - two objects, one with seq and other with array fields are represented in almost the same way in the VM (at least when they are passed to a procedure implementation in the nimscript, but I assume there is no object conversion going on)
type
IgnoredArgsSeq* = object
args: seq[string]
IgnoredArgsArray* = object
args: array[2, string]
dump(IgnoredArgsSeq(args: @["arg1", "arg2"]))
dump(IgnoredArgsArray(args: ["arg1", "arg2"]))
ObjConstr t:tyObject Type
Empty t:tyObject Type
ExprColonExpr
Sym k:Field args t:tySequence
Bracket t:tyArray
StrLit "arg1"
StrLit "arg2"
ObjConstr t:tyObject Type
Empty t:tyObject Type
ExprColonExpr
Sym k:Field args t:tyArray
Bracket t:tyArray
StrLit "arg1"
StrLit "arg2"