前几天看网上看到一篇关于介绍如何将lua函数传递到C++的文章,lua函数作为第一类值(first class),有时需要将它当作参数传给C++。类似这样:
CFun( lua_fun ) -- okCFun( function() print("Hello world") end ) --oklocal xxx = function print("Hello world" ) endCFun( xxx ) --ok
他的做法是在将lua函数作一个wrap,放入全局表,然后只返回一个代码该函数的函数名,,然后只向C++传递该函数名(一个字符串)。类似这样:
function wrap (fn) local id = generate_id() local fn_s = "__callback_fn"..id _G[fn_s] = fn return fn_sendCFun( wrap( function() xxx end ) )
这样的确也一种简便的实现方法,但它带来问题是会导致全局表越来越彭涨,而且使用性不强,比如函数名的构建会偏离我们的命名规范。
之前我也有类似这样的需求,我的做法是在C++中封装一个对象LuaFunction用来代表lua中的函数。当然在lua中同样可以这样传递:
CFun( lua_fun ) -- okCFun( function() print("Hello world") end ) --oklocal xxx = function print("Hello world" ) endCFun( 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
// 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++的一个实现方案。
原文链接: https://www.cnblogs.com/zhengyifei/archive/2011/05/28/2060674.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/26308
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!