Skip to content

new operator #4

Description

@rokups

Hey,
Awesome you pursue this idea! I tried this a while in the past as well, although i tried to make automagic new operator for standard nim objects, allocating non-ref with init() and ref with new(). That was a limited success. I spent a while mucking in nim's ast trees. Since then i no longer use nim for anything. Since you are interested in this featured i figured i will share what i cobbled together. If you find any of this useful - treat this snippet as public domain code. And if it is useless - off to 🗑️ then 👍🏻

import macros
import strutils
# 12:43 <Araq> compiler/semstmts, edit semOverride
# 12:44 <Araq> add a new field finalizer* to the TType in ast.nim
# 12:44 <Araq> look at how deepcopy is implemented
# 12:44 <Araq> where it's used and instantiated
# 12:45 <Araq> cause `=finalize` needs to works with generics too
# 12:45 <Araq> so that's a slight complication
# 12:45 <Araq> but actually just grep for "deepcopy"
# 12:45 <Araq> it's all there for it
# 12:45 <Araq> generic instantation magic
# 12:46 <Araq> codegen type info slot

macro ctor*(none: untyped): auto =
    let args = callsite()
    var prc: NimNode
    var allocator: NimNode
    if args.len == 3:
        if args[1].kind != nnkBracket:
            error("Usage: {.ctor: [allocator: prc]}")
        if args[2].kind != nnkProcDef:
            error("ctor pragma is used only with procedures")
        prc = args[2]
        var margs = args[1]
        for i in 0 ..< margs.len:
            if $margs[i][0] == "allocator":
                allocator = margs[i][1]
            else:
                error("Unknown {.ctor.} paramater " & $margs[i][0])
    else:
        if args[1].kind != nnkProcDef:
            error("ctor pragma is used only with procedures")
        prc = args[1]
        allocator = new_ident_node("new")

    if prc[3][1][1].kind != nnkVarTy:
        error("Constructor must have var non-ref type as first parameter")
    if prc[3][0].kind != nnkEmpty:
        error("Constructor must not have return type")
    var proc_name = $prc[0]
    if proc_name notin ["init", "init*"]:
        error("Constructor name must be `init` but is `" & proc_name & "`")

    var export_proc = proc_name.ends_with("*")
    var type_identifier = prc[3][1][1][0]

    var ctor = quote do:
        proc init(_: typedesc[`type_identifier`]): `type_identifier` =
            init(result)
    ctor = ctor[0]
    ctor[2] = prc[2]

    if export_proc:
        ctor[0] = new_nim_node(nnkPostfix).add(
            new_ident_node("*"),
            ctor[0]
        )

    # Extend ctor with parameters of constructor
    for i in 2 ..< prc[3].len:
        ctor[3].add(prc[3][i])

    # Passes ctor params to main init proc
    ctor[6][0][1] = new_ident_node("result")    # otherwise result is taken from macro context. weird!
    for i in 2 ..< prc[3].len:
        ctor[6][0].add(prc[3][i][0])

    var allc = quote do:
        proc new(_: typedesc[`type_identifier`]): ref `type_identifier` =
            `allocator`(result)
            init(result[])
    allc = allc[0]
    allc[2] = prc[2]

    if export_proc:
        allc[0] = new_nim_node(nnkPostfix).add(
            new_ident_node("*"),
            allc[0]
        )

    # Extend allc with parameters of constructor
    for i in 2 ..< prc[3].len:
        allc[3].add(prc[3][i])

    # Passes allc params to main init proc
    allc[6][0][1] = new_ident_node("result")    # otherwise result is taken from macro context. weird!
    allc[6][1][1][0] = new_ident_node("result")
    for i in 2 ..< prc[3].len:
        allc[6][1].add(prc[3][i][0])

    result = new_stmt_list(prc, ctor, allc)


when is_main_module:
    type
        Foo = object
            a: int
        Bar = object
            a: int

    proc destroy(self: ref Foo) =
        echo "done"
    
    proc custom_new(res: var ref Foo) =
        echo "allocating with custom allocator"
        var x: ref Foo
        new(x)
        res = x

    # var x: ref Foo
    # custom_new(x)

    # proc init(self: var Foo, n: int) =
    #     self.a = n

    # proc init(_: typedesc[Foo], n: int): Foo =
    #     init(result, n)

    # proc new(_: typedesc[Foo], n: int): ref Foo =
    #     custom_new(result, destroy)
    #     init(result[], n)

    proc init(self: var Foo, n: int) {.ctor.} =
        self.a = n

    # proc init(self: var Bar, n: int) {.ctor: [allocator: custom_new].} =
    #     self.a = n

    var f: Foo
    f.init(1)
    echo f.repr
    #echo Foo.init(2).repr
    echo Foo.new(3).repr

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions