静态库和共享对象是共享符号吗?

时间:2013-08-16 09:31:53

标签: c++ lua

我正在创建一个帮助程序来在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;
}

总结:

  • Common.cpp,Common.h编译为简单的静态库(libcommon.a)
  • Main.cpp,编译并链接到libcommon.a
  • Battery.cpp,编译为共享对象并链接到libcommon.a

退出时的主要段错误,核心文件说它位于std :: function&lt;&gt;的析构函数中所以我猜它在同一个指针上被多次调用,是吗?

静态库数据是否在所有代码中共享?我该如何避免这个问题?

核心的开始

#0  0x0000000000404062 in std::__1::function<void (void*, void*)>::~function() ()
#1  0x0000000000404025 in std::__1::function<void (void*, void*)>::~function() ()

下一条跟踪无法读取且无法使用。

1 个答案:

答案 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