我正在创建一个帮助程序来在C ++类和Lua对象之间创建多重继承。因为Lua将C / C ++用户对象存储为void *,所以在检索对象时很难进行安全转换。
例如,
如果你有
class A { }
class B { }
class C : public A, public B { }
然后将类型C的对象传递给Lua,传递C实例的地址,当需要将其转换为B时,C ++编译器会自动将指针对齐到C中B的位置,因此,将void *指针从C直接转换为B是不安全的。
为了避免这个问题,我使用了一种转换器。在Lua中,对象包含它们的名称作为字符串,因此当您需要将对象从类型转换为其他类型时,它使用如下转换器:
converter [“B”] [“C”](mypointer,myresultpointer);
这是帮助创建这些转换器的类:
// Common.h
#include <functional>
#include <memory>
#include <unordered_map>
#include <string>
typedef std::function<void (void *, void *)> LuaConverter;
typedef std::unordered_map<
std::string,
std::unordered_map<
std::string,
LuaConverter
>
> LuaConverters;
class LuaeClass {
public:
static LuaConverters converters;
public:
template <class From, class To>
static void createConverter(const std::string &fromName,
const std::string &toName)
{
converters[toName][fromName] = [&] (void *ptr, void *result) -> void {
std::shared_ptr<From> *from = static_cast<std::shared_ptr<From> *>(ptr);
*((std::shared_ptr<To> *)result) = std::static_pointer_cast<To>(*from);
};
}
};
此类编译为静态库,可在项目中多次使用。
对象需要作为shared_ptr传递(它还解决了所有权和删除问题)。但是,它在使用静态库时会出现段错误。
然后,我有一个简单的模块电池,编译为共享对象并链接到公共库。
对于示例的范围,它不包含太多函数,但它实际上使用了LuaeClass:
// Battery.cpp
#include <lua.hpp>
#include "Common.h"
class Battery {
public:
int getPercent() {
return 100;
}
};
extern "C" int luaopen_battery(lua_State *L)
{
LuaeClass::createConverter<Battery, Battery>("Battery", "Battery");
return 0;
}
这被编译为名为battery.so的共享对象,Lua将使用dlopen()和dlcose()来加载它。
最后,主要。它也链接到common并使用它来创建对象。
// main.cpp
#include <iostream>
#include <memory>
#include <string>
#include <lua.hpp>
#include "Common.h"
using namespace std;
class LuaDeleter {
public:
void operator()(lua_State *L) {
lua_close(L);
}
};
typedef unique_ptr<lua_State, LuaDeleter> LuaState;
int main(void)
{
LuaState L(luaL_newstate());
luaL_requiref(L.get(), "_G", luaopen_base, 1);
luaL_requiref(L.get(), "package", luaopen_package, 1);
// This will dlopen() and dlclose()
string code = "local battery = require \"battery\"";
LuaeClass::createConverter<int, int>("Int", "Int");
if (luaL_dostring(L.get(), code.c_str()) != LUA_OK) {
cerr << lua_tostring(L.get(), -1) << endl;
}
return 0;
}
总结:
退出时的主要段错误,核心文件说它位于std :: function&lt;&gt;的析构函数中所以我猜它在同一个指针上被多次调用,是吗?
静态库数据是否在所有代码中共享?我该如何避免这个问题?
核心的开始
#0 0x0000000000404062 in std::__1::function<void (void*, void*)>::~function() ()
#1 0x0000000000404025 in std::__1::function<void (void*, void*)>::~function() ()
下一条跟踪无法读取且无法使用。
答案 0 :(得分:0)
静态库的代码和全局/静态数据将被注入到链接它的每个模块中。因此,对于您的情况,项目中存在多个LuaeClass::converters
个实例。您需要在链接静态库的每个模块中调用luaopen_battery()
。
我不确定您的崩溃是否与静态链接有关,但我确信您已经进行了复杂的实施。
您要解决的第一个问题是将void*
安全地转换为A*, B*, C*
。要导出到Lua的类/接口?如果是class C
,您可以在C类中定义以下方法:
void pushThis(lua_State *L);
static C* getThis(lua_State *L, int idx);
两种方法都使用C*
,因此您不需要转换函数。你可以use meta table to distinguish your pointers from other userdata。如果您需要B*
,只需:
B* b = (B*)C::getThis(L, idx);
你可能真的不需要shared_ptr。当GC收集Lua对象时,shared_ptr对删除C ++对象没有帮助(因为shared_ptr仍然存在于堆中)。相反,您必须implement a __gc
callback in the meta table to delete your object。