Это очень открытый вопрос.
Недавно я работал над библиотекой связывания lua, поэтому я могу объяснить, как я это сделал, но есть много способов сделать это.
Вы не отметили этот вопрос C++ 11. Однако я собираюсь предположить, что вы используете C++ 11. Если нет, то это чрезвычайно сложно, и я бы сказал, что совсем не практично катиться самостоятельно, особенно если вы еще не знаете достаточно о boost::mpl
, чтобы иметь представление о том, как это сделать. В этом случае вам обязательно нужно использовать luabind
.
Первое, что вам нужно, вы должны создать некоторую базовую инфраструктуру, которая говорит вам, как конвертировать типы C++ в соответствующие Lua типы и обратно.
IMO лучший способ сделать это использовать черту типа, а не одна массивная перегруженная функция. Таким образом, вы будете определить первичный шаблон:
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
т.д. Возможно, вы также хотите специализироваться его std::string
и тому подобное.
Затем вы можете сделать общую push
функцию:
template <typename T>
void push(lua_State * L, const T & t) {
traits::push<T>::to_stack(L, t);
}
Преимущество здесь состоит в том, что неявные преобразования не учитывается при вызове push
. Либо тип, который вы передали, точно совпадает с тем, что вы определили для этого признака, или он терпит неудачу. И вы не можете получить неоднозначные проблемы с перегрузкой между double
и int
и т. Д., Что может стать большой болью в прикладе.
Затем вы должны сделать то же самое для read
, так что у вас есть черта, которая рассказывает вам, как читать значения определенного типа со стека. Ваша техника read
должна как-то сигнализировать о сбоях, вы можете решить, следует ли использовать исключения или другую технику.
После этого вы можете попробовать создать шаблон adapt
, который примет произвольный указатель на функцию и попытается адаптировать его к lua_CFunction
, что делает примерно то же самое.
В принципе, вы хотите использовать вариативные шаблоны, чтобы вы могли специализироваться на всех параметрах указателя функции. Вы передаете эти типы один за другим вашему методу чтения и используете индексную последовательность для чтения из правильных позиций стека. Вы пытаетесь прочитать их все, и если вы можете сделать это без ошибок, вы можете вызвать целевую функцию, а затем вернуть ее результаты.
Если вы хотите также вернуть общие объекты C++ в качестве возвращаемого значения, вы можете вызвать функцию push в конце.
Во-первых, чтобы помочь вам, вам понадобится средство «индексной последовательности». Если вы находитесь в C++14
, вы можете использовать std::make_integer_sequence
, если нет, то вам нужно катиться самостоятельно. Шахта выглядит следующим образом:
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
Вот что ваш adapt
класс может выглядеть следующим образом:
// 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);
}
};
Реальный код из моей реализации является here. Я решил сделать это, не используя исключения.
Этот метод также работает во время компиляции - поскольку вы передаете указатель функции произвольной функции C++ в качестве параметра шаблона непигового типа, а шаблон adapt
создает lua_CFunction
в качестве статического члена класса, когда вы принимаете указатель на adapt<...>::adapted
, он должен быть разрешен во время компиляции. Это означает, что все различные биты могут быть встроены в компилятор.
Чтобы обойти неспособность вывести тип параметра шаблона не типа, как указатель на функцию (до C++ 17), я использую макрос, который выглядит следующим образом:
#define PRIMER_ADAPT(F) &::primer::adapt<decltype(F), (F)>::adapted
Так , Я могу взять сложную функцию C++ f
, а затем использовать PRIMER_ADAPT(&f)
, как если бы это был просто lua_CFunction
.
Вы должны понимать, что, делая все это и тестируя, требуется очень много времени. Я работал над этой библиотекой более месяца, и она реорганизована из некоторого кода в другом проекте, где я уточнил его дольше. В lua также много ловушек, связанных с «автоматизацией» операций стека, таких как это, потому что он не делает никаких проверок для вас, и вам нужно называть lua_checkstack
, чтобы быть строго правильными.
Вы должны обязательно использовать одну из существующих библиотек, если у вас нет действительно неотразимой необходимости, которая ее предотвращает.
Почему вы просто не используете LuaBind? Попытка сделать библиотеку клея не так-то просто. – Gibet
Вы должны точно объяснить, почему вы не можете использовать библиотеку привязки. Это потому, что вам нужно, чтобы он был только заголовком, и вы не можете использовать то, с чем вам нужно связать? Существует множество библиотек только для заголовков. Это потому, что твой профессор сказал тебе, что не можешь? Мне трудно верить в это. –