文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>将lua函数传递给C++

将lua函数传递给C++

时间:2011-05-28  来源:飞鸽(figo)

   前几天看网上看到一篇关于介绍如何将lua函数传递到C++的文章,lua函数作为第一类值(first class),有时需要将它当作参数传给C++。类似这样:

CFun( lua_fun )         -- ok
CFun( function() print(
"Hello world") end ) --ok
local xxx
= function print("Hello world" ) end
CFun( xxx )
--ok

  他的做法是在将lua函数作一个wrap,放入全局表,然后只返回一个代码该函数的函数名,,然后只向C++传递该函数名(一个字符串)。类似这样:

function wrap (fn)
local id
= generate_id()
local fn_s
= "__callback_fn"..id
_G[fn_s]
= fn
return fn_s
end

CFun( wrap( function() xxx end ) )

  这样的确也一种简便的实现方法,但它带来问题是会导致全局表越来越彭涨,而且使用性不强,比如函数名的构建会偏离我们的命名规范。

  之前我也有类似这样的需求,我的做法是在C++中封装一个对象LuaFunction用来代表lua中的函数。当然在lua中同样可以这样传递:

CFun( lua_fun )         -- ok
CFun( function() print(
"Hello world") end ) --ok
local xxx
= function print("Hello world" ) end
CFun( xxx )
--ok

对应的C++的CFun函数是这样定义的:

void  CFun( LuaFunction t )
{
      t.Call<void>();
}

  其中LuaFunction是C++中用来泛指lua函数的一个类,它不必像C++的函数一样不同的函数的声明而有不同函数类型。它的实现类似这样:

// function Object
class LuaFunction
{
public:

        LuaFunction(lua_State* L, int index);
        ~LuaFunction();

        template< typename RVal >
        RVal call( void )
        {
                lua_pushcclosure(m_L, on_error, 0);
                int errfunc = lua_gettop( m_L );
                if( validate() )
                {
                        lua_pushvalue( m_L,m_index );
                        if( lua_isfunction( m_L, -1 ) )
                        {
                                if(lua_pcall( m_L, 0, 1,errfunc ) != 0)
                                {
                                        lua_pop( m_L, 1);
                                }
                        }
                        else
                        {
                                print_error(m_L, "not a lua function." );
                        }
                        lua_remove( m_L,errfunc );
                        return pop<RVal>(m_L);
                }
                lua_remove( m_L,errfunc );
                return RVal();
        }

        template< typename RVal,typename P1 >
        RVal call( P1 p1 )
        {
                lua_pushcclosure(m_L, on_error, 0);
                int errfunc = lua_gettop( m_L );
                if( validate() )
                {
                        lua_pushvalue( m_L, m_index );
                        if( lua_isfunction( m_L, -1 ) )
                        {
                                push( m_L,p1 );
                                if(lua_pcall(m_L, 1, 1,errfunc ) != 0)
                                {
                                        lua_pop(m_L, 1);
                                }
                        }
                        else
                        {
                                print_error(m_L, "not a lua function." );
                        }
                        lua_remove( m_L,errfunc );
                        return pop<RVal>(m_L);
                }

                lua_remove( m_L,errfunc );
                return RVal();
        }

protected:

        bool validate();

        lua_State*              m_L;
        int                             m_index;
        const void*             m_pointer;
        int                             m_ref;
};


LuaFunction::LuaFunction( lua_State* L, int index )
:m_L(L) 
,m_index(index)
,m_ref(0)
{
        m_pointer = lua_topointer(m_L, m_index);
}

LuaFunction::~LuaFunction( void )
{
        if(validate())
        {
                lua_remove(m_L, m_index);
        }
}

bool LuaFunction::validate( void )
{       
        if(m_pointer != NULL)
        {
                if(m_pointer == lua_topointer(m_L, m_index))
                {
                        return true;
                }
                else
                {
                        int top = lua_gettop(m_L);
                        for(int i=1; i<=top; ++i)
                        {
                                if(m_pointer == lua_topointer(m_L, i))
                                {
                                        m_index = i;
                                        return true;
                                }
                        }

                        m_pointer = NULL;
                        return false;
                }
        }
        else
        {
                return false;
        }
}

  单纯做这样的封装话,感觉必要性不是很强,因为它的使用性也不是很多,而且范围不广。我们只能在CFun内部使用,不能保存LuaFunction对象以更后续使用,如:

LuaFunction kRefFun;

CFun(LuaFunction f )
{
   f.Call<void>();
  kRefFun = f;
}

.....
kRefFun.Call<void>();

由于我们无法确定lua什么时候会将该函数对象回收,所以我们再次引用该函数可能早就被lua回收了,那么这将导致kRefFun.Call<void>()会失败,从而kRefFun也就失去意义。仅仅是这样并不满足我们的使用需求,我们总是想能保持这个函数对象,并且只要我们保持了这样函数对象,lua就不会将该对象回收直到 C++自愿释放该对象。为此,lua提供lua_ref接口可以实现这个功能。因为lua的垃圾回收是基于引用数的,只有当一个对象的引用计数为0时,才会将该对象放入回收链表。而lua_ref是会使作用对象的引用计数加1,释放的时候调用lua_unref将引用计数减1即可。加了lua_ref后LuaFunction的实现是这样的:

// function Object
class LuaFunction
{
public:

        LuaFunction(lua_State* L, int index);
        ~LuaFunction();

        template< typename RVal >
        RVal Call( void )
        {
                lua_pushcclosure(m_L, on_error, 0);
                int errfunc = lua_gettop( m_L );
                if( validate() )
                {
                        lua_pushvalue( m_L,m_index );
                        if( lua_isfunction( m_L, -1 ) )
                        {
                                if(lua_pcall( m_L, 0, 1,errfunc ) != 0)
                                {
                                        lua_pop( m_L, 1);
                                }
                        }
                        else
                        {
                                print_error(m_L, "not a lua function." );
                        }
                        lua_remove( m_L,errfunc );
                        return pop<RVal>(m_L);
                }
                lua_remove( m_L,errfunc );
                return RVal();
        }

        template< typename RVal,typename P1 >
        RVal Call( P1 p1 )
        {
                lua_pushcclosure(m_L, on_error, 0);
                int errfunc = lua_gettop( m_L );
                if( validate() )
                {
                        lua_pushvalue( m_L, m_index );
                        if( lua_isfunction( m_L, -1 ) )
                        {
                                push( m_L,p1 );
                                if(lua_pcall(m_L, 1, 1,errfunc ) != 0)
                                {
                                        lua_pop(m_L, 1); 
                                }
                        }
                        else
                        {
                                print_error(m_L, "not a lua function." );
                        }
                        lua_remove( m_L,errfunc );
                        return pop<RVal>(m_L);
                }

                lua_remove( m_L,errfunc );
                return RVal();
        }

protected:

        bool validate();
        bool ref( void );
        bool unref( void );

        lua_State*              m_L;
        int                             m_index;
        const void*             m_pointer;
        int                             m_ref;
};


LuaFunction::LuaFunction( lua_State* L, int index )
:m_L(L) 
,m_index(index)
,m_ref(0)
{
        m_pointer = lua_topointer(m_L, m_index);
        ref();
}

LuaFunction::~LuaFunction( void )
{
        if(validate())
        {
                lua_remove(m_L, m_index);
                unref();
        }
}

bool LuaFunction::validate( void )
{       
        if(m_pointer != NULL)
        {
                if(m_pointer == lua_topointer(m_L, m_index))
                {
                        return true;
                }
                else
                {
                        int top = lua_gettop(m_L);
                        for(int i=1; i<=top; ++i)
                        {
                                if(m_pointer == lua_topointer(m_L, i))
                                {
                                        m_index = i;
                                        return true;
                                }
                        }

                        if( m_ref != 0 )
                        {
                                lua_getref( m_L, m_ref );
                                m_index = lua_gettop( m_L );
                                return true;
                        }
                        m_pointer = NULL;
                        return false;
                }
        }
        else
        {
                return false;
        }
}

bool LuaFunction::ref( void )
{
        if( m_pointer == 0 )
                return false;

        if( validate() )
        {
                m_ref = lua_ref( m_L, m_index );
                return true;
        }
        else
        {
                return false;
        }
}

bool LuaFunction::unref( void )
{
        if( m_pointer == 0 )
                return false;

        lua_unref( m_L, m_ref );
        m_ref = 0;
        return true;
}

  以上只是一个粗劣的封装,并不是保证拿来即可使用,主要是想讲述将lua函数传递给C++的一个实现方案。

相关阅读 更多 +
排行榜 更多 +
翌日波奇狗的历险记手机版下载

翌日波奇狗的历险记手机版下载

休闲益智 下载
怪兽远征安卓版下载

怪兽远征安卓版下载

角色扮演 下载
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载