|
| 1 | +# Using Pointers |
| 2 | + |
| 3 | +## Creation |
| 4 | + |
| 5 | +To create a pointer, you can use `to_ptr`, like so: |
| 6 | + |
| 7 | +```py |
| 8 | +from pointers import to_ptr |
| 9 | + |
| 10 | +ptr = to_ptr("hello world") |
| 11 | +``` |
| 12 | + |
| 13 | +You can also use the `_` object to replicate the address-of operator, like in other languages: |
| 14 | + |
| 15 | +```py |
| 16 | +from pointers import _ |
| 17 | + |
| 18 | +ptr = _&"hello world" |
| 19 | +``` |
| 20 | + |
| 21 | +Finally, you can directly call `Pointer.make_from`: |
| 22 | + |
| 23 | +```py |
| 24 | +from pointers import Pointer |
| 25 | + |
| 26 | +ptr = Pointer.make_from("hello world") |
| 27 | +``` |
| 28 | + |
| 29 | +**Note:** `Pointer.make_from` is more a low level function for creating a pointer. Its API may be changed at any time without warning. |
| 30 | + |
| 31 | +## Dereferencing |
| 32 | + |
| 33 | +There are a few ways to get the underlying value of the pointer, the simplest being calling the `dereference()` method, like so: |
| 34 | + |
| 35 | +```py |
| 36 | +from pointers import _ |
| 37 | + |
| 38 | +ptr = _&"hi" |
| 39 | +print(ptr.dereference()) # prints out "hi" |
| 40 | +``` |
| 41 | + |
| 42 | +Unfortunately, `dereference` is a pretty long name and doesn't look very pretty when you call it everywhere. Fortunately though, there are different (and more preffered) ways of dereferencing the pointer. |
| 43 | + |
| 44 | +We can use the `_` object to once again replicate the syntax from other languages: |
| 45 | + |
| 46 | +```py |
| 47 | +from pointers import _ |
| 48 | + |
| 49 | +ptr = _&"hi" |
| 50 | +print(_*ptr) |
| 51 | +``` |
| 52 | + |
| 53 | +In some cases like this one, you can even just directly use `*`, without even having to touch `_`! |
| 54 | + |
| 55 | +```py |
| 56 | +ptr = _&"hi" |
| 57 | +print(*ptr) # works just fine |
| 58 | +``` |
| 59 | + |
| 60 | +However, `*` is for arg splats, which introduces some problems. You can use `~` as an alternative, which will always work: |
| 61 | + |
| 62 | +```py |
| 63 | +ptr = _&"hi" |
| 64 | +print(~ptr) |
| 65 | +# ~ is a unary operator, so we can use it anywhere we want |
| 66 | +``` |
| 67 | + |
| 68 | +## Decaying |
| 69 | + |
| 70 | +Converting objects to pointers everywhere may be a bit ugly. To fix this, pointers.py provides a few functions to decay parameters into their pointer equivalents. |
| 71 | + |
| 72 | +The most simple one is `decay`: |
| 73 | + |
| 74 | +```py |
| 75 | +from pointers import decay, Pointer |
| 76 | + |
| 77 | +@decay |
| 78 | +def my_function(a: str, b: str, c: Pointer): # must be type hinted as Pointer to convert |
| 79 | + print(a, b, *c) |
| 80 | + |
| 81 | +my_function('a', 'b', 'c') |
| 82 | +``` |
| 83 | + |
| 84 | +This will be fine for most people, but it hsa removes type safety on the target function. If you don't care about type safety in your code, then don't worry about this, but if you do, then there are alternatives. |
| 85 | + |
| 86 | +The first alternative is `decay_annotated`, which decays parameters hinted as `Annotated[T, Pointer]` to a pointer. |
| 87 | + |
| 88 | +Here's an example: |
| 89 | + |
| 90 | +```py |
| 91 | +from pointers import decay_annotated, Pointer |
| 92 | +from typing import Annotated # if you are below python 3.9, you can use typing_extensions here |
| 93 | + |
| 94 | +@decay_annotated |
| 95 | +def my_function(a: str, b: str, c: Annotated[str, Pointer]): |
| 96 | + print(a, b, *c) |
| 97 | + |
| 98 | +my_function('a', 'b', 'c') |
| 99 | +``` |
| 100 | + |
| 101 | +However, `decay_annotated` has a very large drawback. |
| 102 | + |
| 103 | +While it adds type safety for calling the function, it breaks it inside. A type checker still thinks that the argument is a `str`, and not a `Pointer`. |
| 104 | + |
| 105 | +Take the following as an example: |
| 106 | + |
| 107 | +```py |
| 108 | +@decay_annotated |
| 109 | +def my_function(a: str, b: str, c: Annotated[str, Pointer]): |
| 110 | + print(a, b, ~c) # type checker error! |
| 111 | +``` |
| 112 | + |
| 113 | +The solution is to use `decay_wrapped`, which takes the caller function as a paremeter: |
| 114 | + |
| 115 | +```py |
| 116 | +from pointers import decay_wrapped, Pointer |
| 117 | + |
| 118 | +def my_function_wrapper(a: str, b: str, c: str) -> None: # this should mimick your actual function |
| 119 | + ... |
| 120 | + |
| 121 | +@decay_wrapped(my_function_wrapper) |
| 122 | +def my_function(a: str, b: str, c: Pointer[str]): |
| 123 | + print(a, b, *c) |
| 124 | + print(a, b, ~c) # no type checker error, it thinks c is a pointer! |
| 125 | + |
| 126 | +my_function('a', 'b', 'c') # works just fine, type checker things c takes a string |
| 127 | +``` |
| 128 | + |
| 129 | +It can be broken pretty easily though: |
| 130 | + |
| 131 | +```py |
| 132 | +from pointers import decay_wrapped, Pointer |
| 133 | + |
| 134 | +def my_function_wrapper(a: str, b: str, c: str, d: str) -> None: |
| 135 | + ... |
| 136 | + |
| 137 | +@decay_wrapped(my_function_wrapper) |
| 138 | +def my_function(a: str, b: str, c: Pointer[str]): |
| 139 | + print(a, b, *c) |
| 140 | + |
| 141 | +my_function('a', 'b', 'c') # type checker error! missing parameter "d" |
| 142 | +``` |
| 143 | + |
| 144 | +## Assignment |
| 145 | + |
| 146 | +We can change where the pointer is looking at by using `assign`, or more commonly, the `>>=` operator: |
| 147 | + |
| 148 | +```py |
| 149 | +from pointers import _ |
| 150 | + |
| 151 | +ptr = _&"hi" |
| 152 | +ptr.assign("hello") |
| 153 | +ptr >>= "hello" # equivalent to the above |
| 154 | + |
| 155 | +print(*ptr) # prints out "hello" |
| 156 | +``` |
0 commit comments