Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {.experimental.}
- import macros
- import tables
- macro ctor*(none: untyped): auto =
- let args = callsite()
- if args[1].kind != nnkProcDef:
- error("`ctor` pragma is used only with procedures.")
- var procTemplate = args[1]
- var paramsTemplate = procTemplate[3]
- if paramsTemplate.len < 2:
- error("Constructor must have at least one parameter.")
- if paramsTemplate[1][1].kind != nnkVarTy:
- error("First parameter of constructor must be a `var Type`.")
- if paramsTemplate[0].kind != nnkEmpty:
- error("Constructor must not return a value.")
- # Original proc is preserved.
- var prc = procTemplate.copy()
- result = newStmtList(prc)
- # Construct parameter `_: typedesc[T]`
- var resultType = paramsTemplate[1][1][0] # Type identifier
- var typeParam = newNimNode(nnkIdentDefs).add(
- newIdentNode("_"),
- newNimNode(nnkBracketExpr).add(
- newIdentNode("typedesc"),
- resultType.copy()
- ),
- newNimNode(nnkEmpty)
- )
- # Generate value constructor
- # proc init*[T](_: typedesc[MyObj], n: T): MyObj =
- # init(result, n)
- prc = procTemplate.copy()
- var params = prc[3]
- # Insert return type
- params[0] = resultType.copy()
- # Replace `self` parameter with `_: typedesc[T]`
- params[1] = typeParam.copy()
- # Replace body of generated constructor with call to original constructor init(result, ...)
- prc[6] = newStmtList(
- newNimNode(nnkCall).add(
- newIdentNode("init"),
- newIdentNode("result")
- )
- )
- for i in 2..<params.len:
- prc[6][0].add(params[i][0].copy())
- result.add(prc)
- # Generate reference constructor
- # proc new*[T](_: typedesc[MyObj], n: T): ref MyObj =
- # result.new()
- # init(result, n)
- prc = procTemplate.copy()
- if prc[0].kind == nnkPostfix:
- prc[0][1] = newIdentNode("new")
- else:
- prc[0] = newIdentNode("new")
- params = prc[3]
- # Insert ref return type
- params[0] = newNimNode(nnkRefTy).add(resultType.copy())
- # Replace `self` parameter with `_: typedesc[T]`
- params[1] = typeParam.copy()
- # Replace body of generated constructor with call to original constructor new(result); init(result, ...)
- prc[6] = newStmtList(
- newNimNode(nnkCall).add(
- newIdentNode("new"),
- newIdentNode("result")
- ),
- newNimNode(nnkCall).add(
- newIdentNode("init"),
- newIdentNode("result")
- )
- )
- for i in 2..<params.len:
- prc[6][1].add(params[i][0].copy())
- result.add(prc)
- if isMainModule:
- type
- MyObj[T] = object
- n: T
- proc init[T](self: var MyObj[T]; n: T) {.ctor.} =
- self.n = n
- proc init[A, B](self: var Table[A, B], initial_size=64) {.ctor.} =
- self = init_table[A, B](initial_size)
- self[123] = 321
- proc main =
- var a = MyObj.init(1) # Generated value type constructor
- doAssert(a.n == 1)
- var b = MyObj.new(2) # Generated ref type constructor
- doAssert(b.n == 2)
- var c = MyObj[byte]() # Initialize object without constructing it
- doAssert(c.n == 0)
- c.init(3) # Call original constructor which had `ctor` pragma applied to it
- doAssert(c.n == 3)
- var d = MyObj[byte].new() # Initialize ref object without constructing it
- doAssert(d.n == 0)
- d.init(4) # Call original constructor which had `ctor` pragma applied to it
- doAssert(d.n == 4)
- var e = Table[int, int].new()
- doAssert(e[123] == 321)
- var f = Table[int, int].init()
- doAssert(f[123] == 321)
- main()
Add Comment
Please, Sign In to add comment