Lua API für D-Sprache

    Vorwort


    Diese Note wird nicht zu umfangreich sein, sondern im Gegenteil klein.

    Seit geraumer Zeit verfolge ich eine Reihe von Artikeln über die Sprache D, die auf Habré veröffentlicht wurden . Nachdem ich mit einer Reihe von Quellen vertraut war, angefangen bei Wikipedia bis hin zu offiziellen Handbüchern zu dieser Sprache, kam ich zu dem Schluss, dass es ratsam ist, sie in Ihren Forschungsprojekten zu verwenden. Das Hauptprojekt für eine Dissertation kam zum Stillstand, die Bearbeitung war erforderlich (eine Reihe von mechanischen Problemen tauchten auf). Es wurde beschlossen, die Bearbeitung des Projekts mit dem Erlernen einer neuen Sprache für mich zu verbinden.

    Davon abgesehen wurde der Großteil des Codes ziemlich schnell von C / C ++ nach D portiert. Trotz der unterschiedlichen Meinungen über D unter Software-Entwicklern kam die Sprache zu meinem Geschmack.

    Ein Problem: In der alten Version des Projekts wurden mit Lua-Skripten die Parameter des Zugmodells festgelegt und die Arbeitslogik ohne Neukompilierung geändert. Wer es beruflich erlebt, weiß, dass es eine gut entwickelte und ausreichend dokumentierte API für die Entwicklung in C / C ++ gibt.

    Was Lua-Skripte in D betrifft , gibt es eine Reihe von Projekten, zum Beispiel LuaD , mit denen die Möglichkeit eingeführt wird, mit Lua in D-Programmen zu arbeiten. LuaD wurde jedoch für die vorherige Version 5.1 entwickelt. Ich bin auf ein Projekt für 5.2 gestoßen - DerelictLuajedoch mit einem "leichten Tritt", um es gescheitert zu bekommen. Wenn Sie Zeit haben, können Sie es herausfinden, aber es gibt keine Zeit, wie immer. Ich musste meine mentalen Kräfte anstrengen und eine schnellere und einfachere Lösung finden. Wenn der Leser daran interessiert ist, was dabei herausgekommen ist, heißt er cat willkommen.

    1. Aufruf von C-Funktionen aus einem Programm auf D


    Dies wird nicht einfach, sondern sehr einfach realisiert - in Modul D schreiben wir einen Funktionsprototyp, der angibt, dass er sich in einem externen Modul befindet und die C-Konvention zum Aufrufen von Funktionen verwendet
    extern(C) double my_C_func(int param1, double param2);
    

    Natürlich muss das externe Modul auf die eine oder andere Weise mit dem Programm in D verknüpft sein.

    In diesem Zusammenhang entstand die erste Idee - die Arbeit mit Lua in C zu implementieren und die Prototypen in das Modul in D zu schreiben und alles in ein Programm zu kompilieren. Das Project Build Script (mit SCons) wurde auf diese Weise sogar überarbeitet

    SConstruct zum Kompilieren eines Programms aus Modulen in C / C ++ und 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'])
    



    Nachdem ich das Test-Lua-Skript erfolgreich gelesen hatte, stellte ich fest, dass es zu viele Entitäten gibt - warum ein Modul in C schreiben, wenn Sie es in D schreiben können, und die erforderlichen Funktionen direkt aus liblua.so exportieren ?!? Außerdem können Sie auf D die Funktionalität, die ich bereits benötige, implementieren, indem Sie sie beispielsweise in Form einer Klasse entworfen haben (dies wurde in der vorherigen C ++ - Version des Projekts durchgeführt).

    Ausgerüstet mit dem Lua 5.2 Handbuch und den Header-Dateien in / usr / include / habe ich angefangen. Anfänglich bestand die Idee darin, nur die Funktionen zu exportieren, die ich benötigte, und mich damit zu befassen. Aber dann schämte ich mich - sicher können die Ergebnisse dieser Arbeit für jemand anderen nützlich sein. Daher wurde die C-API für Lua fast vollständig auf D portiert.

    2. D-Bibliothek für die Arbeit mit Lua API


    Das Ergebnis ist auf GitHub verfügbar.

    Das Archiv enthält die folgenden Dateien
    1. lua.d - Prototypen von Grundfunktionen
    2. lualib.d - Funktionen zum Arbeiten mit Lua-Bibliotheken
    3. lauxlib.d - zusätzliche Funktionen
    4. luaconf.d - Beschreibung einiger Typen und Konstanten

    Die letzte Datei wird in dem Teil portiert, der in den übrigen Modulen verwendet wird. Diese Arbeit bleibt noch zu erledigen. Im Übrigen können Sie mit dieser Bibliothek die Schnittstelle zu Lua von einem Programm in D aus verwenden, wie dies während der Entwicklung in C / C ++ geschieht. Die aufgelisteten Module sind mit dem Projekt in D verbunden und es ist mit der Bibliothek liblua.so verknüpft (der Linkerschlüssel ist -llua , wenn es um GNU / Linux geht).

    Alle in der Lua C API beschriebenen Funktionen sind implementiert. Beim Schreiben von Modulen wurden alle Makros in den ursprünglichen Headern, die vereinfachte Aufrufe grundlegender API-Funktionen implementieren, durch Funktionen ersetzt.

    Zur Überprüfung wurde in Lua test.lua ein winziges Skript geschrieben


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

    Um es zu lesen, schreiben wir einen solchen Code in D und verbinden die notwendigen Bibliotheken
    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);
    }
    

    Wir sammeln es und vergessen nicht, liblua.so zu verlinken (das Build-Skript ist zwar einfacher, aber es war zu faul, um es separat zu schreiben).
    SConstruct zum Kompilieren eines Testprogramms
    #--------------------------------------------------------------------
    #		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'])
    


    Ausgang haben
    readed integer nv = 2
    sqr(8.000000) = 64.000000 
    


    Anstelle einer Schlussfolgerung


    Die meisten Funktionen wurden in meinem Projekt noch nicht getestet, während ich die Felder der Tabellen las und Lua-Funktionen aufrief. Für eine gute Prüfung sollte dieser Code den Leuten kostenlos ausgehändigt werden, was ich auch tue. Ich hoffe, dass jemand nützlich ist. Vielen Dank für Ihre Aufmerksamkeit auf meine Arbeit.

    PS:


    Der Code muss ernsthaft korrigiert werden. Nehmen wir an, wir mussten für die korrekte Arbeit mit Strings einige Anpassungen an lua_tostring vornehmen (...)
    string lua_tostring(lua_State *L, int idx)
    {
    		return to!string(lua_tolstring(L, idx, null));
    }
    

    Konvertierung zu String hinzufügen. Nun, wie Sie verwenden, werden die Anpassungen vorgenommen

    Jetzt auch beliebt: