0

LuaのCAPIを使用してLuaを拡張しています。私のモジュールでは、を使用してテーブルにデータを入力し、を使用しluaL_refてフィールドを削除したいと思いますluaL_unref。また、できればを使用して、このテーブルを反復処理できるようにしたいと思いますlua_next

テーブルの反復は、のために問題になりluaL_unrefます。Luaでは、割り当てることによってテーブルフィールドを「削除」するのが一般的ですnil(初期化されていないテーブルフィールドはに評価されるためnil)。このnext関数は、スキップするのに十分スマートnilです。luaL_unref参照されていないテーブルフィールドに割り当てることを期待しnilていましたが、整数を割り当てているようです。この整数の値は文書化されていないようです。

次のコードを検討してください。

/* tableDump prints a table: */
/* key: value, key: value, ... */

lua_newtable(L);

lua_pushboolean(L, 0);
int ref1 = luaL_ref(L, -2);

lua_pushinteger(L, 7);
int ref2 = luaL_ref(L, -2);

lua_pushstring(L, "test");
int ref3 = luaL_ref(L, -2);

tableDump(L, -1);

luaL_unref(L, -1, ref1);
tableDump(L, -1);

luaL_unref(L, -1, ref3);
tableDump(L, -1);

luaL_unref(L, -1, ref2);
    tableDump(L, -1);

printf("done.\n");

出力:

1:  false,  2:  7,  3:  `test',  
3:  `test',  2:  7,  0:  1,  
3:  1,  2:  7,  0:  3,  
3:  1,  2:  3,  0:  2,  
done.

何が起きてる?どうすればこれを回避できますか?参照を繰り返し処理し、参照されていないものを無視するためのトリックはありますか?との使用をやめる必要がluaL_refありluaL_unrefますか?


編集

まず、ご回答ありがとうございます!

多分私は間違った質問をしました。

もう少し具体的にさせてください。多くのサブスクリプションユーザーデータを管理する必要があるクライアントユーザーデータがあります。サブスクリプションは、クライアントのサブスクライブ方法によって作成されます。サブスクリプションは、クライアントのサブスクリプション解除方法によって削除されます。サブスクリプションのユーザーデータは基本的に実装の詳細であるため、クライアントAPIでは公開されません。代わりに、クライアントAPIはサブスクリプション参照を使用するため、を使用しluaL_refてサブスクリプションテーブルにデータを入力します。

ref = client:sub(channel, func)
cleint:unsub(ref)

これがキャッチです。クライアントが__gcの残りのすべてのサブスクリプションを自動的にサブスクリプション解除するようにします(そうしないと、ユーザーはセグメンテーション違反になります)。したがって、サブスクリプションを繰り返す必要があるようです。私は本当にここでAPIを悪用していますか?これを行うためのより良い方法はありますか?

4

2 に答える 2

3

および関数はで定義されていluaL_refます。彼らは、彼らが操作しているテーブルに保存している無料の参照リストを追跡することによって機能します。これらの関数は比較的短いので、ここに含めます。luaL_unreflauxlib.c

LUALIB_API int luaL_ref (lua_State *L, int t) {
  int ref;
  t = abs_index(L, t);
  if (lua_isnil(L, -1)) {
    lua_pop(L, 1);  /* remove from stack */
    return LUA_REFNIL;  /* 'nil' has a unique fixed reference */
  }
  lua_rawgeti(L, t, FREELIST_REF);  /* get first free element */
  ref = (int)lua_tointeger(L, -1);  /* ref = t[FREELIST_REF] */
  lua_pop(L, 1);  /* remove it from stack */
  if (ref != 0) {  /* any free element? */
    lua_rawgeti(L, t, ref);  /* remove it from list */
    lua_rawseti(L, t, FREELIST_REF);  /* (t[FREELIST_REF] = t[ref]) */
  }
  else {  /* no free elements */
    ref = (int)lua_objlen(L, t);
    ref++;  /* create new reference */
  }
  lua_rawseti(L, t, ref);
  return ref;
}

LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
  if (ref >= 0) {
    t = abs_index(L, t);
    lua_rawgeti(L, t, FREELIST_REF);
    lua_rawseti(L, t, ref);  /* t[ref] = t[FREELIST_REF] */
    lua_pushinteger(L, ref);
    lua_rawseti(L, t, FREELIST_REF);  /* t[FREELIST_REF] = ref */
  }
}

これらの関数は、(操作しているテーブル以外に)追加のストレージを必要としないため、非常に便利です。ただし、参照値を反復処理する必要がある場合など、参照値と同じテーブルに空き参照リストを格納することが望ましくない場合があります。

私はこの問題を解決するために書きましluaX_refluaX_unrefそれらは、リスト用に別のテーブルに無料の参照リストを格納することを除いて、luaL_refおよびとほぼ同じように機能します。(私のプロジェクトでは、これらを別のソースファイルに入れ、必要に応じて含めました。変更することはお勧めしません。)luaL_unrefllauxlib.c

static int abs_index(lua_State * L, int i){
  return i > 0 || i <= LUA_REGISTRYINDEX ? i : lua_gettop(L) + i + 1;
}

LUALIB_API int luaX_ref(lua_State *L, int t, int l){
  int ref;
  t = abs_index(L, t);
  l = abs_index(L, l);
  if(lua_isnil(L, -1)){
    lua_pop(L, 1); /* remove from stack */
    return LUA_REFNIL; /* 'nil' has a unique fixed reference */
  }
  lua_rawgeti(L, l, FREELIST_REF); /* get first free element */
  ref = (int) lua_tointeger(L, -1); /* ref = l[FREELIST_REF] */
  lua_pop(L, 1); /* remove it from stack */
  if(ref != 0){ /* any free element? */
    lua_rawgeti(L, l, ref); /* remove it from list */
    lua_rawseti(L, l, FREELIST_REF); /* (l[FREELIST_REF] = l[ref]) */
  }else{ /* no free elements */
    ref = (int)lua_objlen(L, l);
    ref++; /* create new reference */
  }
  lua_pushboolean(L, 1);
  lua_rawseti(L, l, ref); /* l[ref] = true */
  lua_rawseti(L, t, ref); /* t[ref] = value */
  return ref;
}

LUALIB_API void luaX_unref(lua_State *L, int t, int l, int ref){
  if(ref >= 0){
    t = abs_index(L, t);
    l = abs_index(L, l);
    lua_rawgeti(L, l, FREELIST_REF);
    lua_rawseti(L, l, ref);  /* l[ref] = l[FREELIST_REF] */
    lua_pushinteger(L, ref);
    lua_rawseti(L, l, FREELIST_REF);  /* l[FREELIST_REF] = ref */
    lua_pushnil(L);
    lua_rawseti(L, t, ref); /* t[ref] = nil */
  }
}

次に、使用法を確認します。

lua_newtable(L); /* 1 */
lua_newtable(L); /* 2 */

lua_pushboolean(L, 0);
int ref1 = luaX_ref(L, 1, 2);

lua_pushinteger(L, 7);
int ref2 = luaX_ref(L, 1, 2);

lua_pushstring(L, "test");
int ref3 = luaX_ref(L, 1, 2);

tableDump(L, 1);
tableDump(L, 2);

luaX_unref(L, 1, 2, ref1);
tableDump(L, 1);
tableDump(L, 2);

luaX_unref(L, 1, 2, ref3);
tableDump(L, 1);
tableDump(L, 2);

luaX_unref(L, 1, 2, ref2);
tableDump(L, 1);
tableDump(L, 2);

printf("done.\n");

出力:

1:  false,  2:  7,  3:  `test',
1:  true,  2:  true,  3:  true,
2:  7,  3:  `test',
3:  true,  2:  true,  0:  1,
2:  7,
3:  1,  2:  true,  0:  3,

3:  1,  2:  3,  0:  2,
done.
于 2012-10-09T01:37:17.563 に答える
2

「返されるキーの一意性を保証する」ためにはluaL_ref、使用されたキーとその後にluaL_unref削除されたキーのリストを維持する必要があります。このリストはで始まりt[0]、インデックスのチェーンがにつながるまで続くようnilです。このリストは、アクティブな参照と同じテーブルに保持されます。

Nicolが観察したように、引き続き「APIの悪用」を行い、実装で定義された動作に依存する場合は、このリンクリストに従って、テーブルを反復処理するときにキーが削除された参照であるかどうかを確認できます。または、実装で定義された動作への依存を回避し、パフォーマンスを向上させるために、削除された参照の別のテーブルを保持し、テーブルを反復するときにそれらをスキップできますが、のリストヘッドを無視する必要がありますt[0]

本当に参照を繰り返す必要がある場合は、まったく別のメカニズムを使用する方がよい場合があります。すべての参照と値のペアを別のテーブルに配置し、別のテーブルの値をnil参照を削除するときに設定するだけです。次に、別のテーブルを単純に繰り返すことができます。

于 2012-10-07T19:41:08.080 に答える