-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fusion/pointers: add pointer arithmetic operators #21
base: master
Are you sure you want to change the base?
Conversation
c007f33
to
2196b55
Compare
type T = typeof(p[]) | ||
cast[ptr T](cast[ByteAddress](p) -% off * sizeof(T)) | ||
|
||
template `[]`*[T](p: ptr T, off: int): T = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single pointer is not an array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about this; slightly more verbose than []
, []=
but still short enough:
# `[]` =>
template `at`*[T](p: ptr T, off: int): T = ...
# `[]=` =>
template `at=`*[T](p: ptr T, off: int, val: T) = ...
it would still allow this:
echo p.at(1)
p.at(2) = 2
p.at(2) -= 3
(at
naming precedent: http://www.cplusplus.com/reference/vector/vector/at/ or https://riptutorial.com/opencv/example/6394/access-individual-pixel-values-with-cv--mat--at-t---)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have the toUncheckedArray
proc to offer array indexing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toUncheckedArray
is useful but not as a replacement for that; this module is about convenience when dealing with low-level code so you use cast
less often and make the intent clearer
p[1]
vs
p.toUncheckedArray[1]
That's why it's been implemented in so many places (with more or less good implementations eg many implementations have issues eg are not safe wrt multiple template argument evaluation bugs) and requested also in many places as shown here #21 (comment)
Among other things, it makes it in particular easy to adapt C/C++ code to nim with the simplest possible syntax.
pa[1] = 2 | ||
doAssert a[1] == 2 | ||
type T = typeof(p[]) # pending https://github.com/nim-lang/Nim/issues/13527 | ||
cast[ptr T](cast[ByteAddress](p) +% off * sizeof(T)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my own low level code I never needed the * sizeof(T)
part. It's not intuitive and probably error-prone. Why try to outsmart the programmer who chose to operate on a very low level for a reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #21 (comment)
## Unsafe. | ||
(p + off)[] | ||
|
||
template `[]=`*[T](p: ptr T, off: int, val: T) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single pointer is not an array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #21 (comment)
I have to disagree here. Here are just a few examples: # Nim/lib/nimhcr.nim:393:14:
curr = cast[ptr cstring](cast[int64](curr) + sizeof(ptr cstring))
=>
curr += 1
# Nim/lib/pure/coro.nim
coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine))
=>
coro.stack.top = cast[pointer](coro + 1)
# Nim/lib/system/excpt.nim:109:
zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
=>
zeroMem(cast[pointer](s + 1), s.len*sizeof(pointer))
# Nim/lib/system/gc_regions.nim (with `type Chunk = ptr BaseChunk`)
r.bump = fresh +! sizeof(BaseChunk)
=>
r.bump = fresh + 1
# many other instances; system/gc* code could likely be made more maintainable with
# less `pointer` and more `ptr[T]` + standard ptr[T] arithmetics, but that's out of scope for this discussion
using There's plenty of evidence for this:
There's no need to obfuscate code just because it involves finally, this is oft-requested and re-implemented, often poorly
plenty of forum posts asking about how to do it too:
|
I don't see the benefit in coro.stack.top = cast[pointer](coro + 1)
r.bump = fresh + 1
Frankly, the In fact, when we analyse the situation further:
vs
Often clarity in programming is achieved by ignoring pretty much everything of what was done in C and Unix. |
There is plenty of evidence pointing to the opposite, see the re-implementations and form posts I've linked. It is also my experience that > 90% of the time when dealing with |
946eb57
to
941556e
Compare
nothing will break, the only thing is that warnings will eventually get triggered once
{.cast(safe).}:
is implemented, and{.cast(safe).}:
can then be used to avoid triggering that warning.this avoids using non-standard operators like
+!
as was suggested in nim-lang/Nim#15490 (comment)and it gives the maximum flexibility: allows user to add
--warningAsError:UnsafeBlock
, or--warning:UnsafeBlock:off
or localize those in code, or use{.cast(safe).}:
blocksas mentioned in nim-lang/Nim#15490 (comment)
often requested, eg: Added ptrops to stdlib by awr1 · Pull Request #12101 · nim-lang/Nim