SWIG%扩展变量

时间:2014-07-08 00:51:29

标签: lua swig

在大多数情况下,我对SWIG处理数据的方式感到高兴,但我遇到了一个问题,在文档中找不到答案。

首先,我将SWIG与Lua一起使用并包含以下结构:

typedef struct 
{ 
    %mutable; 
        float x,y,z; 
    ...
    ...

} Vector3; 


typedef struct 
{
    ...
    ...

    %immutable; 
        Vector3 gravity; 

    ...
    ...

    %extend 
    { 
        void SetGravity(Vector3 gravity) 
        { 
            WorldSetGravity($self,gravity); 
        } 
    }; 
} World; 

正如您所看到的那样,通过调用SetGravity函数可以影响XYZ的重力,并且效果很好。

但是,为了更直观和易于使用,我想让用户有机会独立设置组件(XY或Z),如:

world.gravity.x=-10; 

但我需要在后台SetGravity调用,以便能够将正确的值发送到物理引擎(不会暴露给Lua)。

我想知道是否有%extend个变量可以让我在调用SetGravity时调用world.gravity.xy or z

或者能够为每个组件实现我自己的wrap函数版本,例如:_wrap_World_gravity_set_x,它将分配给我在后台调用SetGravity

1 个答案:

答案 0 :(得分:1)

首先,值得注意的是,这个问题比简单地制作虚拟"更难。使用%extend的成员变量,当有人修改它时会自动调用额外的函数。这是因为您希望它是另一个类的成员来改变行为。

我可以看到您可以采取的几种基本方法来实现此行为:

  1. 在目标脚本语言中注入一些额外的代码来挂钩集合
  2. 在SWIG界面中注入一些额外的内容,以透明地将Vector3内部World内容转换为看起来和感觉相同但仍具有您想要的行为的内容。
  3. 将一些额外的代码注入Vector3的memberin类型映射中,以检查调用它的上下文并相应地修改行为。
  4. 其中#2是我首选的解决方案,因为#1是特定于语言的(并且我不太了解Lua!)从软件工程的角度来看,#3感觉很脏。

    为了实现#2,我做了以下事情:

    %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;
    

    基本上我们撒谎并声称世界gravity成员是特殊的"类型,而实际上它只是Vector3。我们的特殊类型有两个不同的特点。首先,我们为其成员设置/获取C代码。其次,当我们设置此成员时,我们会自动进行额外调用,而不是仅传递值。

    此示例中可能缺少两件您可能想要的东西:

    1. Vector3GravityVector3的透明转换。 (因为除了重力集之外的其他任何东西都会拒绝接受Vector3Gravity个实例)。如果需要,可以使用SWIG / Lua的重载解析机制使其透明。
    2. Vector3Gravity的制定者中,我们不知道这个引力属于哪个世界。

      我们可以通过多种方式解决这个问题,最简单的方法是每次创建Vector3Gravity时隐式设置静态指针。如果只有一个世界,这将是有意义的。

      另一种方法是将Vector3Gravity个实例的全局地图用于自动维护的世界。

      最后,我们可以将typedef用于Vector3Gravity类型,而不是使用World类型,而是使用兼容的布局,但是添加一个指向它的{{1}}的指针。但是,这还有更多的工作。