2016年3月28日月曜日

LuaのC関数側で非ローカルデータを格納する場所

Lua実行環境で非ローカルデータ(呼び出しの外部に存在するデータ)を格納するための場所は、Lua関数/C関数でそれぞれ以下が存在する。

Lua関数
  • グローバル変数
  • 関数の環境
  • 上位値(クロージャー内に保持される非ローカル変数)
C関数
  • レジストリ
  • 環境 ※Lua 5.2より廃止
  • 上位値(クロージャー内に保持される非ローカル変数)

後者のC関数における格納場所に関する覚書。

レジストリ


Cコードからのみアクセス可能なグローバルテーブル。
複数のモジュール間で共有するデータを格納する際に利用する。

モジュール間でグローバルテーブル内の変数を参照する場合には、キー値を共有する必要がある。このキー値はモジュール間で共有するため、他の変数用のキーと重複しないように一意な値を指定する必要がある。

以下はサンプルコード(Luaスクリプトは全く使っていない。Cのコードのみ)。

LuaModule.dll/MathContext.h
#pragma once
#include "lua.hpp"

#ifdef LUADLL
#define DllExport __declspec( dllexport )
#else
#define DllExport __declspec( dllimport )
#endif

namespace LuaModule
{
 class DllExport MathContext
 {
 public:
  static const char* PI;

  MathContext(lua_State* L);
  virtual ~MathContext();

 private:
  MathContext();
 };
}

LuaModule.dll/MathContext.cpp
#include "MathContext.h"

namespace LuaModule
{
 const char* MathContext::PI = "LuaModule.MathContext.PI";

 MathContext::MathContext(lua_State* L)
 {
  lua_pushlightuserdata(L, (void *)&PI);
  lua_pushstring(L, "3.141592");
  lua_settable(L, LUA_REGISTRYINDEX);
 }

 MathContext::~MathContext()
 {
 }

 MathContext::MathContext()
 {
 }
}

LuaSample.exe/LuaSample.cpp
#include <iostream>
#include <process.h>
#include "lua.hpp"
#include "..\LuaModule\MathContext.h"

int main(int argc, char** argv)
{ 
 lua_State* L = luaL_newstate();
 luaL_openlibs(L);

 LuaModule::MathContext context(L);
 lua_pushlightuserdata(L, (void*)&context.PI);
 lua_gettable(L, LUA_REGISTRYINDEX);
 const char* pi = lua_tostring(L, -1);
 printf("PI is %s\n", pi);

 lua_close(L);

 system("pause");
 return 0;
}

DLL側でグローバルテーブル内にPIの値を格納し、EXE側でPIの値を参照し、表示している。
静的な変数のアドレスはCのリンカにより一意性を保証されているため、これをキー値として利用。

環境

単一のモジュール内で共有するデータを格納する際に利用。

Lua 5.2より、C の関数が環境を持たなくなったため、
疑似インデックス LUA_ENVIRONINDEXは廃止されている。
※Lua 5.2 リファレンスマニュアル「8.3 – API の変更」を参照。

上位値


特定の関数内でのみ可視なCの静的変数に相当する。
LuaでC関数を新しく作成するたびに、その関数を任意の個数の上位値に関連づける事ができる。
上位値へのアクセスは疑似インデックスを使用する。

#include <iostream>
#include <process.h>
#include "lua.hpp"

void createNewCounter(lua_State* L, int preset);
static int counter(lua_State* L);

int main(int argc, char** argv)
{ 
 const char* fileName = "C:\\Develop\\LuaScript\\Sample.lua";

 lua_State* L = luaL_newstate();
 luaL_openlibs(L);

 createNewCounter(L, 10);
 if (luaL_dofile(L, fileName))
 {
  printf("%s", lua_tostring(L, -1));
 }

 createNewCounter(L, 100);
 if (luaL_dofile(L, fileName))
 {
  printf("%s", lua_tostring(L, -1));
 }

 lua_close(L);
 system("pause");

 return 0;
}

void createNewCounter(lua_State* L, int preset)
{
 //. カウンターの初期値をスタックにPush(上位値として使用する)。
 lua_pushinteger(L, preset);
 //. クロージャーをスタックにPush。
 //. 第3引数は上位値の数。今回は上位値は1つのみ。
 lua_pushcclosure(L, &counter, 1);
 //. Pushしたクロージャーをグローバルテーブルに格納。
 lua_setglobal(L, "counter");
}

static int counter(lua_State* L)
{
 //. 予めPushされている上位値を取得。
 //. 上位値の取得はlua_upvalueindexを使用する。
 int val = lua_tointeger(L, lua_upvalueindex(1));
 //. 取得した値をインクリメントしてスタックへPush。
 //. この値を関数の呼び出し元(Luaのスクリプト)で使用する。
 lua_pushinteger(L, ++val);
 //. Pushされたインクリメント値を複製して再Push。
 lua_pushvalue(L, -1);
 //. 再Pushした値を上位値へ移動。
 //. 次回の関数呼び出しにおける上位値として使用される。
 lua_replace(L, lua_upvalueindex(1));
 return 1;
}

"Sample.lua"
print(counter())
print(counter())



0 件のコメント:

コメントを投稿