Luaパッケージ&C++実践(一)-LuaとC/C++の基本的なインタラクション
31303 ワード
Luaはコンパクトなスクリプト言語で、それ自体が埋め込みスクリプトとして設計されており、現在のすべてのスクリプトエンジンの中でLuaの速度が最も速い.また、その解釈器は非常に軽量で、その解釈器は200 kにすぎない(バージョンによってはわずかに異なる可能性がある).
Luaプロジェクトには多くの技術点が含まれており、時間をかけて研究することで多くの収穫が得られ、多くのことを学ぶことができます.ホスト言語とのインタラクション、メモリ管理、仮想マシン実装、コラボレーション、クローズドパッケージ、異常キャプチャメカニズムなどが含まれ、その後、徐々に検討する時間があります.
本シリーズは主にLuaパッケージ関連ノートを記録し、主にC++11関連学習と実践を記録する.Luaに関する原理は分からないので、メモの中でもしばらく言わないで、後でLuaを深く勉強する時間があるときは、メモを取ります.
LuaとC/C++のインタラクションベース
LuaとC/C++言語通信の主な方法はLua先進後出(FILO)の仮想スタックである.Luaでは、Luaスタックはstructであり、スタックインデックスの方式は正数でも負数でもよい.違いは、正数インデックス1はスタックの底を永遠に表し、負数インデックス-1はスタックの頂を永遠に表す.
Luaの使用は、Luaのステートマシン
LuaとC/C++のインタラクションを行う場合、メソッド、パラメータ、戻り値はスタックに圧入する必要があります.後で例で説明します.
C/C++呼び出しLua
C/C++呼び出しLuaは比較的簡単であるが、C++を使用する場合、Luaヘッダファイルを導入する際には、
Luaファイルが必要です
C/C++でLuaを呼び出すには、次の手順に従います.
Lua呼び出しC
Lua呼び出しCは、ライブラリを登録し、luaにライブラリをロードすることによって呼び出すこともできるし、直接関数スタックを押すことによって呼び出すこともできる.ここに記録されているのは関数スタックの方式です.
ここでは、呼び出されるC関数を用意します.
C関数が呼び出され,luaのルールに合致する必要がある.前述したように、CとLuaの相互変調はスタックによって行われ、Lua解釈器はLuaで呼び出された関数を解釈する際にも、関数名、関数パラメータなどによってスタックと相互作用する.したがって、上記
Luaはスクリプトを埋め込むだけで、その実行はホストプログラムに依存するので、私たちはやはりCコードを書いてluaを実行する必要があります.そして、上記はC関数を用意しただけですが、このC関数はLuaステータスマシンを通じてluaと連絡を取っていません.
CとLuaの相互呼び出しはこのように実は比較的に理解しやすくて、Cの関数をスタックの中に入れてLuaに呼び出されますか、それともluaの中の関数がluaに呼び出されますか、少なくとも表現の上で、区別がなくて、すべて実行する前に方法をスタックに押し込んで、実行する時、名前によって、正しい関数を見つけてスタックの上に置いて、それからパラメータを押し込んで、関数を実行して、スタックの上部から返される結果を取得します.
Lua呼び出しC++
これは相対的に面倒で、上のLuaがCを呼び出したとき、私たちはlua_を知っていました.Pushcfunctionには、固定フォーマットのC関数しか入力できません.LuaがC++を呼び出すことができ、C++でクラスを使用する方法で呼び出すには、C++コードをより多くの処理をしてluaの要求を満たす必要があります.実際、Luaのuserdataを巧みに使うことで、私たちのさまざまなニーズを満たすことができます.
ここでの例では、実際にはuserdata+metatableによってC++からLuaへのマッピングが実現され、C++クラスがLuaのtableにマッピングされる.ここでmetatableはLuaにあり,tableの挙動を変えることができる.Luaでは,各挙動に対応するメタメソッドが関連付けられている.一般的なメタメソッドは、次のとおりです.
tableにメタテーブルを設定すると、tableに対して操作を実行すると、メタテーブルの定義に従って実行されます.たとえば、tableにメタテーブルが設定され、メタテーブルに__が実現されます.indexメタメソッドはtable.xxxとtable:xxxは先に実行します_indexメソッド、indexは何をすべきかを決めます.
我々は,Luaのこの特性を用いて,C++の呼び出しを実現する.
まず、最終的に期待しているLuaコードを書きます.
C++のクラスと、呼び出したC関数を準備します.
次に、C++とLuaのつながりを確立する必要があります.解釈もコード注釈に直接与えられます.
これで、LuaとC/C++の基本的な相互呼び出しは完了しましたが、このように使うとどうしても面倒な感じがします.私はただ簡単なインタラクションをしたいだけで、こんなに多くのことをしなければなりません.何気なく苦痛なので、後でこの複雑な呼び出しをパッケージ化し、LuaとC++のインタラクションをもっと簡単にします.
その他
メモ関連のコードはGithub上ではコードが変動し続け、必要なものがあれば対応する提出を直接見ることができます.このブログは個人学習ノートや興味のある友人の参考としてのみ使用され、謙虚にアドバイスや指摘を受け、ツッコミや批判を受けず、デザイン思想やコードを引用して出典を明記したいので、ForkとStarを歓迎します.wLuaBindコードアドレス
転載を歓迎します.転載は文章の出所を残してください.湖広午王のブログ[http://blog.csdn.net/junzia/article/details/95001209]
Luaプロジェクトには多くの技術点が含まれており、時間をかけて研究することで多くの収穫が得られ、多くのことを学ぶことができます.ホスト言語とのインタラクション、メモリ管理、仮想マシン実装、コラボレーション、クローズドパッケージ、異常キャプチャメカニズムなどが含まれ、その後、徐々に検討する時間があります.
本シリーズは主にLuaパッケージ関連ノートを記録し、主にC++11関連学習と実践を記録する.Luaに関する原理は分からないので、メモの中でもしばらく言わないで、後でLuaを深く勉強する時間があるときは、メモを取ります.
LuaとC/C++のインタラクションベース
LuaとC/C++言語通信の主な方法はLua先進後出(FILO)の仮想スタックである.Luaでは、Luaスタックはstructであり、スタックインデックスの方式は正数でも負数でもよい.違いは、正数インデックス1はスタックの底を永遠に表し、負数インデックス-1はスタックの頂を永遠に表す.
Luaの使用は、Luaのステートマシン
lua_State
に依存し、Luaのスタックもステートマシンに存在する.ここのステータスマシンは、Javaのjvmに似ていて、luaを解析、実行する礎です.LuaがC/C++を呼び出すにしても、C/C++がLuaを呼び出すにしても、Aがデータをスタック、Bがデータをスタックから取り出し、ここのデータはメタデータでもアドレスでもある可能性があります.LuaとC/C++のインタラクションを行う場合、メソッド、パラメータ、戻り値はスタックに圧入する必要があります.後で例で説明します.
C/C++呼び出しLua
C/C++呼び出しLuaは比較的簡単であるが、C++を使用する場合、Luaヘッダファイルを導入する際には、
#include "lua.hpp"
、lua.hppの内容は,extern cを用いてコンパイラに通知し,C Linkage方式でコンパイル,すなわちC++のname manglingメカニズムを抑制する.コンパイルエラーが発生します.extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
Luaファイルが必要です
function helloAdd(num1, num2)
return (num1 + num2)
end
C/C++でLuaを呼び出すには、次の手順に従います.
void testCCallLua(){
int ret;
// : Lua
lua_State * l = luaL_newstate();
// : 。 , 。 luaopen_xxx(l)
luaL_openlibs(l);
// : ( ) lua ,lua main 。 、table、 ,
ret = luaL_dofile(l, "../res/test1.lua");
std::cout<<"doFile : "<< ret<< std::endl;
// : lua ,
ret = lua_getglobal(l, "helloAdd");
std::cout<<"getFunction : "<< ret<< std::endl;
// :
lua_pushnumber(l, 10);
lua_pushnumber(l, 5);
// : , , , lua
lua_call(l, 2, 1);
// : , , 。 , , 。 lua_pop
double iResult = lua_tonumber(l, -1);
std::cout<<"result:" << iResult << std::endl;
lua_pop(l,1);
// : ,
lua_close(l);
l = nullptr;
}
Lua呼び出しC
Lua呼び出しCは、ライブラリを登録し、luaにライブラリをロードすることによって呼び出すこともできるし、直接関数スタックを押すことによって呼び出すこともできる.ここに記録されているのは関数スタックの方式です.
ここでは、呼び出されるC関数を用意します.
// C
double cFuncAdd(double a, double b){
return a+b;
}
// C , Lua ,
//
int luaBindCFuncAdd(lua_State * l){
double a = luaL_checknumber(l, 1);
double b = luaL_checknumber(l, 2);
lua_pushnumber(l, cFuncAdd(a, b));
return 1;
}
C関数が呼び出され,luaのルールに合致する必要がある.前述したように、CとLuaの相互変調はスタックによって行われ、Lua解釈器はLuaで呼び出された関数を解釈する際にも、関数名、関数パラメータなどによってスタックと相互作用する.したがって、上記
luaBindCFuncAdd
においても、実際の実行はスタックから2つのパラメータを順次取り出し、C関数実行を呼び出し、結果をスタックに押し込み、luaはスタックから結果を得る.Luaはスクリプトを埋め込むだけで、その実行はホストプログラムに依存するので、私たちはやはりCコードを書いてluaを実行する必要があります.そして、上記はC関数を用意しただけですが、このC関数はLuaステータスマシンを通じてluaと連絡を取っていません.
void testLuaCallC(){
std::cout << "start test Lua Call C --------------------- " << std::endl;
// , , lua
lua_State * l = luaL_newstate();
luaL_openlibs(l);
// C , lua_State, int 。
lua_pushcfunction(l,luaBindCFuncAdd);
// C lua getglobal,
lua_setglobal(l,"cFuncAdd");
// lua , lua ,
int ret = luaL_dostring(l,"print('cFuncAdd ret :', cFuncAdd(98,9))");
std::cout<<"doFile:" << ret << std::endl;
lua_close(l);
}
CとLuaの相互呼び出しはこのように実は比較的に理解しやすくて、Cの関数をスタックの中に入れてLuaに呼び出されますか、それともluaの中の関数がluaに呼び出されますか、少なくとも表現の上で、区別がなくて、すべて実行する前に方法をスタックに押し込んで、実行する時、名前によって、正しい関数を見つけてスタックの上に置いて、それからパラメータを押し込んで、関数を実行して、スタックの上部から返される結果を取得します.
Lua呼び出しC++
これは相対的に面倒で、上のLuaがCを呼び出したとき、私たちはlua_を知っていました.Pushcfunctionには、固定フォーマットのC関数しか入力できません.LuaがC++を呼び出すことができ、C++でクラスを使用する方法で呼び出すには、C++コードをより多くの処理をしてluaの要求を満たす必要があります.実際、Luaのuserdataを巧みに使うことで、私たちのさまざまなニーズを満たすことができます.
ここでの例では、実際にはuserdata+metatableによってC++からLuaへのマッピングが実現され、C++クラスがLuaのtableにマッピングされる.ここでmetatableはLuaにあり,tableの挙動を変えることができる.Luaでは,各挙動に対応するメタメソッドが関連付けられている.一般的なメタメソッドは、次のとおりです.
__index // table table
__gc //table ,
__newindex // , __index ,__index ,
// , 。
__add、__sub、__mul、__div、__mod、__unm、__concat、__eq、__lt、__le
__call // Lua
__tostring // , java tostring
tableにメタテーブルを設定すると、tableに対して操作を実行すると、メタテーブルの定義に従って実行されます.たとえば、tableにメタテーブルが設定され、メタテーブルに__が実現されます.indexメタメソッドはtable.xxxとtable:xxxは先に実行します_indexメソッド、indexは何をすべきかを決めます.
我々は,Luaのこの特性を用いて,C++の呼び出しを実現する.
まず、最終的に期待しているLuaコードを書きます.
-- , ,
operate = OperateCpp()
print("OperateCpp:multiply ret : ", operate:multiply(5.0,12.0))
print("OperateCpp.errorCode is :", operate.errorCode)
C++のクラスと、呼び出したC関数を準備します.
class OperateCpp{
private:
double x{};
double y{};
int type{};
public:
int errorCode = -1;
OperateCpp() = default;
~OperateCpp(){
std::cout<<"OperateCpp destroy"<<std::endl;
}
double multiply(double x, double y){
return x * y;
}
};
//
static int LuaCreateOperateCpp(lua_State * l){
//Lua C++ , Lua userdata
// , , , , , userdata。 ,
auto ** pData = (OperateCpp**)lua_newuserdata(l, sizeof(OperateCpp*));
// userdata , C++
*pData = new OperateCpp();
// OperateCpp , 。 , 。
// , , 。
luaL_getmetatable(l, "OperateCpp");
// userdata , -2, new ,-1
lua_setmetatable(l, -2);
return 1;
}
//
static int LuaDestroyOperateCpp(lua_State* L){
//
delete *(OperateCpp**)lua_topointer(L, 1);
return 0;
}
//
static int LuaFuncMultiply(lua_State * l){
auto * oc = *(OperateCpp **)lua_topointer(l, 1);
auto x = lua_tonumber(l,2);
auto y = lua_tonumber(l,3);
auto ret = oc->multiply(x,y);
lua_pushnumber(l,ret);
return 1;
}
// , __index
static int LuaCallIndex(lua_State * l){
auto * oc = *(OperateCpp **)lua_topointer(l, 1);
auto filed = lua_tostring(l,2);
if(strcmp(filed,"errorCode") == 0){
lua_pushnumber(l, oc->errorCode);
}else if(strcmp(filed, "multiply") == 0){
lua_pushcfunction(l, LuaFuncMultiply);
}
return 1;
}
次に、C++とLuaのつながりを確立する必要があります.解釈もコード注釈に直接与えられます.
void testLuaCallCpp(){
std::cout << "start test Lua Call Cpp --------------------- " << std::endl;
lua_State * l = luaL_newstate();
luaL_openlibs(l);
// , OperateCpp OperateCpp , OperateCpp, Lua OperateCpp()
// , table,
lua_pushcfunction(l,LuaCreateOperateCpp);
lua_setglobal(l,"OperateCpp");
// , , ,
// , ,
luaL_newmetatable(l, "OperateCpp");
// __gc , table,
lua_pushstring(l,"__gc");
lua_pushcfunction(l,LuaDestroyOperateCpp);
// metatable ,
lua_settable(l, -3);
// __index , table,
lua_pushstring(l, "__index");
lua_pushcfunction(l,LuaCallIndex);
lua_settable(l,-3);
std::string content = loadString("../res/test2.lua");
int ret = luaL_dostring(l,content.c_str());
std::cout<<"doFile:" << ret << std::endl;
lua_close(l);
}
これで、LuaとC/C++の基本的な相互呼び出しは完了しましたが、このように使うとどうしても面倒な感じがします.私はただ簡単なインタラクションをしたいだけで、こんなに多くのことをしなければなりません.何気なく苦痛なので、後でこの複雑な呼び出しをパッケージ化し、LuaとC++のインタラクションをもっと簡単にします.
その他
メモ関連のコードはGithub上ではコードが変動し続け、必要なものがあれば対応する提出を直接見ることができます.このブログは個人学習ノートや興味のある友人の参考としてのみ使用され、謙虚にアドバイスや指摘を受け、ツッコミや批判を受けず、デザイン思想やコードを引用して出典を明記したいので、ForkとStarを歓迎します.wLuaBindコードアドレス
転載を歓迎します.転載は文章の出所を残してください.湖広午王のブログ[http://blog.csdn.net/junzia/article/details/95001209]