【问题标题】:Using C++ functions in Lua/Binding在 Lua/Binding 中使用 C++ 函数
【发布时间】:2016-11-20 15:17:42
【问题描述】:

所以基本上我试图找到一种在 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++ 中没有一种方法可以自动生成自定义函数(大概在编译时)——这太棒了——所以我什至不知道从哪里开始。

【问题讨论】:

  • 您应该准确解释为什么不能使用绑定库。是因为,您只需要它作为标题,并且不能使用必须链接的东西吗?有很多只有头文件的绑定库。是因为你的教授告诉你不能吗?我很难相信。

标签: c++ lua luabind


【解决方案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

基本上,您希望使用可变参数模板,以便您可以专门针对函数指针的所有参数。您将这些类型一一传递给您的读取方法,并使用索引序列从正确的堆栈位置读取。你尝试阅读它们,如果你能做到没有错误,那么你可以调用目标函数,然后返回它的结果。

如果您还想将通用 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&lt;...&gt;::adapted 时,它必须在编译时全部解析。这意味着编译器可以内联所有不同的位。

为了解决无法推断函数指针等非类型模板参数的类型(C++17 之前)的问题,我使用了如下所示的宏:

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

所以,我可以取一个复杂的 C++ 函数 f,然后像使用 lua_CFunction 一样使用 PRIMER_ADAPT(&amp;f)

您应该意识到,制作所有这些东西并对其进行测试需要很长时间。我在这个库上工作了一个多月,它是从另一个项目中的一些代码中重构出来的,在那里我对其进行了更长时间的改进。 lua 中也有很多与此类“自动化”堆栈操作相关的陷阱,因为它不会为您进行任何边界检查,您需要调用 lua_checkstack 才能严格正确。

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

【讨论】:

    【解决方案2】:

    如果您不限于标准 Lua 库,您可以尝试 LuaJIT。它有 ffi 支持。调用外部函数很简单:

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

    【讨论】:

      猜你喜欢
      • 2010-11-27
      • 1970-01-01
      • 2013-11-27
      • 2021-04-15
      • 2011-12-27
      • 2015-07-14
      • 2016-08-04
      • 1970-01-01
      • 2015-02-12
      相关资源
      最近更新 更多