favorite 发表于 2020-11-25 09:03

slua unreal分析(四)LuaArray&LuaMap

相关文章:
之前介绍的UObject,UStruct传递都是单个元素传递,slua也支持array和map的容器传递。
LuaArray

UE中数组类型为TArray,把TArray从UE传到lua,会用一个LuaArray类把TArray包装起来,LuaArray也提供了迭代接口供lua使用数组常用操作。
TArray传到Lua时有两种创建LuaArray方式,一种会把TArray复制一份,Lua中对LuaArray操作不会影响C++TArray,另一种是把TArray引用传到Lua。Lua对LuaArray的添加和删除会影响C++的TArray。


复制一份
当TArray作为函数的返回值时,会复制一份,相关代码如下:
构造函数接受两个参数,分别为数组元素的UProperty,和源array数据。
LuaArray::LuaArray(UProperty* p, FScriptArray* buf)
    : inner(p)
    , prop(nullptr)
    , propObj(nullptr)
{
    array = new FScriptArray();
    clone(array, p, buf);
}

void LuaArray::clone(FScriptArray* destArray, UProperty* p, const FScriptArray* srcArray) {
    // blueprint stack will destroy the TArray
    // so deep-copy construct FScriptArray
    // it's very expensive
    if(!srcArray || srcArray->Num()==0)
      return;
      
    FScriptArrayHelper helper = FScriptArrayHelper::CreateHelperFormInnerProperty(p,destArray);
    helper.AddValues(srcArray->Num());
    uint8* dest = helper.GetRawPtr(;
    uint8* src = (uint8*)srcArray->GetData();
    for(int n=0;n<srcArray->Num();n++) {
      p->CopySingleValue(dest,src);
      dest+=p->ElementSize;
      src+=p->ElementSize;
    }
}源array类型为FScriptArray,可以认为是一个数组,通过遍历FScriptArray可以把元素逐个复制到luaarray的array属性中。因为我们已经知道了数组内元素的UProperty类型,因此可用对应的CopySingleValue方法利用反射机制来拷贝元素。


传递引用
把UObject上的TArray属性进行传递时,会使用传递引用的方式。LuaArray构造函数中,并没有创建新的FScriptArray,而是把Tarray的指针赋给了array,因此Lua中操作的就是TArray本身,对LuaArray的add和remove操作都会影响到实际的TArray。
LuaArray::LuaArray(UArrayProperty* p, UObject* obj)
    : inner(p->Inner)
    , prop(p)
    , propObj(obj)
{
    array = prop->ContainerPtrToValuePtr<FScriptArray>(obj);
}

数组迭代
LuaArray实现了基本的add,remove,迭代等功能,前面的相对简单,主要介绍下迭代功能。
Lua中,可以通过for in pairs语法遍历LuaArray中的元素:
for i,v in pairs(arr) do
    print("arr item",i,v)
endslua提供了LuaArray::Enumerator类作为迭代器,LuaArray::Enumerable作为迭代函数,来实现pairs功能,迭代函数内容如下:
int LuaArray::Enumerable(lua_State* L) {
    CheckUD(LuaArray::Enumerator, L, 1);
    auto arr = UD->arr;
    if (arr->isValidIndex(UD->index)) {
      auto element = arr->inner;
      auto es = element->ElementSize;
      auto parms = ((uint8*)arr->array->GetData()) + UD->index * es;
      LuaObject::push(L, UD->index);
      LuaObject::push(L, element, parms);
      UD->index += 1;
      return 2;
    }
    return 0;
}UD就是Enumerator,index为当前遍历到的下标,根据下标和数组,直接去ScriptArray对应的内存偏移取元素指针,返回给lua。
LuaArray与GC
LuaArray会对内部的元素产生引用,UEGC会看到这些引用,当LuaArray销毁,这些引用也会消失。因为LuaArray继承自FGCObject,内部实现了AddReferencedObjects()函数,每次UE执行GC时,都会调用这个函数。函数内部通过之前介绍的LuaReference方法,模拟一遍UE的引用添加逻辑。


LuaMap

LuaMap与LuaArray的实现思想基本相同,也实现了Map的add,remove等常用功能,最大的区别可能是LuaMap底层数据为ScriptMap散列表,这也使LuaMap在一些处理上要小心对待。
如下图所示,ScriptArray和ScriptMap都存储了3个元素,容器中也都留有空位。对于ScriptArray,做clone、迭代等操作时只要遍历前三个位置即可,也不需要判断前三个位置元素是否有效,因为它们肯定是有效的。而ScriptMap由于是散列表,因此clone、迭代时必须把整个容器都遍历一遍,还有判断当前元素是否有效,过滤掉空的槽位。
页: [1]
查看完整版本: slua unreal分析(四)LuaArray&LuaMap