- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我尝试使用 lua_touserdata()
将 C++ 类转换为空指针,然后使用 lua_pushlightuserdata()
将其转换回 C++ 类。
但是,一旦完成转换,我就无法在类中索引变量。
这是我的测试代码:
MyBindings.h
class Vec2
{
public:
Vec2():x(0), y(0){};
Vec2(float x, float y):x(x), y(y){};
float x, y;
};
void *getPtr(void *p)
{
return p;
}
MyBindings.i
%module my
%{
#include "MyBindings.h"
%}
%typemap(typecheck) void*
{
$1 = lua_isuserdata(L, $input);
}
%typemap(in) void*
{
$1 = lua_touserdata(L, $input);
}
%typemap(out) void*
{
lua_pushlightuserdata(L, $1);
++SWIG_arg;
}
%include "MyBindings.h"
main.cpp
#include "lua.hpp"
extern "C"
{
int luaopen_my(lua_State *L);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
const int ret = luaL_dostring(L, "local vec = my.Vec2(3, 4)\n"
"local p = my.getPtr(vec)\n"
"print(p.x)");
if (ret)
{
std::cout << lua_tostring(L, -1) << '\n';
}
lua_close(L);
}
我得到的结果:
[string "local vec = my.Vec2(3, 4)..."]:3: attempt to index a userdata value (local 'p')
我期望的结果:
3
我应该怎么做才能得到我期望的结果?
最佳答案
如果你想这样做,你必须调整你的设计。首先,函数 getPtr
无法工作,因为它太通用了。 SWIG 无法神奇地猜测类型并做出正确的事情。您至少必须修复输入的类型。
MyBindings.h
struct Vec2 {
Vec2() : x(0), y(0){};
Vec2(float x, float y) : x(x), y(y){};
float x, y;
};
void *getPtr(Vec2 &p) { return &p; }
再一次,你真的确定要这样做吗?因为它会变得丑陋!
您至少需要两个元方法,__index
和__newindex
,以通过指针获取和设置 vector 的元素。我在接口(interface)文件的文字 block (%{ ... %}
) 中实现了这些,但您也可以将它们移动到 header 并将此 header 包含在文字 block 中。
现在您必须让 Lua 知道您定义的元方法并将它们插入到命名的元表中,这样您就可以将 Vec2
类型的指针与其他指针区分开来。因此,您必须在接口(interface)文件的 %init
部分添加一些内容,以便在解释器启动时注册元表。
因为您必须去掉 getPtr
的 void*
输入参数,typecheck
和 in
可以删除类型映射。 out
类型映射必须进行调整。我们必须为适合指向 Vec2
的指针的用户数据分配内存。我们将用户数据设置为该指针并将 Vec2
元表添加到它上面。现在这非常简单,不是吗?(讽刺)
MyBindings.i
%module my
%{
#define SWIG_FILE_WITH_INIT
#include <string>
#include "MyBindings.h"
static int setVec2(lua_State *L) {
Vec2 *v = *static_cast<Vec2 **>(luaL_checkudata(L, 1, "Vec2"));
luaL_argcheck(L, v != nullptr, 1, "invalid pointer");
std::string index = luaL_checkstring(L, 2);
luaL_argcheck(L, index == "x" || index == "y", 2, "index out of range");
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
float record = lua_tonumber(L, 3);
if (index == "x") {
v->x = record;
} else if (index == "y") {
v->y = record;
} else {
assert(false); // Can't happen!
}
return 0;
}
static int getVec2(lua_State *L) {
Vec2 *v = *static_cast<Vec2 **>(luaL_checkudata(L, 1, "Vec2"));
luaL_argcheck(L, v != nullptr, 1, "invalid pointer");
std::string index = luaL_checkstring(L, 2);
luaL_argcheck(L, index == "x" || index == "y", 2, "index out of range");
if (index == "x") {
lua_pushnumber(L, v->x);
} else if (index == "y") {
lua_pushnumber(L, v->y);
} else {
assert(false); // Can't happen!
}
return 1;
}
static const struct luaL_Reg Vec2_meta[] = {
{"__newindex", setVec2},
{"__index", getVec2},
{nullptr, nullptr} // sentinel
};
%}
%init %{
luaL_newmetatable(L, "Vec2");
luaL_setfuncs(L, Vec2_meta, 0);
lua_pop(L, 1);
%}
%typemap(out) void*
{
void * udata = lua_newuserdata(L, sizeof(Vec2 *));
*static_cast<void **>(udata) = $1;
luaL_getmetatable(L, "Vec2");
lua_setmetatable(L, -2);
++SWIG_arg;
}
%include "MyBindings.h"
让我们看看它是否有效。
test.lua
local my = require("my")
local vec = my.Vec2(3, 4)
local p = my.getPtr(vec)
print(p.x, p.y)
p.x = 1.0
p.y = 2.0
print(p.x, p.y)
print(vec.x, vec.y)
$ swig -lua -c++ MyBindings.i
$ clang++ -Wall -Wextra -Wpedantic -I/usr/include/lua5.3 -fPIC -shared MyBindings_wrap.cxx -o my.so -llua5.3
$ lua5.3 test.lua
3.0 4.0
1.0 2.0
1.0 2.0
如果您使用轻型用户数据可能会更容易一些,但缺点是所有轻型用户数据将共享相同的元表,因此您只能对一种对象执行此操作。
将指向特定类型的指针转换为 void*
称为类型删除,因为您丢失了有关所包含数据的所有信息。因此,在恢复类型时必须小心,确保恢复的是正确的类型。转换为不相关的类型是未定义的行为,如果幸运的话,会导致程序崩溃。
您可能不希望像 Vec2
那样使用 void*
。强制转换为 void*
的目的是什么,当您无论如何都想保留原始含义时。相反,您想要两个函数,getPtr
和 getVec2
。 getPtr
函数删除类型并为您提供一个 void*
对象,该对象在 Lua 中不可用,但可以方便地传递给接受任意数据作为 void 的回调函数*
。完成后,getVec2
函数会将类型恢复为 Vec2
。
在示例中,getVec2
函数通过引用返回,即返回的对象将是对您调用 getPtr
的对象的引用。这也意味着,如果原始对象被垃圾回收,您将得到一个无效指针,这将使您的应用程序崩溃。
MyBindings.h
struct Vec2 {
Vec2() : x(0), y(0){};
Vec2(float x, float y) : x(x), y(y){};
float x, y;
};
void *getPtr(Vec2 &p) { return &p; }
Vec2 &getVec2(void *p) { return *static_cast<Vec2 *>(p); }
MyBindings.i
%module my
%{
#define SWIG_FILE_WITH_INIT
#include "MyBindings.h"
%}
%include "MyBindings.h"
test.lua
local my = require("my")
local vec = my.Vec2(3, 4)
-- Erase the type of vec to pass it around
local p = my.getPtr(vec)
-- Then restore the type using getVec2
local v = my.getVec2(p)
-- Take care! v is a reference to vec
v.x = 1.0
v.y = 2.0
print(v.x, v.y)
print(vec.x, vec.y)
调用示例:
$ swig -lua -c++ MyBindings.i
$ clang++ -Wall -Wextra -Wpedantic -I/usr/include/lua5.3 -fPIC -shared MyBindings_wrap.cxx -o my.so -llua5.3
$ lua5.3 test.lua
1.0 2.0
1.0 2.0
要查看引用语义失败,请将 vec = nil collectgarbage()
放在 local p = my.getPtr(vec)
之后。它不会在我的机器上崩溃,但 Valgrind 报告无效的读取和写入。
关于c++ - 如何索引转换后的用户数据值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52232478/
初学者 android 问题。好的,我已经成功写入文件。例如。 //获取文件名 String filename = getResources().getString(R.string.filename
我已经将相同的图像保存到/data/data/mypackage/img/中,现在我想显示这个全屏,我曾尝试使用 ACTION_VIEW 来显示 android 标准程序,但它不是从/data/dat
我正在使用Xcode 9,Swift 4。 我正在尝试使用以下代码从URL在ImageView中显示图像: func getImageFromUrl(sourceUrl: String) -> UII
我的 Ubuntu 安装 genymotion 有问题。主要是我无法调试我的数据库,因为通过 eclipse 中的 DBMS 和 shell 中的 adb 我无法查看/data/文件夹的内容。没有显示
我正在尝试用 PHP 发布一些 JSON 数据。但是出了点问题。 这是我的 html -- {% for x in sets %}
我观察到两种方法的结果不同。为什么是这样?我知道 lm 上发生了什么,但无法弄清楚 tslm 上发生了什么。 > library(forecast) > set.seed(2) > tts lm(t
我不确定为什么会这样!我有一个由 spring data elasticsearch 和 spring data jpa 使用的类,但是当我尝试运行我的应用程序时出现错误。 Error creatin
在 this vega 图表,如果我下载并转换 flare-dependencies.json使用以下 jq 到 csv命令, jq -r '(map(keys) | add | unique) as
我正在提交一个项目,我必须在其中创建一个带有表的 mysql 数据库。一切都在我这边进行,所以我只想检查如何将我所有的压缩文件发送给使用不同计算机的人。基本上,我如何为另一台计算机创建我的数据库文件,
我有一个应用程序可以将文本文件写入内部存储。我想仔细看看我的电脑。 我运行了 Toast.makeText 来显示路径,它说:/数据/数据/我的包 但是当我转到 Android Studio 的 An
我喜欢使用 Genymotion 模拟器以如此出色的速度加载 Android。它有非常好的速度,但仍然有一些不稳定的性能。 如何从 Eclipse 中的文件资源管理器访问 Genymotion 模拟器
我需要更改 Silverlight 中文本框的格式。数据通过 MVVM 绑定(bind)。 例如,有一个 int 属性,我将 1 添加到 setter 中的值并调用 OnPropertyChanged
我想向 Youtube Data API 提出请求,但我不需要访问任何用户信息。我只想浏览公共(public)视频并根据搜索词显示视频。 我可以在未经授权的情况下这样做吗? 最佳答案 YouTube
我已经设置了一个 Twilio 应用程序,我想向人们发送更新,但我不想回复单个文本。我只是想让他们在有问题时打电话。我一切正常,但我想在发送文本时显示传入文本,以确保我不会错过任何问题。我正在使用 p
我有一个带有表单的网站(目前它是纯 HTML,但我们正在切换到 JQuery)。流程是这样的: 接受用户的输入 --- 5 个整数 通过 REST 调用网络服务 在服务器端运行一些计算...并生成一个
假设我们有一个名为 configuration.js 的文件,当我们查看内部时,我们会看到: 'use strict'; var profile = { "project": "%Projec
这部分是对 Previous Question 的扩展我的: 我现在可以从我的 CI Controller 成功返回 JSON 数据,它返回: {"results":[{"id":"1","Sourc
有什么有效的方法可以删除 ios 中 CBL 的所有文档存储?我对此有疑问,或者,如果有人知道如何从本质上使该应用程序像刚刚安装一样,那也会非常有帮助。我们正在努力确保我们的注销实际上将应用程序设置为
我有一个 Rails 应用程序,它与其他 Rails 应用程序通信以进行数据插入。我使用 jQuery $.post 方法进行数据插入。对于插入,我的其他 Rails 应用程序显示 200 OK。但在
我正在为服务于发布请求的 API 调用运行单元测试。我正在传递请求正文,并且必须将响应作为帐户数据返回。但我只收到断言错误 注意:数据是从 Azure 中获取的 spec.js const accou
我是一名优秀的程序员,十分优秀!