在运行时使用C ++中使用的脚本语言创建新的类/成员 [英] Creating new classes/members at run-time in scripting languages used in C++

查看:71
本文介绍了在运行时使用C ++中使用的脚本语言创建新的类/成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经不停地解决这个问题了几个月,现在想真正提出一个合适的解决方案,以解决使用以下方法创建新的用户定义类(以及这些类的实例)的情况C ++ 11项目中运行时的成员函数/属性.

I've been working on this problem off and on for a few months, and now wanted to really come up with a proper solution that will handle the case of creating new user-defined classes (and instances of those classes) with member functions/properties at run-time in a C++11 project.

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

So far, I've been using SWIG (formerly with Python, now with Lua, exploring Squirrel). Like all the C++ binding/embedding libraries I've encountered so far (Luna*, luabinder, luabind, OOLua, Sqrat/Sqext, Squall), all expect your classes to be predefined in C++ prior to code execution because they either rely on preprocessor directives or templates.

所以我的问题是,有没有使用更程序化的方法来包装语言的库,或者对于Lua或Squirrel之类的东西有很好的教程/示例,有人会建议他们处理自定义的创建自定义成员和函数的命名类?某些方向将不胜感激.

So my question is, are there any libraries out there that use a more procedural approach to wrapping a language, or are there any good tutorials/examples for something like Lua or Squirrel, that one would recommend for handling the creation of custom-named classes with custom members and functions? Some direction would be greatly appreciated.

即使只是一个很好的示例,它展示了如何在Lua,Squirrel中通过各自的C ++ API在不使用宏/模板/动态生成的代码的情况下在Lua,Squirrel中创建具有函数和属性的自定义类,也将大有帮助.

Even simply a good example showing how to create a custom class with a function and a property, in either Lua, Squirrel, via their respective C++ APIs without the use of macros/templates/dynamically-generated code, would be hugely helpful.

我所做的只是创建一个Instance类,该类包含成员键/值对的std::vector和一个成员,该成员标识类型,以便可以查找功能.但是,关于在Lua/Squirrel中创建简单类而不使用静态代码的文献很少.

I have gone as far as creating an Instance class that contains a std::vector of members key/value pairs, and a member identifying the type so functions can be looked up. However, there is very little documentation out there on creating simple classes in Lua/Squirrel without the use of static code.

我想要一个可以在任何平台上运行且无需动态链接的解决方案.

EDIT 2: I would like a solution that works on any platform and without having to dynamically link.

推荐答案

考虑以下Lua多重地图示例.

Consider following Lua multimap example.

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 ++的几种方式来实现它.

You can implement this in C++ in several ways.

  1. 使用沉重的Lua API.下面的代码几乎与Lua完全一样.

#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用户数据(我需要再花一个小时来实现这一点,所以请问您是否真的需要-但这不是您的问题所致)

这篇关于在运行时使用C ++中使用的脚本语言创建新的类/成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆