📜 ⬆️ ⬇️

Lua API for D language

Foreword


This note will not be too voluminous, but rather, quite the contrary, small.

For quite a long time, I have been following a series of articles about the D language, published on Habré . After reviewing a number of sources, ranging from Wikipedia and ending with the official manuals on this language, I came to the conclusion that it would be useful to use it in my research projects. The main project of the doctoral dissertation came to a standstill, demanding processing (a number of mechanical issues surfaced). It was decided to combine the revision of the project and learning a new language for me.

Said done - most of the code was rather quickly transferred from C / C ++ to D. Despite the different opinions about D being in the environment of software developers, I liked the language.
')
One problem - in the old version of the project to set the parameters of the train model and change the logic of work without recompilation, Lua scripting was used. Those who come across it know by their occupation that there is a well-developed and well-documented API for developing in C / C ++.

As for Lua-scripting in D, there are a number of projects, for example LuaD , which brings the ability to work with Lua into D programs. However, LuaD is designed for the previous version 5.1. I came across a project for 5.2 - DerelictLua , but offhand from the “light kick” it was not possible to get it. If you have time, you can figure it out, but as always there is no time. I had to strain my thinking powers and come up with a faster and easier solution. If the reader is interested in what came of it, welcome under cat.

1. Calling C-functions from a D program


This is implemented not simply, but very simply - in module D we write the function prototype, indicating that it is located in the external module and uses the C-function calling convention
extern(C) double my_C_func(int param1, double param2); 

Naturally, the external module must be one way or another assembled with the program in D.

In this regard, the first idea arose - to implement work with Lua on C, and to register prototypes in a module on D and compile everything into one program. There was even a redesigned project build script (using SCons) in this way.

SConstruct to compile a program from modules in C / C ++ and D
 #-------------------------------------------------------------------- # 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']) 



After successfully reading the test Lua script, it was understood that there are too many entities - why write a module in C, if you can write it in D, and export the necessary functions directly from liblua.so ?!? In addition, on D, you can implement the functionality that I already need by setting it up, let's say in the form of a class (this was done in the previous C ++ version of the project).

Armed with the guide to Lua 5.2 and the header files in / usr / include / I started. Initially, the idea was to export only the functions I needed and stop there. But then I felt ashamed - for sure the results of this work may be useful to someone else. Therefore, the C API to Lua was almost completely ported to D.

2. D-library for working with Lua API


Result is available on github

The archive contains the following files
  1. lua.d - prototypes of basic functions
  2. lualib.d - functions for working with Lua libraries
  3. lauxlib.d - additional functions
  4. luaconf.d - description of some types and constants

As for the last file, it is ported to the part that is used inside other modules. This work has yet to be done. Otherwise, this library allows you to use the interface to Lua from a program in D, as is done when developing in C / C ++. The listed modules are connected to the project on D and it is linked with the library liblua.so (the linker key is -llua , in the case of GNU / Linux).

Implemented all the functions described in the Lua C API. When writing modules, all macros in the original headers that implement the simplified calls to the basic API functions were replaced by functions.

A tiny Lua script was written for verification.

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

To read it, we write such code on D, connecting the necessary libraries.
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); } 

Putting it together , not forgetting linking liblua.so (the build script may be easier, but it was too lazy to write separately).
SConstruct to compile a test program
 #-------------------------------------------------------------------- # 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']) 


having an exit
 readed integer nv = 2 sqr(8.000000) = 64.000000 


Instead of conclusion


Most of the functions have not yet been tested, in fact, in my project, reading of table fields and calling Lua functions is used so far. For a good check, this code should be given free of charge to the people, which I do. I hope someone will come in handy. Thank you for your attention to my work.

PS:


The code will have to be seriously corrected. For example, to work correctly with strings, we had to make some adjustments in lua_tostring (...)
 string lua_tostring(lua_State *L, int idx) { return to!string(lua_tolstring(L, idx, null)); } 

adding conversion to string. Well, as you make adjustments will be made

Source: https://habr.com/ru/post/248039/


All Articles