nimraylib_now icon indicating copy to clipboard operation
nimraylib_now copied to clipboard

How to wrap object fields

Open planetis-m opened this issue 4 years ago • 10 comments

Not everything needs to be wrapped, for example math objects are ok. Fields with naming like xCount need to be write protected. However object like Mesh hold collections that need to be wrapped in order to implement range checks for example.

First one is easy, remove export marker and write procs, as explained in the manual:

proc glyphCount*(x: Front): int32 = x.glyphCount

My solution for the last one is to return hidden pointers (this has the cost of introducing temporaries) and define array access operators on distinct types, one for each field:

type
  MaterialMaps* = distinct Material

template maps*(x: Material): MaterialMaps = MaterialMaps(x)

proc `[]`*(x: MaterialMaps, i: MaterialMapIndex): MaterialMap =
  # implement range checks here
  result = Material(x).maps[i]

proc `[]`*(x: var MaterialMaps, i: MaterialMapIndex): var MaterialMap =
  result = Material(x).maps[i]

proc `[]=`*(x: var MaterialMaps, i: MaterialMapIndex, val: MaterialMap) =
  Material(x).maps[i] = val

Tested and it works. Pros: Accidentally creating a copy: let x = x.maps is prevented by the compiler (no =copy defined for distincts), also the syntax remains the same. Cons: temporary pointer variables are introduced by the C back end.

planetis-m avatar Jan 08 '22 10:01 planetis-m

Also worth exploring dot operators: https://nim-lang.github.io/Nim/manual_experimental.html#special-operators-dot-operators

planetis-m avatar Jan 14 '22 12:01 planetis-m

Needs to support .[] and .[]= in order to work properly.

planetis-m avatar Jan 17 '22 13:01 planetis-m

I even changed the parser to accept .![] but the solution is simple. Just use templates! Updated the issue.

planetis-m avatar Jan 17 '22 16:01 planetis-m

I'm not sure I completely understand the problem. Even with range checks - usually you just pass a model to a function and this function parses it into a suitable C struct so we rarely have to interact with the internals unless we want something very specific. And at this point we might as well risk a little bit since these cases should be rare and it will be relatively simple to isolate the bug should it happen.

I don't know the main use case for this so the above can be wrong. Could you clarify the need for this? This change looks a bit involved so I wouldn't include it unless it drastically improves the ease of using the library. And there's always a reason that C works fine with it, do we really want to put more safeguards by sacrificing the familiarity with C.

greenfork avatar Jan 19 '22 05:01 greenfork

In lots of examples there is:

  let mapTexture = loadTexture("resources/cubicmap_atlas.png")
  model.materials[0].maps[int(Albedo)].texture = mapTexture

So they are accessing raw pointers directly. With this proposal the syntax remains exactly the same but .materials and .maps is actually a template call and there is no need for returning auxiliary wrapper objects like this.

planetis-m avatar Jan 19 '22 13:01 planetis-m

Could you provide the resulting syntax after your suggested change? It looks like model.materials[0][Albedo] where I would expect model.materials[0].maps[Albedo] as in C

greenfork avatar Jan 20 '22 03:01 greenfork

The second one! Actually for ModelAnimation.framePoses I used m.framePoses[i, j] because it's ptr UncheckedArray[ptr UncheckedArray[Transform]]

planetis-m avatar Jan 20 '22 14:01 planetis-m

It totally went over my head that `[]`, `[]=` both suffer from https://github.com/nim-lang/Nim/issues/19351 We need .copy pragma for procs as araq suggested.

planetis-m avatar Jan 22 '22 13:01 planetis-m

It totally went over my head that `[]`, `[]=` both suffer from nim-lang/Nim#19351 We need .copy pragma for procs as araq suggested.

Does .copy pragma imply that there will be an allocation and copy somewhere? I didn't see a comment from Araq on this topic

greenfork avatar Feb 05 '22 07:02 greenfork

.copy means the parameter type can't be a hidden pointer, there is a heuristic like sizeof(param) > 3*sizeof(float) that changes parameter types for non var proc parameter.

planetis-m avatar Feb 05 '22 10:02 planetis-m