pointersluaswiglua-tabletypemaps

Proper way to free a pointer array in SWIG input typemap?


Hi I'm trying to wrap the following function using SWIG.

static void readTable(int argc, t_atom *argv) { //accepts table in Lua e.g. readTable({"ab",3});

        for (int i=0; i<argc; ++i) {

            if (argv[i].a_type == A_FLOAT)
                printf("FLOAT : %g\n", argv[i].a_w.w_float);
            else if (argv[i].a_type == A_SYMBOL)
                printf("SYMBOL : %s\n", argv[i].a_w.w_symbol->s_name);
        }
    }

Here's the typemap I created.

%include "exception.i"
%typemap(in) (int argc, t_atom *argv)
{
    if (!lua_istable(L, 1)) {
      SWIG_exception(SWIG_RuntimeError, "argument mismatch: table expected");
    }
    lua_len(L, 1);
    $1 = lua_tointeger(L, -1);
    $2 = (t_atom *)getbytes($1 * sizeof(t_atom)); //internally calls calloc()

    for (int i=0; i<$1; ++i) {

        lua_pushinteger(L, i+1);
        lua_gettable(L, 1);

        if(lua_isnumber(L, -1)) {

            $2[i].a_type = A_FLOAT;
            $2[i].a_w.w_float = lua_tonumber(L, -1);
        }          
        else if(lua_isstring(L, -1)) {

            $2[i].a_type = A_SYMBOL;
            $2[i].a_w.w_symbol = gensym(lua_tostring(L, -1));
        }
    }
    freebytes($2, $1 * sizeof(t_atom)); //internally calls free()
}

As you can see, I allocate memory for an array using getbytes() which internally calls calloc() and free the memory using freebytes() which internally calls free().

Surprisingly, when I call readTable() function in Lua, it works fine without crashing.

for example, calling test.readTable({3, "abc"}) in Lua prints the following result.

FLOAT : 3
SYMBOL : abc

My questions are

a) How can readTable() print results without crashing when I freed the memory in SWIG interface? Maybe memory is not being properly freed?

b) If a) doesn't make sense or is not safe to use, would you recommend calling the freebytes() inside the function readTable() instead so it can be like the following?

static void readTable(int argc, t_atom *argv) {

        for (int i=0; i<argc; ++i) {

            if (argv[i].a_type == A_FLOAT)
                post("FLOAT : %g", argv[i].a_w.w_float);
            else if (argv[i].a_type == A_SYMBOL)
                post("SYMBOL : %s", argv[i].a_w.w_symbol->s_name);
        }
        freebytes(argv, argc * sizeof(t_atom));
    }

Solution

  • That is exactly the situation which %typemap(freearg) is for.

    %include "exception.i"
    
    %typemap(in) (int argc, t_atom *argv) {
        if (!lua_istable(L, $input)) {
            SWIG_exception(SWIG_RuntimeError, "argument mismatch: table expected");
        }
        lua_len(L, $input);
        $1 = lua_tointeger(L, -1);
        $2 = (t_atom *)getbytes($1 * sizeof(t_atom)); // internally calls calloc()
    
        for (int i = 0; i < $1; ++i) {
    
            lua_pushinteger(L, i + 1);
            lua_gettable(L, $input);
    
            if (lua_isnumber(L, -1)) {
                $2[i].a_type = A_FLOAT;
                $2[i].a_w.w_float = lua_tonumber(L, -1);
            } else if (lua_isstring(L, -1)) {
                $2[i].a_type = A_SYMBOL;
                $2[i].a_w.w_symbol = gensym(lua_tostring(L, -1));
            } else {
                SWIG_exception(SWIG_RuntimeError, "unhandled argument type");
            }
        }
    }
    
    %typemap(freearg) (int argc, t_atom *argv) {
        freebytes($2, $1 * sizeof(t_atom)); // internally calls free()
    }
    
    static void readTable(const std::string &name, int argc, t_atom *argv);
    

    This is the generated code from SWIG 3.0

    static int _wrap_readTable(lua_State* L) {
      int SWIG_arg = 0;
      std::string *arg1 = 0 ;
      int arg2 ;
      t_atom *arg3 = (t_atom *) 0 ;
    
      SWIG_check_num_args("readTable",2,2)
      if(!lua_isuserdata(L,1)) SWIG_fail_arg("readTable",1,"std::string const &");
    
      if (!SWIG_IsOK(SWIG_ConvertPtr(L,1,(void**)&arg1,SWIGTYPE_p_std__string,0))){
        SWIG_fail_ptr("readTable",1,SWIGTYPE_p_std__string);
      }
    
      {
        if (!lua_istable(L, 2)) {
          SWIG_exception(SWIG_RuntimeError, "argument mismatch: table expected");
        }
        lua_len(L, 2);
        arg2 = lua_tointeger(L, -1);
        arg3 = (t_atom *)getbytes(arg2 * sizeof(t_atom)); // internally calls calloc()
    
        for (int i = 0; i < arg2; ++i) {
          lua_pushinteger(L, i + 1);
          lua_gettable(L, 2);
    
          if (lua_isnumber(L, -1)) {
            arg3[i].a_type = A_FLOAT;
            arg3[i].a_w.w_float = lua_tonumber(L, -1);
          } else if (lua_isstring(L, -1)) {
            arg3[i].a_type = A_SYMBOL;
            arg3[i].a_w.w_symbol = gensym(lua_tostring(L, -1));
          } else {
            SWIG_exception(SWIG_RuntimeError, "unhandled argument type");
          }
        }
      }
      readTable((std::string const &)*arg1,arg2,arg3);
    
      {
        freebytes(arg3, arg2 * sizeof(t_atom)); // internally calls free()
      }
      return SWIG_arg;
    
      if(0) SWIG_fail;
    
    fail:
      {
        freebytes(arg3, arg2 * sizeof(t_atom)); // internally calls free()
      }
      lua_error(L);
      return SWIG_arg;
    }