-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Added ptrops to stdlib #12101
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
Added ptrops to stdlib #12101
Conversation
I think those can be template or |
lib/pure/ptrarith.nim
Outdated
## arithmetic is sometimes desirable for low-level ops/FFI with C. | ||
|
||
func `+`*[T](x: ptr[T]; offset: SomeInteger): ptr[T] = | ||
cast[ptr[T]](cast[ByteAddress](x) + cast[ByteAddress](offset * 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.
I think the +
should be a +%
, do pointer wraparound like unsigned int? or is it undefined behaviour like signed integers?
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.
I assume undefined. But yes I like +%
better as a less accessible operator
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.
seems better to use offset
as a function name here - you want to be able to look for where these operations are used instead of hiding them behind an overload
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 also: https://doc.rust-lang.org/std/primitive.pointer.html - it has docs, examples, risks, etc that hint at how compilers think about this - the names are pretty standard too meaning it's easier to cross-reference with other languages / port code.
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.
I actually meant this +% for the ByteAddress summation but +% for the op name sounds good as well: https://nim-lang.org/docs/system.html#%2B%25%2Cint64%2Cint64
I feel like offset is too much. Libraries that import this probably have lots of pointer arithmetics to do.
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.
I use +!
for pointer additions, the !
screams "dangerous".
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.
+!
at least is unique and thus possible to grep for.
I don't really see the argument that you use pointer operations "a lot" - in particular, you typically don't stack multiple pointer ops in a single expression so even with a name like offset
(that's easier to google for than %!
), you won't end up with excessively long lines because there will only be one of them
If you have lots and lots of pointer arithmetic to do, perhaps that's a sign that you should wrap it in something safer?
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.
That's for writing the wrapper ;)
lib/pure/ptrarith.nim
Outdated
func `+`*[T](x: ptr[T]; offset: SomeInteger): ptr[T] = | ||
cast[ptr[T]](cast[ByteAddress](x) + cast[ByteAddress](offset * sizeof(T))) | ||
|
||
func `-`*[T](x: ptr[T]; offset: SomeInteger): ptr[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.
It should be a template.
I expect those are used in very tight loops.
I dare say emitting assembly might also be considered if somehow compilers cannot generate optimized code.
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.
I agree they should all be templates.
Adding a single line documentation comment would be cool too
(I know is obvious what they do, but new people will not understand).
lib/pure/ptrarith.nim
Outdated
# | ||
|
||
## This module offers operators for performing arithmetic on raw pointer types | ||
## in Nim. Nim is *not* C: using pointer arithmetic in Nim should be avoided (if |
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.
Indeed, and because of this I don't think we should be making pointer arithmetic easier by adding a module like this.
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.
Maybe add the same logic as documentation explaining how can be done,
what are the problems doing so can bring on your code (pros/cons, explanation),
but not really adding it as a new std lib module ❔
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 have lenientops, bitops, endians already for people to spend happy time debugging.
We can add a big bold Pointer arithmetics are very sharp tools. This module is unsafe, comes without guarantees and might cause you to spend endless hours in a debugger, eat your cat or cause the heat death of the universe.
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.
I kind of like the idea @juancarlospaco has, maybe as an addition to the Nim tutorial
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.
That being said the reason I thought I thought it should be a module was because it came up frequently enough (in the forums) for people to want something like this in full knowledge of its foot-shooting properties
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.
Add it to the Nim Manual, Not the Tutorial, noobs should not start with Pointer Arithmethics for a Hello World. 😆
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.
I generally don't see warnings that don't say why something is unsafe as terribly useful - they're not going to stop anyone from using the module. the module does come with guarantees: it will offset pointers, nothing more.
The offset operation itself is harmless.
It is accessing memory through these new pointers that is unsafe, because the new location might be uninitialized, aliased or unaligned. The aliasing in particular brings several interesting issues to the table, because it opens the floodgates to a host of aliasing optimization bugs that generally are only visible when compiling in release mode, meaning crashes that start when you ship the product.
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.
meaning crashes that start when you ship the product.
Btw, you can test your software in -d:release
mode too. ;-)
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.
well, we're talking about risks, and pointer aliasing is a clear and poorly understood risk, judging from the number of stack overflow posts about it, and it's specially devious because it only happens when optimizations are turned on. using this module without understanding them seems bad, and not for the cat - adding the module to the std lib without understanding them seems even worse.
lib/pure/ptrarith.nim
Outdated
@@ -0,0 +1,25 @@ | |||
# |
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 calling it ptrops
similar to bitops
?
generally, I'd recommend against merging this, and thinking about how and if |
@arnetheduck Perhaps |
@awr1 yes, but consider what will happen with your patch on a 32-bit machine when you have a pointer-to-byte pointing to |
## in Nim. Nim is *not* C: using pointer arithmetic in Nim should be avoided (if | ||
## possible, use `array[T]`, `ptr UncheckedArray[T]`, etc.), however, pointer | ||
## arithmetic is sometimes desirable for low-level ops/FFI with C. **Pointer | ||
## arithmetic is a very sharp tool. This module is unsafe, comes without |
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.
## arithmetic is a very sharp tool. This module is unsafe, comes without | |
## | |
## Risks | |
## ----- | |
## | |
## - Pointer Arithmetic is a very sharp tool. This module is unsafe. | |
## - Nim is *not* C: Using Pointer Arithmetic in Nim should be avoided. | |
## - Other reason why it can be problematic. | |
## - Other reason why it can be problematic. |
|
||
template `+%`*[T](x: ptr[T]; offset: SomeInteger): ptr[T] = | ||
## **Unsafe.** Adds `offset * sizeof(T)` bytes from the pointer `x`. | ||
cast[ptr[T]](cast[ByteAddress](x) + cast[ByteAddress](offset * 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.
You need to use +%
here as well to avoid int range checks
https://nim-lang.org/docs/system.html#%2B%25%2CIntMax32%2CIntMax32
|
||
template `-%`*[T](x: ptr[T]; offset: SomeInteger): ptr[T] = | ||
## **Unsafe.** Subtracts `offset * sizeof(T)` bytes from the pointer `x`. | ||
cast[ptr[T]](cast[ByteAddress](x) - cast[ByteAddress](offset * 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.
ditto
Add a |
Sorry, rejected. Pointer arithmetic isn't that hard to do in Nim and people who need to do it can already figure out how to do it. |
I agree with Araq here. Pointer arithmetic already works when you cast to |
we've added something similar to but yeah, longer term it's usually better to use something else |
Unforunately pointer arithmetic keeps coming up every so often which kinda suggests "maybe we should have this as a stdlib module." Although I am not a huge fan of "C-like functionality that is often more trouble than its worth" 😐