D言語用のLua API

まえがき



この音符はあまり大きくありませんが、むしろ逆に小さくなります。



かなり長い間、私はHabréで発行されたD言語に関する一連の記事をフォローしてきました 。 ウィキペディアから始めて、この言語の公式マニュアルで終わる多くのソースに精通して、私はあなたの研究プロジェクトでそれを使用することが望ましいという結論に達しました。 博士論文の主なプロジェクトは停止し、処理が必要になりました(多くの機械的な問題が表面化しました)。 プロジェクトの処理と新しい言語の学習を組み合わせることが決定されました。



とは言っても、ほとんどのコードはC / C ++からDに非常に迅速に移植されました。ソフトウェア開発者の間でDについて意見が異なるにもかかわらず、この言語は私の好みになりました。



1つの問題-古いバージョンのプロジェクトでは、Luaスクリプトを使用して、列車モデルのパラメータを設定し、再コンパイルせずに作業のロジックを変更しました。 職業によってそれに遭遇する人は、C / C ++で開発するための十分に開発され、十分に文書化されたAPIがあることを知っています。



DでのLuaスクリプトについては、 DuプログラムでLuaを操作する機能を導入するLuaDなど、いくつかのプロジェクトがあります。 ただし、LuaDは以前のバージョン5.1用に設計されています。 5.2のプロジェクト-DerelictLuaに出会いましたが、「簡単なキック」でそれを実現することはできませんでした。 時間があれば、それを把握できますが、いつものように時間はありません。 私は精神力に負担をかけ、より速く簡単な解決策を考え出さなければなりませんでした。 読者がこの結果に興味を持っているなら、猫へようこそ。



1. D上のプログラムからC関数を呼び出す



これは単純にではなく、非常に単純に実現されます-モジュールDで関数プロトタイプを作成し、外部モジュールに配置され、関数の呼び出しにC規約を使用することを示します

extern(C) double my_C_func(int param1, double param2);
      
      





当然、外部モジュールは何らかの方法でDのプログラムとリンクする必要があります。



この点で、最初のアイデアが浮かびました-CでLuaを使用して作業を実装し、Dでモジュールにプロトタイプを記述し、すべてを1つのプログラムにコンパイルします。 プロジェクト構築スクリプト(SConsを使用)は、この方法でやり直されました



C / C ++およびDのモジュールからプログラムをコンパイルするためのSConstruct
 #-------------------------------------------------------------------- # Project globals #-------------------------------------------------------------------- source_dir = 'src/' d_include_path = 'src/D/' release_target_dir = 'bin/release/' debug_target_dir = 'bin/debug/' target_name = 'train' #-------------------------------------------------------------------- # Release build configuration #-------------------------------------------------------------------- release_env = Environment( CC='gcc', CXX='g++', DMD='dmd', DPATH=d_include_path, LINK='gcc', CPPFLAGS='-O3', DFLAGS='-O' ) release_env.VariantDir(release_target_dir, source_dir, duplicate=0) d_sources = Glob(release_target_dir + 'D/*.d') c_sources = Glob(release_target_dir + 'C/*.c') c_obj = release_env.Object(c_sources) d_obj = release_env.Object(d_sources) release_env.Program(release_target_dir + target_name, d_obj + c_obj, LIBS=['phobos2', 'lua']) #-------------------------------------------------------------------- # Debug build configuration #-------------------------------------------------------------------- debug_env = Environment( CC='gcc', CXX='g++', DMD='dmd', DPATH=d_include_path, LINK='gcc', CPPFLAGS='-g3', DFLAGS='-g' ) debug_env.VariantDir(debug_target_dir, source_dir, duplicate=0) d_sources = Glob(debug_target_dir + 'D/*.d') c_sources = Glob(debug_target_dir + 'C/*.c') c_obj = debug_env.Object(c_sources) d_obj = debug_env.Object(d_sources) debug_env.Program(debug_target_dir + target_name, d_obj + c_obj, LIBS=['phobos2', 'lua'])
      
      









テストLuaスクリプトを正常に読み取った後、エンティティが多すぎることに気付きました-Dで記述できるモジュールをCで記述し、必要な関数をliblua.soから直接エクスポートするのはなぜですか?!? さらに、Dでは、クラスの形式で(たとえば、プロジェクトの以前のC ++バージョンで行われたように)設計済みの既に必要な機能を実装できます。



Lua 5.2のマニュアル/ usr / include /のヘッダーファイルで武装して始めました。 当初は、必要な機能のみをエクスポートし、それに専念するというアイデアでした。 しかし、それから私は恥ずかしく思いました-確かに、この仕事の結果は他の誰かに役立つかもしれません。 したがって、LuaのC APIはほぼ完全にDに移植されました。



2. Lua APIを操作するためのDライブラリ



結果はGitHubで入手できます。



アーカイブには次のファイルが含まれています

  1. lua.d-基本機能のプロトタイプ
  2. lualib.d -Luaライブラリを操作するための関数
  3. lauxlib.d-追加機能
  4. luaconf.d-いくつかのタイプと定数の説明


最後のファイルについては、残りのモジュール内で使用される部分に移植されます。 この作業はまだ行われていません。 残りについては、このライブラリを使用すると、C / C ++での開発中に行われるように、DのプログラムからLuaへのインターフェイスを使用できます。 リストされたモジュールはDのプロジェクトに接続され、 liblua.soライブラリにリンクされます(GNU / Linuxの場合、リンカーキーは-lluaです)。



Lua C APIで説明されているすべての機能が実装されています。 モジュールを作成するとき、基本的なAPI関数への単純化された呼び出しを実装する元のヘッダーのすべてのマクロは関数に置き換えられました。



検証用に小さなLuaスクリプトが作成されました



test.lua

 --     nv = 2 -- ,    my_number_sqr = function(x) return x*x end
      
      





それを読むには、必要なライブラリを接続して、Dでそのようなコードを書きます

main.d

 module main; import std.stdio; import lua; import lualib; import lauxlib; //------------------------------------------------------------------- // //------------------------------------------------------------------- void main() { //    Lua     lua_State *lua_state = luaL_newstate(); luaL_openlibs(lua_state); //    if (luaL_dofile(lua_state, "test.lua")) { writeln("Error"); } //    //    Lua int top = lua_gettop(lua_state); //     nv lua_getglobal(lua_state, "nv"); //      lua_Integer tmp = lua_tointeger(lua_state, -1); //   while (top - lua_gettop(lua_state)) lua_pop(lua_state, 1); //   my_number_sqr top = lua_gettop(lua_state); lua_getglobal(lua_state, "my_number_sqr"); double x = 8; lua_pushnumber(lua_state, x); lua_pcall(lua_state, 1, 1, 0); double ret = lua_tonumber(lua_state, 01); while (top - lua_gettop(lua_state)) lua_pop(lua_state, 1); //   writeln("readed integer nv = ", tmp); writefln("sqr(%f) = %f ", x, ret); lua_close(lua_state); }
      
      





liblua.soのリンクを忘れずに収集します (ビルドスクリプトの方が簡単かもしれませんが、個別に書くのは面倒でした )。

テストプログラムをコンパイルするSConstruct
 #-------------------------------------------------------------------- # Project globals #-------------------------------------------------------------------- source_dir = 'src/' d_include_path = 'src/D/' release_target_dir = 'bin/release/' target_name = 'test_lua_api' #-------------------------------------------------------------------- # Release build configuration #-------------------------------------------------------------------- release_env = Environment( CC='gcc', CXX='g++', DMD='dmd', DPATH=d_include_path, LINK='gcc', CPPFLAGS='-O3', DFLAGS='-O' ) release_env.VariantDir(release_target_dir, source_dir, duplicate=0) d_sources = Glob(release_target_dir + 'D/*.d') d_obj = release_env.Object(d_sources) release_env.Program(release_target_dir + target_name, d_obj, LIBS=['phobos2', 'lua'])
      
      







出力した

 readed integer nv = 2 sqr(8.000000) = 64.000000
      
      







結論の代わりに



ほとんどの関数はまだテストされていません;実際、私のプロジェクトでは、テーブルのフィールドを読み取り、Lua関数を呼び出しています。 良いチェックのために、このコードは私がする人々に無料で与えられるべきです。 誰かが役に立つといいな。 私の仕事に注目してくれてありがとう。



PS:



コードを真剣に修正する必要があります。 文字列を正しく動作させるために、lua_tostringを調整する必要があるとしましょう(...)

 string lua_tostring(lua_State *L, int idx) { return to!string(lua_tolstring(L, idx, null)); }
      
      





文字列に変換を追加します。 さて、使用すると調整が行われます



All Articles