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.
Warum Sie einfach nicht LuaBind verwenden? Der Versuch, eine Leim-Bibliothek zu erstellen, ist nicht so einfach. – Gibet
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. –