使用直的Lua,我如何公开一个现有的C ++类对象在Lua脚本中使用? [英] Using straight Lua, how do I expose an existing C++ class objec for use in a Lua script?

查看:371
本文介绍了使用直的Lua,我如何公开一个现有的C ++类对象在Lua脚本中使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在进一步丰富我在C ++中嵌入Lua脚本的经验,
,我可以在这里使用手。



考虑下面两个类:

  // Person.hpp 
#pragma once
#include < string>

class Person {
private:
std :: string p_Name;
int p_Age;

public:
Person(const std :: string& strName,const int& intAge)
:p_Name(strName),p_Age(intAge){}

Person():p_Name(),p_Age(0){}

std :: string getName()const {return p_Name; }
int getAge()const {return p_Age; }

void setName(const std :: string& strName){p_Name = strName; }
void setAge(const int& intAge){p_Age = intAge; }
};

...和...

  // PersonManager.hpp 
#pragma once
#includePerson.hpp
#include< vector>

class PersonManager {
//假设这个类是单例,因此
//没有公共构造函数,但是一个静态函数返回
/ / singleton实例。
private:
std :: vector< Person *> pm_People;

public:
bool personExists(const std :: string& strName){/ * ... * /}
bool addPerson(const std :: string& strName ,const int& intAge){/ * ... * /}
Person * getPerson(const std :: string& strName){/ * ... * /}
void removePerson std :: string& strName){/ * ... * /}
void removeAllPeople(){/ * ... * /}
};

...其中 getPerson code> pm_People 向量以查看具有指定名称的人是否存在,使用 personExists



现在,考虑下面的函数,从Lua获取 Person 对象并返回其年龄。



< pre class =lang-cpp prettyprint-override> // Lua_Person.cpp
#includeLua_Person.hpp//Lua_Person.hpp声明了调用的函数, 函数到Lua。
#includePersonManager.hpp
#includePerson.hpp

int lua_GetPersonAge(lua_State * LS){
//验证userdata。
luaL_checktype(LS,1,LUA_TUSERDATA);

//获取Personuserdata。
Person * luaPerson = reinterpret_cast< Person *>(lua_touserdata(LS,1));

//检查Person指针是否不为null。
if(luaPerson == nullptr)
luaL_error(LS,lua_GetPersonAge:你给了一个空指针!

//将人的年龄推到Lua堆栈上。
lua_pushnumber(LS,luaPerson-> getAge());

//返回年龄整数。
return 1;
}

我想要做的是获得一个已经实例化和现有的<$使用 getPerson PersonManager 单例中的c $ c> Person 对象到Lua,
所以我可以这样做:

  local testPerson = People.get(Stack Overflower)
print(testPerson:getAge())

我尝试类似下面的代码块,无效:

  int lua_GetPerson(lua_State * LS ){
//验证传递的参数。
luaL_checktype(LS,1,LUA_TSTRING);

//获取字符串。
std :: string personName = lua_tostring(LS,1);

//验证此人是否存在。
if(PersonManager :: getInstance()。personExists(personName)== false)
luaL_error(LS,lua_GetPerson:此ID不存在:%s,personName.c_str

//将新的userdata放入双指针,并将其分配给已经存在的Person对象。
Person ** p = static_cast< Person **>(lua_newuserdata(LS,sizeof(Person *)))); //< Userdata>
* p = PersonManager :: getInstance()。getPerson(personName);

//将该对象放入Meta_Person元表中。
//假设metatable是在使用Lua注册Person / Person Manager函数时创建的。
luaL_getmetatable(LS,Meta_Person); //< Metatable>,< Userdata>
lua_setmetatable(LS,-2); //< Metatable>

//返回metatable。
return 1;
}

任何人都可以在这里伸出援助之手,方向?
我没有使用任何lua包装库,只是直接Lua。



谢谢。



编辑:我用来公开我的 Person PersonManager 函数的函数如下:

  void exposePerson(lua_State * LS){
static const luaL_reg person_functions [] = {
{ getAge,lua_getPersonAge},
{nullptr,nullptr}
};

luaL_newmetatable(LS,Meta_Person);
lua_pushstring(LS,__index);
lua_pushvalue(LS,-2);
lua_settable(LS,-3);

luaL_openlib(LS,nullptr,person_functions,0);
}

void exposePersonManager(lua_State * LS){
static const luaL_reg pman_functions [] = {
{get,lua_getPerson},
{ nullptr,nullptr}
};

luaL_openlib(LS,People,pman_functions,0);

lua_pop(LS,1);
}


解决方案

即在Lua中注册 PersonManager 。由于它是一个单例,我们将它注册为一个全局。

  void registerPersonManager(lua_State * lua)
{
//首先,我们创建一个userdata实例,它将保存指向我们单例的指针
PersonManager ** pmPtr =(PersonManager **)lua_newuserdata(
lua,sizeof(PersonManager *));
* pmPtr = PersonManager :: getInstance(); //假设是
//返回单例实例的函数

//现在我们为该对象创建metatable
luaL_newmetatable(lua,PersonManagerMetaTable);
//你通常应该检查,如果表是新创建的或不是,但
//因为它是一个单例,我不会打扰。

//表现在在堆栈的顶部。
//因为我们希望Lua在metatable中查找PersonManager的方法,
//我们必须将它引用为__indexmetamethod

lua_pushvalue(lua, 1);
lua_setfield(lua,-2,__index);
// lua_setfield将值从堆栈的顶部弹出并分配给我们的
//字段。因此,lua_pushvalue,它只是将我们的表复制在堆栈顶部。
//当我们调用lua_setfield时,Lua弹出我们对表的第一个引用,
//将它作为__index字段存储在我们的表中,这也是第二个
// topmost位置。
//这部分是至关重要的,因为没有__index字段,Lua不知道
//在哪里查找PersonManager的方法

luaL_Reg personManagerFunctions [] = {
'get',lua_PersonManager_getPerson,
nullptr,nullptr
};

luaL_register(lua,0,personManagerFunctions);

lua_setmetatable(lua,-2);

lua_setglobal(lua,PersonManager);
}

现在我们处理 PersonManager get 方法:

  int lua_PersonManager_getPerson(lua_State * lua) 
{
//记住第一次修改应该是你的PersonManager
//实例的userdata,就像在Lua中一样,你将调用PersonManager:getPerson(Stack Overflower);
//通常我会首先检查,如果第一个参数是userdata与metatable
//调用PersonManagerMetaTable,出于安全原因

PersonManager ** pmPtr =(PersonManager **)luaL_checkudata (
lua,1,PersonManagerMetaTable);
std :: string personName = luaL_checkstring(lua,2);

Person * person =(* pmPtr) - > getPerson(personName);
if(person)
registerPerson(lua,person); //注册person的函数。
//函数被调用后,新创建的Person
//对象在堆栈顶部
else
lua_pushnil(lua);

return 1;
}

void registerPerson(lua_State * lua,Person * person)
{
//我们假设这个人是一个有效的指针
Person * * pptr =(Person **)lua_newuserdata(lua,sizeof(Person *));
* pptr = person; //将指针存储在userdata中。您必须注意确保
//指针在整个时间内有效,Lua可以访问它。

if(luaL_newmetatable(lua,PersonMetaTable))//这很重要。因为你
//可能会调用它多次,你应该检查,表是新的
//创建还是已经存在
{
//表是新创建的,所以我们注册它的函数
lua_pushvalue(lua,-1);
lua_setfield(lua,-2,__index);

luaL_Reg personFunctions [] = {
getAge,lua_Person_getAge,
nullptr,nullptr
};
luaL_register(lua,0,personFunctions);
}

lua_setmetatable(lua,-2);
}

最后处理 Person getAge

  int lua_Person_getAge(lua_State * lua)
{
Person ** pptr =(Person **)lua_checkudata(lua,1,PersonMetaTable);

lua_pushnumber(lua,(* pptr) - > getAge());
return 1;
}

现在您应该调用 registerPersonManager

现在在Lua中,你应该可以这样做:

在执行Lua代码之前,你最好只是在创建新的Lua状态和打开需要的库后。 p>

  local person = PersonManager:getPerson(Stack Overflower); 
print(person:getAge());

我目前无法访问Lua或C ++来测试它,让你开始。请谨慎使用
Person 指针的有效期。

I've been furthering my experience in embedding Lua scripting in C++, and I could use a hand, here.

Consider the following two classes:

// Person.hpp
#pragma once
#include <string>

class Person {
    private:
        std::string p_Name;
        int p_Age;

    public:
        Person(const std::string & strName, const int & intAge)
            : p_Name(strName), p_Age(intAge) { }

        Person() : p_Name(""), p_Age(0) { }

        std::string getName() const { return p_Name; }
        int getAge() const { return p_Age; }

        void setName(const std::string & strName) { p_Name = strName; }
        void setAge(const int & intAge) { p_Age = intAge; }
};

... and ...

// PersonManager.hpp
#pragma once
#include "Person.hpp"
#include <vector>

class PersonManager {
    // Assume that this class is a singleton, and therefore
    // has no public constructor, but a static function that returns the
    // singleton instance.
    private:
        std::vector<Person *> pm_People;

    public:
        bool personExists(const std::string & strName) { /* ... */ }
        bool addPerson(const std::string & strName, const int & intAge) { /* ... */ }
        Person * getPerson(const std::string & strName) { /* ... */ }
        void removePerson(const std::string & strName) { /* ... */ }
        void removeAllPeople() { /* ... */ }
};

... where getPerson checks the pm_People vector to see if the person with the specified name exists, using personExists.

Now, consider the following function that gets a Person object from Lua and returns its age.

// Lua_Person.cpp
#include "Lua_Person.hpp"       // "Lua_Person.hpp" declares the function called to expose the "Person" functions to Lua.
#include "PersonManager.hpp"
#include "Person.hpp"

int lua_GetPersonAge(lua_State * LS) {
    // Validate the userdata.
    luaL_checktype(LS, 1, LUA_TUSERDATA);

    // Get the "Person" userdata.
    Person * luaPerson = reinterpret_cast<Person *>(lua_touserdata(LS, 1));

    // Check to see if the Person pointer is not null.
    if(luaPerson == nullptr)
        luaL_error(LS, "lua_GetPersonAge: You gave me a null pointer!");

    // Push the person's age onto the Lua stack.
    lua_pushnumber(LS, luaPerson->getAge());

    // Return that age integer.
    return 1;
}

What I want to do is to get an already-instantiated and existing Person object from the PersonManager singleton, using getPerson, and expose that object to Lua, so I can do something like this:

local testPerson = People.get("Stack Overflower")
print(testPerson:getAge())

I tried something like the code block below, to no avail:

int lua_GetPerson(lua_State * LS) {
    // Validate the argument passed in.
    luaL_checktype(LS, 1, LUA_TSTRING);

    // Get the string.
    std::string personName = lua_tostring(LS, 1);

    // Verify that the person exists.
    if(PersonManager::getInstance().personExists(personName) == false)
        luaL_error(LS, "lua_GetPerson: No one exists with this ID: %s", personName.c_str());

    // Put a new userdata into a double pointer, and assign it to the already existing "Person" object requested.
    Person ** p = static_cast<Person **>(lua_newuserdata(LS, sizeof(Person *)));    // <Userdata>
    *p = PersonManager::getInstance().getPerson(personName);

    // Put that person object into the "Meta_Person" metatable.
    // Assume that metatable is created during the registration of the Person/Person Manager functions with Lua.
    luaL_getmetatable(LS, "Meta_Person");   // <Metatable>, <Userdata>
    lua_setmetatable(LS, -2);               // <Metatable>

    // Return that metatable.
    return 1;
}

Can anybody lend a helping hand here, or at least point me in the right direction? I am not using any lua wrapper libraries, just straight Lua.

Thank you.

EDIT: The functions that I use to expose my Person and PersonManager functions are as follows:

void exposePerson(lua_State * LS) {
    static const luaL_reg person_functions[] = {
        { "getAge", lua_getPersonAge },
        { nullptr, nullptr }
    };

    luaL_newmetatable(LS, "Meta_Person");
    lua_pushstring(LS, "__index");
    lua_pushvalue(LS, -2);
    lua_settable(LS, -3);

    luaL_openlib(LS, nullptr, person_functions, 0);
}

void exposePersonManager(lua_State * LS) {
    static const luaL_reg pman_functions[] = {
        { "get", lua_getPerson },
        { nullptr, nullptr }
    };

    luaL_openlib(LS, "People", pman_functions, 0);

    lua_pop(LS, 1);
}

解决方案

Let's start off the top, that is by registering PersonManager in Lua. Since it's a singleton, we'll register it as a global.

void registerPersonManager(lua_State *lua)
{
    //First, we create a userdata instance, that will hold pointer to our singleton
    PersonManager **pmPtr = (PersonManager**)lua_newuserdata(
        lua, sizeof(PersonManager*));
    *pmPtr = PersonManager::getInstance();  //Assuming that's the function that 
                                            //returns our singleton instance

    //Now we create metatable for that object
    luaL_newmetatable(lua, "PersonManagerMetaTable");
    //You should normally check, if the table is newly created or not, but 
    //since it's a singleton, I won't bother.

    //The table is now on the top of the stack.
    //Since we want Lua to look for methods of PersonManager within the metatable, 
    //we must pass reference to it as "__index" metamethod

    lua_pushvalue(lua, -1);
    lua_setfield(lua, -2, "__index");
    //lua_setfield pops the value off the top of the stack and assigns it to our 
    //field. Hence lua_pushvalue, which simply copies our table again on top of the stack.
    //When we invoke lua_setfield, Lua pops our first reference to the table and 
    //stores it as "__index" field in our table, which is also on the second 
    //topmost position of the stack.
    //This part is crucial, as without the "__index" field, Lua won't know where 
    //to look for methods of PersonManager

    luaL_Reg personManagerFunctions[] = {
         'get', lua_PersonManager_getPerson,
          nullptr, nullptr
    };

    luaL_register(lua, 0, personManagerFunctions);

    lua_setmetatable(lua, -2);

    lua_setglobal(lua, "PersonManager");
}

Now we handle PersonManager's get method:

int lua_PersonManager_getPerson(lua_State *lua)
{
    //Remember that first arbument should be userdata with your PersonManager 
    //instance, as in Lua you would call PersonManager:getPerson("Stack Overflower");
    //Normally I would first check, if first parameter is userdata with metatable 
    //called PersonManagerMetaTable, for safety reasons

    PersonManager **pmPtr = (PersonManager**)luaL_checkudata(
        lua, 1, "PersonManagerMetaTable");
    std::string personName = luaL_checkstring(lua, 2);

    Person *person = (*pmPtr)->getPerson(personName);
    if (person)
        registerPerson(lua, person);    //Function that registers person. After 
                //the function is called, the newly created instance of Person 
                //object is on top of the stack
    else
        lua_pushnil(lua);

    return 1;
}

void registerPerson(lua_State *lua, Person *person)
{
    //We assume that the person is a valid pointer
    Person **pptr = (Person**)lua_newuserdata(lua, sizeof(Person*));
    *pptr = person; //Store the pointer in userdata. You must take care to ensure 
                    //the pointer is valid entire time Lua has access to it.

    if (luaL_newmetatable(lua, "PersonMetaTable")) //This is important. Since you 
        //may invoke it many times, you should check, whether the table is newly 
        //created or it already exists
    {
        //The table is newly created, so we register its functions
        lua_pushvalue(lua, -1);
        lua_setfield(lua, -2, "__index");

        luaL_Reg personFunctions[] = {
            "getAge", lua_Person_getAge,
            nullptr, nullptr
        };
        luaL_register(lua, 0, personFunctions);
    }

    lua_setmetatable(lua, -2);
}

And finally handling Person's getAge.

int lua_Person_getAge(lua_State *lua)
{
    Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable");

    lua_pushnumber(lua, (*pptr)->getAge());
    return 1;
}

You should now call registerPersonManager before executing your Lua code, best just after you create new Lua state and open needed libraries.

Now within Lua, you should be able to do that:

local person = PersonManager:getPerson("Stack Overflower");
print(person:getAge());

I don't have access to either Lua or C++ at the moment to test it, but that should get you started. Please be careful with lifetime of the Person pointer you give Lua access to.

这篇关于使用直的Lua,我如何公开一个现有的C ++类对象在Lua脚本中使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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