在C ++中使用的脚本语言中在运行时创建新的类/成员

时间:2016-11-06 16:21:32

标签: c++ lua swig squirrel

我一直在研究这个问题几个月,现在想要真正找到一个合适的解决方案来处理创建新的用户定义类(以及这些类的实例)的情况)在C ++ 11项目的运行时使用成员函数/属性。

到目前为止,我一直在使用SWIG(以前使用Python,现在使用Lua,正在使用Squirrel)。就像我到目前为止遇到的所有C ++绑定/嵌入库(Luna *,luabinder,luabind,OOLua,Sqrat / Sqext,Squall)一样,所有人都希望在代码执行之前用C ++预定义类,因为它们要么依赖在预处理程序指令或模板上。

所以我的问题是,是否有任何库使用更程序化的方法来包装语言,或者是否有任何像Lua或Squirrel这样的好的教程/示例,建议用于处理自定义的创建具有自定义成员和函数的命名类?一些方向将不胜感激。

即使只是一个很好的例子,展示了如何使用函数和属性创建自定义类,在Lua,Squirrel中,通过各自的C ++ API而不使用宏/模板/动态生成的代码,将会非常有用

编辑:我已经创建了一个包含Instance成员键/值对的std::vector类,以及一个识别该类型的成员,因此可以查找函数。但是,在没有使用静态代码的情况下,在Lua / Squirrel中创建简单类的文档非常少。

编辑2:我想要一个适用于任何平台的解决方案,而无需动态链接。

3 个答案:

答案 0 :(得分:1)

创建一个派生自某个现有C ++类的类是我知道将新类带入正在运行的C ++程序的唯一方法。如果没有动态编译实际的C ++源代码并加载生成的库,则无法实际添加新类。接下来最好的事情是在C ++中创建一个包装Python(Lua等)对象的代理对象,并使该Python(Lua)对象成为一个类的实例,该类扩展了镜像到Python(Lua)端的现有C ++类。

        C++

     +---------+         mirrors                   +--------------+
     | class X |  ...............................> | class X      |
     +---------+                                   | mirrored to  |
          |                                        | Python       |
          | inherits                               +--------------+
          v                                      inherits  |
     +-----------------+                                   v
     | class X_Wrapper |        references         +--------------+
     |    | python obj ------------------------->  | class CX(X): |
     +-----------------+                           |    def met() |
                                                   +--------------+

以下是使用boost扩展C ++类的示例,使用boost :: python作为桥梁。

C ++方面:

#include <boost/python.hpp>
#include <iostream>

using namespace boost::python;
// this is the interface we will implement in Python
struct World
{
    virtual std::string greet() = 0;
    virtual ~World() {}
};

// this is a helper class needed to access Python-overrided methods
struct WorldWrap : World, wrapper<World>
{
    std::string greet()
    {
        return this->get_override("greet")();
    }
};

// This function tests our class implemented in Python
std::string test(World* w)
{
    std::cout << "Calling w->greet() on some World-derived object\n";
    return w->greet();
}

// This is what the Python side will see
BOOST_PYTHON_MODULE(hello)
{
    class_<WorldWrap, boost::noncopyable>("World")
            .def("greet", pure_virtual(&World::greet));

    def("test", test);
}

Python方面:

import hello


class HomeWorld(hello.World):
    """ Implements a function defined in C++ as pure virtual """
    def greet(self):
        return "howdy"

home = HomeWorld()
print (hello.test(home))

答案 1 :(得分:1)

考虑遵循Lua multimap示例。

Multimap = {};

function Multimap:__index(key)
    if (key == 'keys') then
        local ret = {}
        for k,_ in pairs(self) do
            ret[#ret+1] = k;
        end
        return ret;
    else
        return rawget(getmetatable(self), key)
    end
end

function Multimap.Create()
    local self = {};
    setmetatable(self, Multimap);
    return self;
end

function Multimap:Insert(key, value)
    local list = self[key];
    if (list == nil) then
        list = {};
        self[key] = list;
    end
    table.insert(list, value);
end

function Multimap:Remove(key, value)
    local list = self[key];
    assert(list ~= nil, "key not found");
    for i = 1,#list do
        if (list[i] == value) then
            table.remove(list, i);
            if (#list == 0) then
                self[key] = nil;
            end
            return;
        end
    end
    error("value not found");
end


-- testing

m = Multimap.Create()
m:Insert(1,5)
m:Insert(2,6)
m:Insert(3,7)
m:Insert(1,8)
m:Remove(2,6)
print(pcall(function() 
    m:Remove(2,6)   -- will produce assert exception
end))

print("keys left: ", table.concat(m.keys, ','))

您可以通过多种方式在C ++中实现此功能。

  1. 使用重型Lua API。下面的代码几乎与Lua完全相同。
  2. #include <Lua/src/lua.hpp>
    
    int Multimap_Index(lua_State* L) {
        lua_settop(L, 2);       // force 2 arguments
    
        const char *key_value = "key";
        size_t key_len;
        const char *key = lua_tolstring(L, 2, &key_len);
        if (!strncmp(key, key_value, strlen(key_value))) {
            int i = 0;
            lua_newtable(L);                // stack : self, key, ret = {}
            int ret = lua_gettop(L);
            lua_pushnil(L);                 // stack : self, key, ret, nil
            while (lua_next(L, 1) != 0) {   // stack : self, key, ret, k, v
                lua_pop(L, 1);              // stack : self, key, ret, k
                lua_len(L, ret);            // stack : self, key, ret, k, #ret
                lua_pushvalue(L, -2);       // stack : self, key, ret, k, #ret, k
                lua_rawseti(L, ret, lua_tointeger(L, -2)+1); // ret[#ret+1] = k ; || stack : self, key, ret, k, #ret
                lua_pop(L, 1);              // stack : self, key, ret, k
            }
            // stack : self, key, ret
            return 1;
        }
        else {
            lua_getmetatable(L, 1);     // stack : self, key, metatable(self)
            lua_pushvalue(L, 2);        // stack : self, key, metatable(self), key
            lua_rawget(L, -2);          // stack : self, key, metatable(self), rawget(metatable(self), key)
            return 1;
        }
    }
    
    int Multimap_Remove(lua_State* L) {
        lua_settop(L, 3);               // force 3 arguments: self, key, value
        lua_checkstack(L, 12);          // reserve 12 arguments on stack (just in case)
        lua_pushvalue(L, 2);            // stack: self, key, value, key
        lua_gettable(L, 1);             // stack: self, key, value, list = self[key]
        if (lua_isnil(L, -1))
            luaL_error(L, "key not found");
        lua_len(L, -1);                 // stack: self, key, value, list, #list
        int count = lua_tointeger(L, -1);
        lua_pop(L, 1);                  // stack: self, key, value, list
        for (int i = 1; i <= count; ++i) {
            lua_rawgeti(L, -1, i);      // stack: self, key, value, list, v = list[i]
            if (lua_compare(L, 3, 5, LUA_OPEQ)) {   // if (list[i] == value)
                lua_getglobal(L, "table");      // stack : self, key, value, list, v, table
                lua_getfield(L, -1, "remove");  // stack : self, key, value, list, v, table, table.remove
                lua_pushvalue(L, 4);
                lua_pushinteger(L, i);          // stack : self, key, value, list, v, table, table.remove, list, i
                lua_call(L, 2, 0);              // table.remove(list, i); || stack : self, key, value, list, v, table
                lua_pushnil(L);
                if (lua_next(L, 4) == 0) {      // if list is empty table
                    lua_pushvalue(L, 2);
                    lua_pushnil(L);
                    lua_settable(L, 1);         // self[key] = nil
                }
                return 0;
            }
        }
        luaL_error(L, "value not found");
    }
    
    int main() {
        auto L = luaL_newstate();
        luaL_openlibs(L);
    
        lua_newtable(L);
        int Multimap = lua_gettop(L);           // Multimap = {}
        lua_pushvalue(L, Multimap);
        lua_setglobal(L, "Multimap");           // _G.Multimap = Multimap;
    
        // option 1: create a C function for operation
        // Multimap.__index = &Multimap_Index
        lua_pushcfunction(L, Multimap_Index);
        lua_setfield(L, Multimap, "__index");
    
        // option 2: compile Lua code and use it
        luaL_loadstring(L,
            "local self = {};\n"
            "setmetatable(self, Multimap);\n"
            "return self;\n"
        );
        lua_setfield(L, Multimap, "Create");    // Multimap.Create = &Multimap_Create
    
        luaL_loadstring(L,
            "local self, key, value = ...;\n"   // initialize local variables from parameters here
            "local list = self[key];\n"
            "if (list == nil) then\n"
            "   list = {};\n"
            "   self[key] = list;\n"
            "end\n"
            "table.insert(list, value);\n"
        );
        lua_setfield(L, Multimap, "Insert");    // Multimap.Create = &Multimap_Insert
    
        lua_pushcfunction(L, Multimap_Remove);
        lua_setfield(L, Multimap, "Remove");    // Multimap.Create = &Multimap_Remove
    
        lua_getfield(L, Multimap, "Create");
        lua_call(L, 0, 1);
        int m = lua_gettop(L);
        lua_getfield(L, m, "Insert");           // stack : m, m.insert
        int Insert = lua_gettop(L);
    
        // m.Insert(m, 1, 5)
        lua_pushvalue(L, Insert);
        lua_pushvalue(L, m);
        lua_pushinteger(L, 1);
        lua_pushinteger(L, 5);
        lua_call(L, 3, 0);
    
        // m.Insert(m, 2, 6)
        lua_pushvalue(L, Insert);
        lua_pushvalue(L, m);
        lua_pushinteger(L, 2);
        lua_pushinteger(L, 6);
        lua_call(L, 3, 0);
    
        // m.Insert(m, 3, 7)
        lua_pushvalue(L, Insert);
        lua_pushvalue(L, m);
        lua_pushinteger(L, 3);
        lua_pushinteger(L, 7);
        lua_call(L, 3, 0);
    
        // m.Insert(m, 1, 8)
        lua_pushvalue(L, Insert);
        lua_pushvalue(L, m);
        lua_pushinteger(L, 1);
        lua_pushinteger(L, 8);
        lua_call(L, 3, 0);
    
        // m.Remove(m, 2, 6)
        lua_getfield(L, m, "Remove");
        lua_pushvalue(L, m);
        lua_pushinteger(L, 2);
        lua_pushinteger(L, 6);
        lua_call(L, 3, 0);
    
        // m.Remove(m, 2, 6)
        lua_getfield(L, m, "Remove");
        lua_pushvalue(L, m);
        lua_pushinteger(L, 2);
        lua_pushinteger(L, 6);
        lua_pcall(L, 3, 0, 0);
        printf("%s\n", lua_tostring(L, -1));
    
        lua_getglobal(L, "table");
        lua_getfield(L, -1, "concat");
        lua_getfield(L, m, "keys");
        lua_pushstring(L, ",");
        lua_call(L, 2, 1);
        printf("keys left: %s\n", lua_tostring(L, -1));
    
        lua_close(L);
    
        return 0;
    }
    
    1. 或者你可以使用使用std :: multimap的Lua userdata(我需要另外一个小时才能实现这个,所以问你是否真的需要它 - 从你的问题中找不到)

答案 2 :(得分:-1)

免责声明:我发布此贡献作为答案,因为我没有足够的声誉点来添加评论。

评论:撇开与特定脚本语言绑定的问题,您似乎面临C++语言的基本限制:它不是&#34; { {3}}&#34; (正如其他评论所指出的那样)。也就是说,该语言不提供任何扩展或修改已编译程序的功能。

但也许所有的希望都不会丢失。在网上搜索&#34; c ++动态加载&#34;揭示了一些系统(例如Linux和Windows)似乎确实实现了dynamic机制。

以下是关于该主题的两篇(旧)文章的链接。

    Linux日志中的
  1. dynamic loading
  2. Dr.Dobb的
  3. Dynamic Class Loading for C++ on Linux
  4. 乍一看,它们看起来很有趣。不过,我不确定它们是否仍然相关。

    这只是在黑暗中拍摄的。