Lua API for D language

    Foreword


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

    For quite a long time I have been following a series of articles about the language D published on Habré . Having familiarized with a number of sources, starting from Wikipedia and ending with official manuals on this language, I came to the conclusion that it is advisable to use it in your research projects. The main project for a doctoral dissertation came to a standstill, required processing (a number of mechanical issues surfaced). It was decided to combine processing of the project and learning a new language for me.

    That being said, most of the code was pretty quickly ported from C / C ++ to D. Despite the different opinions about D among software developers, the language came to my taste.

    One problem - in the old version of the project, Lua scripting was used to set the parameters of the train model and change the logic of work without recompilation. Those who encounter it by occupation know that there is a well-developed and sufficiently documented API for development in C / C ++.

    As for Lua scripting in D, there are a number of projects, for example LuaD , which introduces the ability to work with Lua in D programs. However, LuaD is designed for the previous version 5.1. I came across a project for 5.2 - DerelictLuahowever, offhand with a "light kick" to get it failed. If you have time, you can figure it out, but there is no time, as always. I had to strain my mental powers and come up with a faster and simpler solution. If the reader is interested in what came of this, welcome to Cat.

    1. Calling C-functions from a program on D


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

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

    In this regard, the first idea came up - to implement work with Lua in C, and write the prototypes in the module in D and compile everything into one program. The project build script was even redone (using SCons) in this way

    SConstruct for compiling 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, I realized 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, having designed it, say in the form of a class (this was done in the previous C ++ version of the project).

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

    2. D-library for working with Lua API


    The 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 features
    4. luaconf.d - description of some types and constants

    As for the last file, it is ported in the part that is used inside the rest of the modules. This work remains to be done. For the rest, this library allows you to use the interface to Lua from a D program, as it is done during development in C / C ++. The listed modules are connected to the D project and it is linked with the liblua.so library (the linker key is -llua , if it comes to GNU / Linux).

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

    For verification, a tiny script was written in Lua

    test.lua
    --	Глобальная переменная целого типа
    nv = 2
    --	Функция, вычисляющая квадрат аргумента
    my_number_sqr = function(x)
    	return x*x
    end
    

    To read it, we write such code in 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);
    }
    

    We collect it, not forgetting to link liblua.so (the build script might 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 output
    readed integer nv = 2
    sqr(8.000000) = 64.000000 
    


    Instead of a conclusion


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

    PS:


    The code will have to be seriously corrected. Let's say that for correct work with strings we had to make some adjustments to 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 use the adjustments will be made

    Also popular now: