分享

aau中的类 与结构

 quasiceo 2014-01-10
lua 实现类的继承有三种方法。
第一种拷贝法,把父类的成员拷贝到子类。对象拥有所有成员的独立拷贝。
第二种原型共享法,对象只拥有各自私有的成员如变量,有共性的成员如函数放到原型,元表中。所有该类的对象共享。
第三种混入法,或者掺元法。结合以上两种。开始是原型共享,但当访问到某个父类中才有的变量时,就将其拷贝一份到当前对象,
以后就访问此局部拷贝。


我的想法:
类的数据成员是固定的。
类的方法在一个表中。
所有对象拥有各自的数据成员,拥有共享的方法表。

现在的做法,数据成员和方法混在一个表里。

想象一下
对表的抽象 容器。
如何访问重载。
可以如数组那样顺序存放。
用数字索引。
可以像结构一样,用名字做索引。//偏移优化。偏移模式,可以jit优化。
可以用字符串做索引。
用c++扩展类型,使其像原生类一样操作。

词法,语法分开,两遍式编译。便于增加,修改新的词法元素。

类型信息中说明其是否是属性,是有相应的getter,setter.

可以像表一样。metatable。
现在的做法是对表中不存在的元素才去元表中找__index   ,  __newindex

aau
普通类采用拷贝法。
util.declare采用原型共享法。


结构体(struct table)

在table构造器中中以添加静态类型声明,使用类似于C语言中的语法声明API函数需要的结构体(struct)。
struct中所有成员应当赋于初始值.

通常,我们使用class来定义API函数中需要用到的结构体:

class POINT{
int x = 0; //结构体成员必须设定初始值
int y = 0;
}

//创建一个结构体对象
pt = POINT();

//每个结构体会创建一个_struct只读字段记录类型定义,语义如下:
pt = { _struct = "int x;int y" }



////////////////////////////////////////////////////
//在API函数中使用结构体
//=====================================================

//导入DLL
User32 := raw.loadDll("User32.dll");
//声明API函数
GetCursorPos := User32.api( "GetCursorPos", "word(struct& point)")

//通常,我们使用class来定义API函数中需要用到的结构体:
class Point {
int x=0;
int y=0;
};

/* AAuto 允许在table或class成员的键名字前面添加一个描述性标识符,标识符遵循普通变量命名规则。
可以通过"_struct" 只读字段再次读取所有的API数据类型描述。*/

//创建一个新的结构体
p = Point();

//使用API函数
GetCursorPos(p);

//p是按引用传递的参数
io.print(p.x,p.y)
///////////////////////////////////////////////////////////////////////////////////////////////////////
例子
import mouse;
import console;

class POINT{
     int x;
     int y;
     print = function(){
         self.print( this.x,this.y );
     }
}

namespace POINT {
     print = ..console.log;
}

//创建结构体对象
var point = POINT();

//调用 WINAPI
::GetCursorPos(point)
//::GetCursor = u.api("GetCursor","pointer()")

//调用对象成员函数
point.print()
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

lua实现
http:///ext_ffi_tutorial.html

Defining Metamethods for a C Type

The following code explains how to define metamethods for a C type. We define a simple point type and add some operations to it:

local ffi = require("ffi")
ffi.cdef[[
typedef struct { double x, y; } point_t;
]]

local point
local mt = {
  __add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
  __len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
  __index = {
    area = function(a) return a.x*a.x + a.y*a.y end,
  },
}
point = ffi.metatype("point_t", mt)

local a = point(3, 4)
print(a.x, a.y)  --> 3  4
print(#a)        --> 5
print(a:area())  --> 25
local b = a + point(0.5, 8)
print(#b)        --> 12.5

Here's the step-by-step explanation:

This defines the C type for a two-dimensional point object.

We have to declare the variable holding the point constructor first, because it's used inside of a metamethod.

Let's define an __add metamethod which adds the coordinates of two points and creates a new point object. For simplicity, this function assumes that both arguments are points. But it could be any mix of objects, if at least one operand is of the required type (e.g. adding a point plus a number or vice versa). Our __len metamethod returns the distance of a point to the origin.

If we run out of operators, we can define named methods, too. Here the __index table defines an area function. For custom indexing needs, one might want to define __index and __newindex functions instead.

This associates the metamethods with our C type. This only needs to be done once. For convenience, a constructor is returned by ffi.metatype(). We're not required to use it, though. The original C type can still be used e.g. to create an array of points. The metamethods automatically apply to any and all uses of this type.

Please note that the association with a metatable is permanent and the metatable must not be modified afterwards! Ditto for the __index table.

Here are some simple usage examples for the point type and their expected results. The pre-defined operations (such as a.x) can be freely mixed with the newly defined metamethods. Note that area is a method and must be called with the Lua syntax for methods: a:area(), not a.area().

The C type metamethod mechanism is most useful when used in conjunction with C libraries that are written in an object-oriented style. Creators return a pointer to a new instance and methods take an instance pointer as the first argument. Sometimes you can just point __index to the library namespace and __gc to the destructor and you're done. But often enough you'll want to add convenience wrappers, e.g. to return actual Lua strings or when returning multiple values.

Some C libraries only declare instance pointers as an opaque void * type. In this case you can use a fake type for all declarations, e.g. a pointer to a named (incomplete) struct will do: typedef struct foo_type *foo_handle. The C side doesn't know what you declare with the LuaJIT FFI, but as long as the underlying types are compatible, everything still works.

Translating C Idioms

Here's a list of common C idioms and their translation to the LuaJIT FFI:

Idiom C code Lua code
Pointer dereference
int *p;
x = *p;
*p = y;
x = p[0]
p[0] = y
Pointer indexing
int i, *p;
x = p[i];
p[i+1] = y;
x = p[i]
p[i+1] = y
Array indexing
int i, a[];
x = a[i];
a[i+1] = y;
x = a[i]
a[i+1] = y
struct/union dereference
struct foo s;
x = s.field;
s.field = y;
x = s.field
s.field = y
struct/union pointer deref.
struct foo *sp;
x = sp->field;
sp->field = y;
x = s.field
s.field = y
Pointer arithmetic
int i, *p;
x = p + i;
y = p - i;
x = p + i
y = p - i
Pointer difference
int *p1, *p2;
x = p1 - p2;x = p1 - p2
Array element pointer
int i, a[];
x = &a[i];x = a+i
Cast pointer to address
int *p;
x = (intptr_t)p;x = tonumber(
 ffi.cast("intptr_t",
          p))
Functions with outargs
void foo(int *inoutlen);
int len = x;
foo(&len);
y = len;
local len =
  ffi.new("int[1]", x)
foo(len)
y = len[0]
Vararg conversions
int printf(char *fmt, ...);
printf("%g", 1.0);
printf("%d", 1);
 
printf("%g", 1);
printf("%d",
  ffi.new("int", 1))

To Cache or Not to Cache

It's a common Lua idiom to cache library functions in local variables or upvalues, e.g.:

local byte, char = string.byte, string.char
local function foo(x)
  return char(byte(x)+1)
end

This replaces several hash-table lookups with a (faster) direct use of a local or an upvalue. This is less important with LuaJIT, since the JIT compiler optimizes hash-table lookups a lot and is even able to hoist most of them out of the inner loops. It can't eliminate all of them, though, and it saves some typing for often-used functions. So there's still a place for this, even with LuaJIT.

The situation is a bit different with C function calls via the FFI library. The JIT compiler has special logic to eliminate all of the lookup overhead for functions resolved from a C library namespace! Thus it's not helpful and actually counter-productive to cache individual C functions like this:

local funca, funcb = ffi.C.funcb, ffi.C.funcb -- Not helpful!
local function foo(x, n)
  for i=1,n do funcb(funca(x, i), 1) end
end

This turns them into indirect calls and generates bigger and slower machine code. Instead you'll want to cache the namespace itself and rely on the JIT compiler to eliminate the lookups:

local C = ffi.C          -- Instead use this!
local function foo(x, n)
  for i=1,n do C.funcb(C.funca(x, i), 1) end
end

This generates both shorter and faster code. So don't cache C functions, but do cache namespaces! Most often the namespace is already in a local variable at an outer scope, e.g. from local lib = ffi.load(...). Note that copying it to a local variable in the function scope is unnecessary.




//struct 原始结构体
namespace raw

class struct {
    ctor( cls ) {
        var tcls = type(cls);
        var struct = class {
            ctor( ... ){
                if( tcls = "class"){
                    this[["_object"]] = cls( ... )
                }
                else {
                    this[["_object"]] = ..table.mixin({},cls,...);
                }
               
                this[["_struct_cdata"]] = ..raw.malloc(this[["_object"]])
            };
            @_meta;
        }
        struct._meta =  _meta;
        return struct;
    }; 
}
 
namespace struct{
    var convert = ..raw.convert;
    var mixin = ..raw.mixin;
   
    _meta = {
        _topointer = function(){ 
            return owner[["_struct_cdata"]];
        }
        _get = function(k){ 
            var obj = owner[["_object"]];
            convert(owner[["_struct_cdata"]],obj);
            return obj[k]
        }
        _set = function( k, v ){
            var obj = owner[["_object"]];
            var handle = owner[["_struct_cdata"]];
            mixin(handle,obj,{[k] = v } )
        }
    }
}

/**intellisense()
raw.struct = 将结构体转换为原始结构体\n原始结构体创建的对象会分配一块固定的内存\n可用于API参数pointer类型实参并自动转换为C指针
raw.struct(__/*结构体*/) = 分配内存,创建并返回静态类,\n参数可以是定义了静态类型的类(结构体)\n也可以是声明了静态类型的table原型表,\n如果参数是原型表,则会创建一个默认的构造函数\n默认构造函数将接收参数混入原型创建新对象
end intellisense**/



最近学习api操作,拿wlan的api练手

例如下面这个API,这是MSDN的官方C++示例
http://msdn.microsoft.com/en-us/library/windows/desktop/ms706716(v=vs.85).aspx

  源代码 [ AAuto ]

001DWORD WINAPI WlanEnumInterfaces(
002  _In_        HANDLE hClientHandle,
003  _Reserved_  PVOID pReserved,
004  _Out_       PWLAN_INTERFACE_INFO_LIST *ppInterfaceList
005);
里面的一个参数

PWLAN_INTERFACE_INFO_LIST *ppInterfaceList

因为不懂C++,所以我查阅了一下资料,

好像ppInterfaceList参数是一个PWLAN_INTERFACE_INFO_LIST型的结构体,
加上*号代表传入的是指向PWLAN_INTERFACE_INFO_LIST型结构体的一个指针,
然后我开始写AAuto代码,

先查阅了结构体类型的相关资料,在api数据类型文档中发现一句
http://bbs./doc/reference/libraries/kernel/raw/datatype.html
注意在API参数中,struct被转换为指针按引用传递,如果是按值传递的结构体,需要展开所有成员为普通参数.



所以我这样定义了这个API函数,对上面这句话的理解就是 只需要把结构体声明一个结构体对象,然后传进去就行了,最后发现理解的不对



结构体:
import win.guid;
class WLAN_INTERFACE_INFO_LIST{
    int dwNumberOfItems;
    int dwIndex;
    struct  InterfaceInfo[] = { {
        struct InterfaceGuid = ..win.guid();
        byte strInterfaceDescription[256];
        int isState;
    } };
}

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,struct ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var dwResult = WlanEnumInterfaces(hClient,null,pIfList)

io.print(dwResult,pIfList[
'InterfaceInfo'][1]['InterfaceGuid'])


结果发现他只能打印出一个空的,也就是默认的那个GUID 00000000-0000-0000-0000-000000000000




然后我又把API声明改成这个样子,

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,struct& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var dwResult,pIfList2 = WlanEnumInterfaces(hClient,null,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])


结果还是输出GUID 00000000-0000-0000-0000-000000000000




接着我又改成这种,API最后一个参数传入指针(我也不知道这算不算是指针)

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.malloc(pIfList)

var dwResult = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

结果还是输出GUID 00000000-0000-0000-0000-000000000000




于是我又改,改成传入引用指针

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.malloc(pIfList)

var dwResult = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

结果还是输出GUID 00000000-0000-0000-0000-000000000000




然后我查手册,发现传入参数加&,会有一个返回值,于是我又修改了,加了个返回值

WlanEnumInterfaces = ::Wlanapi.api("WlanEnumInterfaces","int(int hClientHandle,pointer pReserved,pointer& ppInterfaceList)")

pIfList = WLAN_INTERFACE_INFO_LIST();

var pPointer =  raw.malloc(pIfList)

var dwResult,pPointer2 = WlanEnumInterfaces(hClient,null,pPointer)

pIfList2 = raw.convert(pPointer2,pIfList)

io.print(dwResult,pIfList2[
'InterfaceInfo'][1]['InterfaceGuid'])

终于得到了正常的GUID 449b23c8-f18c-4d64-9bbd-221c2c9671fe





关键问题就是:

1.
结构体
struct
32位
table
struct

可以在API参数中使用空表 {} 表示C/C++中的null结构体指针

注意在API参数中,struct被转换为指针按引用传递,如果是按值传递的结构体,需要展开所有成员为普通参数.


手册里这么说"在API参数中,struct被转换为指针按引用传递"
我直接传入struct对象,为什么获取不到值呢?
后来必须用raw.malloc() 把结构体对象复制到内存中,然后传入指向这个结构体的指针 才能正常获取数据

2.
raw.malloc分配定长的内存,并返回托管指针.
参数如果是一个普通的结构体(struct table).raw.malloc创建相同大小的内存并复制结构体的内存数据.

托管指针一般是由指向AAuto分配的内存,内存不再使用时会自动释放,是一种内部指针、伪指针。
raw.malloc创建的托管指针并可在API函数中作为pointer指针类型参数使用,也可以可以使用索引操作符直接读写内存数据.这与AAuto中的字符串类似,指定索引返回指定位置的字节码,如果索引溢出(过大或过小)会返回0.

像我这个结构体,算是个普通机构体吧?
class WLAN_INTERFACE_INFO_LIST{
    int dwNumberOfItems;
    int dwIndex;
    struct  InterfaceInfo[] = { {
        struct InterfaceGuid = ..win.guid();
        byte strInterfaceDescription[256];
        int isState;
    } };
}

根据上面手册的说明,
"参数如果是一个普通的结构体(struct table).raw.malloc创建相同大小的内存并复制结构体的内存数据."

那我这样用raw.malloc()函数复制的结构体,是一个空的结构体,里面没有任何数据,申请的内存也肯定不大,
如果这个api函数往里面添加数据过多,会不会导致内存错误之类的问题?

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多