Reputation: 41
In most cases Im happy by the way SWIG is handling data, however Im facing an issue and cannot find an answer in the documentation.
First of all Im using SWIG with Lua and have the following structures wrapped:
typedef struct
{
%mutable;
float x,y,z;
...
...
} Vector3;
typedef struct
{
...
...
%immutable;
Vector3 gravity;
...
...
%extend
{
void SetGravity(Vector3 gravity)
{
WorldSetGravity($self,gravity);
}
};
} World;
As you can see the gravity XYZ can be affected by calling the SetGravity
function, and it work great.
However, in order to be more intuitive and easier to use, I would like to give the opportunity to the user to set component (XY or Z) independently like:
world.gravity.x=-10;
But I need to call in the background SetGravity
in order to be able to send the proper value to the physics engine (which is not exposed to Lua).
I would like to know if there’s a way to %extend
variables which will allow me to call SetGravity
when the world.gravity.xy or z
is called?
Or be able to implement my own version of the wrap function for each component like: _wrap_World_gravity_set_x
which will allot me to call SetGravity
in the background.
Upvotes: 1
Views: 437
Reputation: 88711
Firstly it's worth noting that this problem is harder than simply making a "virtual" member variable using %extend
that automatically calls an extra function when someone modifies it. This is because you want the fact that it's a member of another class to alter the behaviour.
I can see several fundamental approaches you could take to get this behaviour:
Vector3
inside World
to something that still looks and feels the same, but has the behaviour you want under the hood.Vector3
that checks the context it's being called from and modifies the behaviour accordingly.Of these #2 is my preferred solution because #1 is language specific (and I don't know Lua well enough to do it!) and #3 feels dirty from a software engineering perspective.
To implement #2 I did the following:
%module test
%{
#include "test.h"
%}
typedef struct
{
%mutable;
float x,y,z;
} Vector3;
%nodefaultctor Vector3Gravity;
%{
// Inside C this is just a typedef!
typedef Vector3 Vector3Gravity;
// But we have magic for sets/gets now:
#define MEMBER_VAR(ct,vt,rt,n) \
SWIGINTERN void ct##_##n##_set(ct *self, const vt val) { \
self->n = val; \
/* Need to find a way to lookup world here */ \
WorldSetGravity(world, self); \
} \
SWIGINTERN vt ct##_##n##_get(const ct *self) { return self->n; }
MEMBER_VAR(Vector3Gravity, float, Vector3, x)
MEMBER_VAR(Vector3Gravity, float, Vector3, y)
MEMBER_VAR(Vector3Gravity, float, Vector3, z)
%}
// Inside SWIG Vector3Gravity is a distinct type:
typedef struct
{
%mutable;
%extend {
float x,y,z;
}
} Vector3Gravity;
%typemap(memberin,noblock=1) Vector3Gravity gravity %{
$1 = *((const Vector3*)$input);
WorldSetGravity($self, $1); // This gets expanded to automatically make this call
%}
typedef struct
{
// This is a blatant lie!
Vector3Gravity gravity;
} World;
Essentially we're lying and claiming that the gravity
member of world is a "special" type, whereas really it's just a Vector3
. Our special type has two distinct features. Firstly sets/gets on its members are implemented a C code by us. Secondly when we set this member we automatically make an extra call rather than just pass the values in.
There are two things possibly missing from this example that you might want:
Vector3Gravity
to Vector3
. (As it stands anything other than the set for gravity will refuse to accept Vector3Gravity
instances). You can make that transparent by using the overload resolution mechanisms of SWIG/Lua if needed.Inside the setters for Vector3Gravity
we don't know which world this gravity belongs to.
We could solve that in several ways, the simplest being to implicitly set a static pointer every time we create a Vector3Gravity
. This would make sense if there only ever is one world.
Another approach would be to use a global map of Vector3Gravity
instances to worlds that gets maintained automatically.
Finally, instead of using a typedef
for the Vector3Gravity
type we could make it a real distinct type, with a compatible layout, but add a pointer to the World
it came from. That's more work though.
Upvotes: 1