2016-07-18 3 views
2

Так что в основном я пытаюсь найти способ использования функций C++ в Lua, которые не являются lua_CFunctions (не возвращаются, а int и принимают lua_State в качестве параметра). В основном ваша обычная старая функция C++. Уловка, однако, заключается в том, что я пытаюсь найти способ сделать это, не написав собственный выделенный lua_CFunction (так что в принципе, у меня уже есть программа или куча функций на C++, которые я хочу использовать в Lua, и я Не нужно писать новую функцию для каждого из них).Использование функций C++ в Lua/Binding

Так, скажем, у меня есть очень простой функции C++:

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

(с или без статична, она не должна() дело?).

Скажем, я хочу использовать эту функцию в Lua, вызывая doubleInt (10) в сценарии lua. Есть ли способ сделать это без написания отдельного номера

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

для каждой индивидуальной функции? Итак, что-то похожее на то, что luaBind делает с их функцией def() (и я знаю, что это отстой, но я не могу использовать отдельную выделенную библиотеку привязки, должен писать самостоятельно).

Я знаю, что мне нужно написать класс с шаблонами для этого, но я даже не имею ни малейшего представления о том, как получить функцию в Lua. Я не думаю, что на C++ есть способ автоматически генерировать пользовательскую функцию (предположительно во время компиляции) - это было бы потрясающе, поэтому я даже не знаю, с чего начать.

+0

Почему вы просто не используете LuaBind? Попытка сделать библиотеку клея не так-то просто. – Gibet

+0

Вы должны точно объяснить, почему вы не можете использовать библиотеку привязки. Это потому, что вам нужно, чтобы он был только заголовком, и вы не можете использовать то, с чем вам нужно связать? Существует множество библиотек только для заголовков. Это потому, что твой профессор сказал тебе, что не можешь? Мне трудно верить в это. –

ответ

1

Это очень открытый вопрос.

Недавно я работал над библиотекой связывания 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, чтобы быть строго правильными.

Вы должны обязательно использовать одну из существующих библиотек, если у вас нет действительно неотразимой необходимости, которая ее предотвращает.

0

Если вы не ограничены стандартным Lua lib, вы можете попробовать LuaJIT. Он имеет поддержку. Вызов внешней функции такой же простой, как:

local ffi = require("ffi") 
ffi.cdef[[ 
int printf(const char *fmt, ...); 
]] 
ffi.C.printf("Hello %s!", "world") 
Смежные вопросы