2016-07-18 9 views
2

Also im Grunde versuche ich einen Weg zu finden, C++ - Funktionen in Lua zu verwenden, die nicht lua_CFunctions sind (nicht zurückgeben und int und einen lua_State als Parameter nehmen). Grundsätzlich deine normale alte C++ Funktion. Der Haken dabei ist, dass ich versuche, einen Weg zu finden, ohne eine eigene dedizierte lua_CFunction zu schreiben (also stelle mir vor, dass ich bereits ein Programm oder eine Reihe von Funktionen in C++ habe, die ich in Lua verwenden möchte, und ich nicht Ich möchte für jede von ihnen eine neue Funktion schreiben müssen.Verwendung von C++ - Funktionen in Lua/Binding

Also, sagen, ich habe eine sehr einfache C++ Funktion:

static int doubleInt(int a) { 
    return a*2; 
} 

(mit oder ohne statische, sollte es nicht (k) eine Rolle?).

Angenommen, ich möchte diese Funktion in Lua verwenden, indem ich doubleInt (10) in einem Lua-Skript aufruft. Gibt es eine Möglichkeit, dies zu tun, ohne ein separates

static int callFunc(lua_State *L) { 
    //do stuff to make the function work in lua 
} 

für jede einzelne Funktion zu schreiben? Also etwas nach dem, was luaBind mit ihrer def() Funktion macht (und ich weiß, dass es scheiße ist, aber ich kann nicht wirklich eine separate dedizierte Binde-Bibliothek verwenden; muss meine eigene schreiben).

Ich weiß, ich muss eine Klasse mit Vorlagen dafür schreiben, aber ich habe nicht einmal die geringste Ahnung, wie man die Funktion in Lua bekommt. Ich glaube nicht, dass es in C++ eine Möglichkeit gibt, automatisch eine benutzerdefinierte Funktion zu erzeugen (vermutlich zur Kompilierzeit) - das wäre toll - und ich weiß nicht einmal, wo ich anfangen soll.

+0

Warum Sie einfach nicht LuaBind verwenden? Der Versuch, eine Leim-Bibliothek zu erstellen, ist nicht so einfach. – Gibet

+0

Sie sollten genau erklären, warum Sie keine Bindungsbibliothek verwenden können. Liegt es daran, dass Sie es nur als Header verwenden müssen und Sie nicht etwas verwenden können, mit dem Sie verlinken müssen? Es gibt eine Fülle von Bibliotheken, die nur den Header binden. Liegt es daran, dass dein Professor dir gesagt hat, dass du es nicht kannst? Es fällt mir schwer, das zu glauben. –

Antwort

1

Dies ist eine sehr offene Frage.

Ich habe vor kurzem an einer Lua-Binde-Bibliothek gearbeitet, also kann ich erklären, wie ich das gemacht habe, aber es gibt viele Möglichkeiten, wie Sie das tun können.

Sie haben diese Frage nicht C++ 11 markiert. Ich gehe jedoch davon aus, dass Sie C++ 11 verwenden. Wenn nicht, dann ist es extrem schwierig und ich würde gar nicht sagen, dass es praktisch ist, eigene zu rollen, besonders wenn man nicht schon genug über boost::mpl weiß, um eine Idee zu haben, wie man es macht. Sie sollten auf jeden Fall einfach luabind in diesem Fall verwenden.


Das erste, was Sie brauchen, ist, müssen Sie einige grundlegende Infrastruktur schaffen, die Ihnen sagt, wie man entsprechende LUA-Typen und zurück C++ Typen konvertieren.

IMO der beste Weg, dies zu tun, eine Art Merkmal verwendet, und nicht ein massive überladene Funktion. Also, werden Sie eine primäre Vorlage definieren:

namespace traits { 

template <typename T> 
struct push; 

template <> 
struct push<int> { 
    static void to_stack(lua_State * L, int x) { lua_pushinteger(L, x); } 
}; 

template <> 
struct push<double> { 
    static void to_stack(lua_State * L, double d) { lua_pushnumber(L, d); } 
}; 

... 

} // end namespace traits 

Etc. Sie wahrscheinlich auch für std::string und ähnliches spezialisieren wollen.

Dann können Sie eine generische push Funktion wie folgt machen:

template <typename T> 
void push(lua_State * L, const T & t) { 
    traits::push<T>::to_stack(L, t); 
} 

Der Vorteil hierbei ist, dass implizite Konvertierungen nicht berücksichtigt werden, wenn Sie push nennen. Entweder entspricht der Typ, den Sie übergeben haben, genau dem, für den Sie das Merkmal definiert haben, oder es schlägt fehl. Und zwischen double und int usw. können Sie keine mehrdeutigen Überlastungsprobleme bekommen, was ein großer Schmerz im Hintern sein kann.

Dann müssen Sie dasselbe für read tun, also haben Sie eine Eigenschaft, die Ihnen sagt, wie man Werte eines gegebenen Typs vom Stapel liest. Ihre read Technik muss Fehler irgendwie signalisieren, können Sie entscheiden, ob das Ausnahmen oder eine andere Technik verwenden sollte.

Sobald Sie dies haben, können Sie versuchen, eine adapt Vorlage, die eine beliebige Funktion Zeiger und versuchen, es in eine lua_CFunction, die in etwa die gleiche Sache macht, zu machen.

Grundsätzlich möchten Sie variadische Vorlagen verwenden, damit Sie sich gegen alle Parameter des Funktionszeigers spezialisieren können. Sie übergeben diese Typen nacheinander an Ihre Lesemethode und verwenden eine Indexsequenz, um von den richtigen Stapelpositionen zu lesen. Sie versuchen, sie alle zu lesen, und wenn Sie es ohne Fehler tun können, können Sie die Zielfunktion aufrufen und dann die Ergebnisse zurückgeben.

Wenn Sie auch generische C++ - Objekte als Rückgabewert zurückgeben möchten, können Sie Ihre Push-Funktion am Ende aufrufen.

Zuerst, um zu helfen, benötigen Sie eine "Indexsequenz" -Funktion. Wenn Sie in C++14 sind, können Sie std::make_integer_sequence verwenden, wenn nicht, dann müssen Sie Ihre eigenen rollen. Mine sieht wie folgt aus:

namespace detail { 

/*** 
* Utility for manipulating lists of integers 
*/ 

template <std::size_t... Ss> 
struct SizeList { 
    static constexpr std::size_t size = sizeof...(Ss); 
}; 

template <typename L, typename R> 
struct Concat; 

template <std::size_t... TL, std::size_t... TR> 
struct Concat<SizeList<TL...>, SizeList<TR...>> { 
    typedef SizeList<TL..., TR...> type; 
}; 

/*** 
* Count_t<n> produces a sizelist containing numbers 0 to n-1. 
*/ 
template <std::size_t n> 
struct Count { 
    typedef 
    typename Concat<typename Count<n - 1>::type, SizeList<n - 1>>::type type; 
}; 

template <> 
struct Count<0> { 
    typedef SizeList<> type; 
}; 

template <std::size_t n> 
using Count_t = typename Count<n>::type; 

} // end namespace detail 

Hier ist, was Ihre adapt Klasse aussehen könnte:

// Primary template 
template <typename T, T> 
class adapt; 

// Specialization for C++ functions: int (lua_State *, ...) 
template <typename... Args, int (*target_func)(lua_State * L, Args...)> 
class adapt<int (*)(lua_State * L, Args...), target_func> { 

    template <typename T> 
    struct impl; 

    template <std::size_t... indices> 
    struct impl<detail::SizeList<indices...>> { 
    static int adapted(lua_State * L) { 
     try { 
     return target_func(L, read<Args>(L, 1 + indices)...); 
     } catch (std::exception & e) { 
     return luaL_error(L, "Caught an exception: %s", e.what()); 
     } 
    } 
    }; 

public: 
    static int adapted(lua_State * L) { 
    using I = detail::Count_t<sizeof...(Args)>; 
    return impl<I>::adapted(L); 
    } 
}; 

Der eigentliche Code aus meiner Implementierung ist here. Ich habe beschlossen, es ohne Ausnahmen zu tun.

Diese Technik funktioniert auch zur Kompilierzeit - da Sie einen Funktionszeiger an eine beliebige C++ - Funktion als Nicht-Typ-Vorlagenparameter übergeben und die adapt Vorlage eine lua_CFunction als statisches Klassenmitglied erzeugt, wenn Sie dies tun ein Zeiger auf adapt<...>::adapted, muss alles zur Kompilierzeit aufgelöst werden. Dies bedeutet, dass alle verschiedenen Bits vom Compiler inline gesetzt werden können.

um die Unfähigkeit zu arbeiten, um den Typ eines nicht-Typ Template-Parameter wie ein Funktionszeiger (vor C++ 17) abzuleiten, verwende ich ein Makro, das wie folgt aussieht:

#define PRIMER_ADAPT(F) &::primer::adapt<decltype(F), (F)>::adapted 

So Ich kann eine komplizierte C++ Funktion f nehmen und dann PRIMER_ADAPT(&f) verwenden, als ob es einfach eine lua_CFunction wäre.

Sie sollten jedoch erkennen, dass es sehr lange dauert, all diese Sachen zu machen und zu testen. Ich habe mehr als einen Monat lang an dieser Bibliothek gearbeitet und sie wurde von einem Code in einem anderen Projekt, in dem ich sie länger verfeinert hatte, umstrukturiert. Es gibt auch viele Fallen in Lua im Zusammenhang mit "Automatisierung" Stack-Operationen wie folgt, weil es keine Grenzen für Sie überprüfen und Sie müssen lua_checkstack anrufen, um streng korrekt zu sein.

Sie sollten auf jeden Fall eine der vorhandenen Bibliotheken verwenden, es sei denn, Sie haben eine wirklich zwingende Notwendigkeit, die es verhindert.

0

Wenn Sie nicht auf Standard-Lua-Lib beschränkt sind, können Sie LuaJIT ausprobieren. Es hat ffi Unterstützung. Aufruf externe Funktion ist so einfach wie:

local ffi = require("ffi") 
ffi.cdef[[ 
int printf(const char *fmt, ...); 
]] 
ffi.C.printf("Hello %s!", "world")