在Lua / Binding中使用C ++函数

时间:2016-07-18 06:03:48

标签: c++ lua luabind

所以基本上我试图找到一种方法在Lua中使用不是lua_CFunctions的C ++函数(不返回和int并将lua_State作为参数)。基本上你的常规旧C ++功能。但问题是,我正试图找到一种方法来实现它而不用编写自己专用的lua_CFunction(所以基本上想象一下,我已经在C ++中有一个程序或一堆函数,我想在Lua中使用,而且我不喜欢我不想为每个人写一个新功能。)

所以,说我有一个非常简单的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 ++中有一种方法可以自动生成自定义函数(大概是在编译时) - 这太棒了 - 所以我甚至不知道从哪里开始。

2 个答案:

答案 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时,不会考虑隐式转化。您传递的类型与您为其定义特征的内容完全匹配,或者失败。并且你不能在doubleint等之间出现模糊的过载问题,这可能是一个很大的痛苦。

然后,你必须为read做同样的事情,所以你有一个特征告诉你如何从堆栈中读取给定类型的值。你的read技术需要以某种方式表示失败,你可以决定是否应该使用异常或不同的技术。

完成此操作后,您可以尝试创建一个adapt模板,该模板将采用任意函数指针,并尝试将其调整为lua_CFunction,其大致相同。

基本上,您希望使用可变参数模板,以便可以专门针对函数指针的所有参数。您将这些类型逐个传递给read方法,并使用索引序列从正确的堆栈位置读取。您尝试全部读取它们,如果您可以无错误地执行它们,则可以调用目标函数,然后返回其结果。

如果你想将通用C ++对象作为返回值推回,那么你可以在最后调用你的推送功能。

首先,要提供帮助,您需要一个“索引序列”工具。如果你在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来严格正确。

你绝对应该使用现有的一个库,除非你有一个非常迫切需要阻止它。

答案 1 :(得分:0)

如果你不限于标准的Lua lib,你可以试试LuaJIT。它有ffi支持。调用外部函数非常简单:

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