I'm using the chipmunk7 library bindings, and trying to call the iterator eachShape
. The bindings source is here
SpaceShapeIteratorFunc* = proc (shape: Shape; data: pointer) {.cdecl.}
## Space/body iterator callback function type.
[..]
proc eachShape*(space: Space; `func`: SpaceShapeIteratorFunc; data: pointer) {.cdecl, importc: "cpSpaceEachShape".}
## Call `func` for each shape in the space.
And calling like this
import std/math
import chipmunk7
import playdate/api
var gravity = v(0, 100)
var timeStep = 1.0/50.0
var time = 0.0
var space = newSpace()
space.gravity = gravity
[..]
proc drawChipmunkHello*() =
# iterate over all shapes in the space
for shape in eachShape(space):
if shape.shapeType == ShapeType.circle:
let circle = shape as CircleShape
drawCircle(circle.body.position, circle.radius, circle.body.angle, kColorBlack)
elif shape.shapeType == ShapeType.segment:
let segment = shape as SegmentShape
drawSegment(segment, kColorBlack)
elif shape.shapeType == ShapeType.poly:
let poly = shape as PolyShape
for i in 0 ..< poly.count:
let a = poly.getVert(i)
let b = poly.getVert((i + 1) % poly.count)
playdate.graphics.drawLine(a.x.toInt, a.y.toInt, b.x.toInt, b.y.toInt, 1, kColorBlack);
I get the coompiler error
hello.nim(63, 25) Error: type mismatch: got <Space>
but expected one of:
proc eachShape(space: Space; func: SpaceShapeIteratorFunc; data: pointer)
first type mismatch at position: 2
missing parameter: func
1 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
Line 63 being the first line of drawChipmunkHello()
A c-sample of how to call the iterator is here
What is the correct syntax to call the iterator?
What Chipmunk calls an iterator and what Nim calls an iterator are two different things. The way you're trying to call eachShape
is indeed correct for a Nim iterator with one parameter. But the three-parameter construct that Chipmunk calls an iterator is not callable this way. Let's look at the error message, it say that eachShape
where given <Space>
, i.e. a single argument of type Space
but it expected:
proc eachShape(space: Space; func: SpaceShapeIteratorFunc; data: pointer)
first type mismatch at position: 2
missing parameter: func
which is three arguments Space
, SpaceShapeIteratorFunc
, and a pointer
. It also tells us that the type mismatch happened at position 2 and that it failed because it was simply missing the func
argument, which makes sense, because we only provided one Space
argument. Now, let's have a look at SpaceShapeIteratorFunc
:
SpaceShapeIteratorFunc* = proc (shape: Shape; data: pointer) {.cdecl.}
## Space/body iterator callback function type.
As we can see this is simply an alias for a procedure which takes a Shape
and a pointer
. It is also defined as being {.cdecl.}
which just means that it follows the standard C calling convention. What this means is that we have to convert the body of your loop into a procedure, and then manually pass that procedure to the iterator. The pointer
that is accepted by SpaceShapeIteratorFunc
and required by eachShape
is where we can store any context that we want to have available in the iterator.
Let's do a simple rewrite:
import std/math
import chipmunk7
import playdate/api
var gravity = v(0, 100)
var timeStep = 1.0/50.0
var time = 0.0
var space = newSpace()
space.gravity = gravity
[..]
proc shapeIter(shape: Shape, data: pointer) {.cdecl.} =
if shape.shapeType == ShapeType.circle:
let circle = shape as CircleShape
drawCircle(circle.body.position, circle.radius, circle.body.angle, kColorBlack)
elif shape.shapeType == ShapeType.segment:
let segment = shape as SegmentShape
drawSegment(segment, kColorBlack)
elif shape.shapeType == ShapeType.poly:
let poly = shape as PolyShape
for i in 0 ..< poly.count:
let a = poly.getVert(i)
let b = poly.getVert((i + 1) % poly.count)
playdate.graphics.drawLine(a.x.toInt, a.y.toInt, b.x.toInt, b.y.toInt, 1, kColorBlack);
proc drawChipmunkHello*() =
# iterate over all shapes in the space
eachShape(space, shapeIter, nil)
As you can see we've now split the body of the iterator body into a new procedure shapeIter
which matches the signature of SpaceShapeIteratorFunc
and we pass this along to eachShape
together with a nil
pointer because we don't need to pass any context. The reason we don't have to pass any context is because all the variables required live in the global scope of our module. If we indeed needed some context to be passed along into the body of the iterator we would need to pass this via the data: pointer
field. This is quite common in C, but can be a bit cumbersome to do in Nim. This is a bit more of an advanced topic, but you could use a Nim closure for this and get the procedure and environment pointers with rawProc
and rawEnv
respectively. Note that rawProc
still has a {.nimcall.}
calling convention, so a caller helper proc would be required. All of this complexity could be put into a custom for loop macro at which point you would be able to call the iterator like you did in your first example. But this is getting into some quite complex territory.