Forum: Poser Python Scripting


Subject: Python Extension (.pyd) Call-Behavior Recommendations?

Spanki opened this issue on Mar 24, 2008 · 7 posts


Spanki posted Mon, 24 March 2008 at 4:22 PM

Part II - New Instance vs. Pointer / Reference

My second question has to do with how various data/objects get returned from my new 'type' implementations... for example, here are two new types implemented by the extension...

Type:    <VectorType><br></br>
Members:<br></br>
x  -  <FloatType>   ( aliases: 
vec.u, vec.r, vec.wgt0, vec[0] )<br></br>
y  -  <FloatType>   ( aliases: 
vec.v, vec.g, vec.wgt1, vec[1] )<br></br>
z  -  <FloatType>   ( aliases: 
vec.w, vec.b, vec.wgt2, vec[2] )<br></br><br></br>
Type:    <TriPolyType><br></br>
Members:<br></br>
v0           
- <IntType>   ( triangle vertex
indices... )<br></br>
v1           
- <IntType><br></br>
v2            - <IntType><br></br>
uv0          
- <IntType>   ( triangle texture vertex
indices... )<br></br>
uv1          
- <IntType> <br></br>
uv2          
- <IntType><br></br>
polyIndex     -
<IntType>    ( index of ngon that spawned
the tripoly )<br></br>
triangleIndex - <IntType>    ( index of
triangle within the above ngon )<br></br>
plane         -
<FloatType>  ( pre-computed plane equation )<br></br>
normal        -
<VectorType> ( face normal vector )<br></br>

...Note that internally, my code doesn't store pointers to Python Objects for simple types like ints or floats, but for complex data types (like the 'normal' member of the TriPolyType), it does (incrementing and decrementing the reference counts as needed).

The next thing to note is that, as a 'type' implementation / extension, my C code is basically a 'handler' for any data of the new type, so the Python interpreter calls some routine in my code any time it needs to get info about or operate on my new type.  So let's look at some simple example code...

verts = [Vector(0.0, 0.0, 0.0) for i in range(3)]  #
create 3 new vectors with not-very-useful positions<br></br>
norm = Vector(0.0, 1.0, 0.0)  # psuedo normal vector,
pointing up (maybe down :) )<br></br>
tp = TriPoly(0, 1,
2)         # create a
new tripoly,with 0/1/2 indices - we'll fill in the normal
afterwards<br></br>
tp.normal = norm<br></br>

...ok, so most of the actual values being used above psuedo code are meaningless (all 3 vertices or the tripoly would be at 0.0, etc), so I just wanted some 'structure' to talk about :).  Given the above, if we do:

ndx0 = tp.v0

...then what gets returned from my handler is a 'new' Python Object, with the value set to whatever I had stored in the v0 member ('0', in this case).  However, if you do:

newnorm = tp.normal

...currently, what my code does it bump the reference count on the normal member (a pointer to a Python Object of type (which my code also happens to implement, but it could be a list or some other type)) and returns the object as a pointer/reference to the one being stored in the internal tripoly structure.

It's only recently occured to me that, while handy in some cases, it might also be the cause of some hard to track down bugs in people's python scripts.  Consider the following...

newnorm.y = -1.0

...now, not only does the script's local 'newnorm' varible have it's y axis set to -1.0, but the normal stored in "tp.normal" also got changed (and this is a pretty simplistic example.. that tripoly might well be some nth index into a larger list of them, which might be part of an even larger mesh-type structure, etc).

My new class does provide a way to work-around this particular issue, using it's .clone() method...

newnorm = tp.normal.clone()

...that would end up with a new instance/copy of the normal, instead of a pointer to the existing one, but I'm fairly fast closing in on the decision/opinion to just always return a new instance/copy of the normal, instead of a pointer/reference.

Thoughts / Comments?

Thanks,

Keith

Cinema4D Plugins (Home of Riptide, Riptide Pro, Undertow, Morph Mill, KyamaSlide and I/Ogre plugins) Poser products Freelance Modelling, Poser Rigging, UV-mapping work for hire.