ptr
Let a function change a variable its caller owns, with @variable pointers and shared ptr.new cells.
import "std:ptr"OSL passes numbers, strings and booleans by value: a function gets its own copy, so changing it doesn't affect the caller. Usually that is what you want. When you want a function to change the caller's variable itself, std:ptr gives you two tools:
@variablepoints at an existing variable. Read and write through it withptr.derefandptr.set.ptr.new(value)makes a shared cell that doesn't belong to any variable. Copies of the cell share the one value.
Pointers are rare in everyday OSL. Arrays, objects and classes are already shared when you pass them, and a function can return several values in an array or object. Reach for ptr for the cases that remain: an out-parameter, a counter many parts of a program update, or an interop boundary where Go expects a pointer.
Pointing at a variable#
@count makes a pointer to the variable count. A parameter that receives one is typed *int. ptr.deref reads the variable it points at, and ptr.set writes it:
import "std:ptr"
def bump(*int n) (
ptr.set(n, ptr.deref(n) + 1)
)
int count = 1
bump(@count)
bump(@count)
log countWithout the @, bump(count) would be a type error, and count would be a copy anyway. The @ at the call site makes it obvious that the callee can change the variable.
Swapping two variables#
A function that must change two of the caller's variables is the classic use:
import "std:ptr"
def swap(*int x, *int y) (
int saved = ptr.deref(x)
ptr.set(x, ptr.deref(y))
ptr.set(y, saved)
)
int a = 1
int b = 2
swap(@a, @b)
log a bOther types#
ptr.deref returns a value of any type, so say what you expect before using it with methods of that type:
import "std:ptr"
def shout(*string text) (
ptr.set(text, ptr.deref(text).as<string>.toUpper())
)
string name = "ada"
shout(@name)
log nameA shared cell#
ptr.new(value) makes a *ptr.Pointer: a cell holding one value. cell.value() reads it and cell.set(v) replaces it. Unlike @, the cell isn't tied to a variable, so it can be handed to many functions and outlives the scope that made it:
import "std:ptr"
def addTo(*ptr.Pointer total, int amount) (
total.set(total.value() + amount)
)
auto total = ptr.new(10)
addTo(total, 5)
addTo(total, 100)
log total.value()Copying a cell does not copy the value. Both names refer to the same cell:
import "std:ptr"
auto first = ptr.new(1)
auto second = first
second.set(2)
log first.value()cell.swap(other) exchanges the values of two cells in place.
import "std:ptr"
auto left = ptr.new("left")
auto right = ptr.new("right")
left.swap(right)
log left.value() right.value()Same value or same cell#
== on two pointers compares what they hold, and === asks whether they are the same cell. The compiler warns when you use == on cells, so write what you mean:
import "std:ptr"
auto a = ptr.new(1)
auto b = ptr.new(1)
auto c = a
log a === b
log a === cCounters that several functions share#
A shared cell is a simple way to count things across callbacks, without a global:
import "std:ptr"
def makeHandler(*ptr.Pointer seen, string label) def(string) (
return def(string item) (
seen.set(seen.value() + 1)
log label item
)
)
auto seen = ptr.new(0)
auto onA = makeHandler(seen, "a:")
auto onB = makeHandler(seen, "b:")
onA("x")
onB("y")
onA("z")
log seen.value()For state shared between threads, use std:sync, which guards access. A plain cell isn't protected against two threads changing it at once.
Null and low-level queries#
ptr.isNull(value) is true for null and for a null Go pointer. The rest of the package (sizeOf, alignOf, offsetOf, addressOf, sliceLen, sliceCap) answers questions about how a value is laid out in memory. They are for debugging and for talking to Go code. Addresses change from run to run, so never store one or print it in a test:
import "std:ptr"
log ptr.isNull(null) ptr.isNull(5)
log ptr.sizeOf(1)
int[] items = [1, 2, 3]
log ptr.sliceLen(items)What does this print?
auto first = ptr.new(1)
auto second = first
second.set(2)
log first.value()Double in place
Finish double so it doubles the caller's variable. The call passes @score, so the function gets a pointer.
42
API reference#
16 members, generated from the std:ptr source of OSL 9.3.0.
Functions
ptr.addressOf(any value) intaddressOf returns the address of value as a number: for an object, array, function or handle, the address of the data it refers to, which every reference to it shares; for null, 0; for other values, the address of a temporary copy.
ptr.alignOf(any value) intalignOf returns the alignment in bytes of value's Go representation. Throws ValueError for null.
ptr.deref(any pointer) anyderef returns the value pointer points at: the variable behind @value, or what a ptr.new Pointer holds. Throws ValueError for a null pointer and TypeError for a value that isn't a pointer.
ptr.equalPointers(any a, any b) booleanequalPointers reports whether a and b are the same object, array, function or handle rather than equal copies. Two nulls are the same.
ptr.isNull(any value) booleanisNull reports whether value is null or a null Go pointer.
ptr.new(any value) *ptr.Pointernew returns a Pointer holding value. Copies of the Pointer share the cell, so p.set(v) is seen by every holder.
ptr.offsetOf(any value, string field) intoffsetOf returns the byte offset of the Go field named field within a Go struct value, such as a package handle. Throws ValueError when value isn't a struct or has no such field; OSL objects are maps, not structs.
ptr.set(any pointer, any value) booleanset stores value where pointer points, in place: in the variable behind @value, or in a ptr.new Pointer. Returns true. Throws ValueError for a null pointer and TypeError for a value that isn't a pointer or a value the variable's type can't hold.
ptr.sizeOf(any value) intsizeOf returns the size in bytes of value's Go representation; for an array or object, the size of one element. Throws ValueError for null.
ptr.sliceCap(array arr) intsliceCap returns how many elements an array can hold before it has to grow.
ptr.sliceData(array arr) intsliceData returns the address of an array's first element, or 0 for an empty array.
ptr.sliceLen(array arr) intsliceLen returns how many elements an array holds.
ptr.stringData(string text) intstringData returns the address of a string's first byte, or 0 for "".