mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 19:02:18 +00:00
Add withValue for immutable tables (#24825)
This change adds `withValue` templates for the `Table` type that are
able to operate on immutable table values -- the existing implementation
requires a `var`.
This is needed for situations where performance is sensitive. There are
two goals with my implementation:
1. Don't create a copy of the value in the table. That's why I need the
`cursor` pragma. Otherwise, it would copy the value
2. Don't double calculate the hash. That's kind of intrinsic with this
implementation. But the only way to achieve this without this PR is to
first check `if key in table` then to read `table[key]`
I brought this up in the discord and a few folks tried to come up with
options that were as fast as this, but nothing quite matched the
performance here. Thread starts here:
https://discord.com/channels/371759389889003530/371759389889003532/1355206546966974584
(cherry picked from commit 0f5732bc8c)
This commit is contained in:
@@ -676,6 +676,68 @@ template withValue*[A, B](t: var Table[A, B], key: A,
|
||||
else:
|
||||
body2
|
||||
|
||||
template withValue*[A, B](t: Table[A, B], key: A,
|
||||
value, body1, body2: untyped) =
|
||||
## Retrieves the value at `t[key]` if it exists, assigns
|
||||
## it to the variable `value` and executes `body`
|
||||
runnableExamples:
|
||||
type
|
||||
User = object
|
||||
name: string
|
||||
|
||||
proc `=copy`(dest: var User, source: User) {.error.}
|
||||
|
||||
proc exec(t: Table[int, User]) =
|
||||
t.withValue(1, value):
|
||||
assert value.name == "Hello"
|
||||
do:
|
||||
doAssert false
|
||||
|
||||
var executedElseBranch = false
|
||||
t.withValue(521, value):
|
||||
doAssert false
|
||||
do:
|
||||
executedElseBranch = true
|
||||
assert executedElseBranch
|
||||
|
||||
var t = initTable[int, User]()
|
||||
t[1] = User(name: "Hello")
|
||||
t.exec()
|
||||
|
||||
mixin rawGet
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index > 0:
|
||||
let value {.cursor, inject.} = t.data[index].val
|
||||
body1
|
||||
else:
|
||||
body2
|
||||
|
||||
template withValue*[A, B](t: Table[A, B], key: A,
|
||||
value, body: untyped) =
|
||||
## Retrieves the value at `t[key]` if it exists, assigns
|
||||
## it to the variable `value` and executes `body`
|
||||
runnableExamples:
|
||||
type
|
||||
User = object
|
||||
name: string
|
||||
|
||||
proc `=copy`(dest: var User, source: User) {.error.}
|
||||
|
||||
proc exec(t: Table[int, User]) =
|
||||
t.withValue(1, value):
|
||||
assert value.name == "Hello"
|
||||
|
||||
t.withValue(521, value):
|
||||
doAssert false
|
||||
|
||||
var t = initTable[int, User]()
|
||||
t[1] = User(name: "Hello")
|
||||
t.exec()
|
||||
|
||||
withValue(t, key, value, body):
|
||||
discard
|
||||
|
||||
|
||||
iterator pairs*[A, B](t: Table[A, B]): (A, B) =
|
||||
## Iterates over any `(key, value)` pair in the table `t`.
|
||||
|
||||
Reference in New Issue
Block a user