为什么要自己实现呢,因为脚本这东西总要加密,lua似乎没有直接从内存读取脚本这东西,似乎dostring可以,不过因为听说效率较低所以pass,而且dostring也不能读取编译后的文件。
其实纯C的函数指针和C++等高级语言的抽象接口差不多,不过灵活度还是要高很多。现在发现面向对象有些东西的确是太死板了,以前觉得模板这东西很强大,现在发现这个不过是来弥补先天的不足,那还不如C的函数指针来得直接方面和明了。
不过习惯了面向对象,要转到函数式编程还有点绕不开,去看了关于lua自己的lua_Reader的实现,分别是dostring和dofile的实现,大概只是这个东西只是解析相应的资源,最终给lua_load用来加载block。如果只是简单的字符串就简单了,直接传进去就好了,dostring就是这么干的,而dofile就复杂很多,仔细分析,最需要的区别还是对于有无BOM和编译后的lua文件的解析,对于一般的需求,那个判断BOM我就不弄了,但是还是要判断是否编译了。
最初自己卡在一个地方,后来发现原来是lua5.1编译好的文件lua5.2根本不认,好吧,手头没有5.2的编译器,只好拿源码去编个,然后就搞定了。总结来说,最大的区别就是文件头是不是LUA_SIGNATURE。lua_load会先读取第一个字符,发现是编译文件就用编译文件策略,然后进行读后面的字符。
另一个其实不太明白的地方是关于buffer的大小,看起来dostring似乎并没有考虑这个问题,可能也并不需要处理这个问题,但是dofile里面却是用了LUAL_BUFFERSIZE(512)这个值,每次只读512个字节,虽然不知道为什么要这样不过我还是模仿了他。
贴一下代码,这里的从文件读取其实可以换成从内存,反正只是buffer的不同而已。而且我也自己去判断是否是编译文件了。
struct LoadFile { bool hasHead; int n; char* buffer; char* bufferToRead; }; static const char *getFile(lua_State *L, void *ud, size_t *size) { LoadFile *lf = (LoadFile *)ud; (void)L; /* not used */ if(lf->hasHead) { *size = 1; lf->hasHead = false; return LUA_SIGNATURE; } if(lf->n==0)return NULL; if (lf->n > LUAL_BUFFERSIZE) { *size = LUAL_BUFFERSIZE; lf->bufferToRead = lf->buffer; lf->buffer += LUAL_BUFFERSIZE; lf->n -= LUAL_BUFFERSIZE; } else { *size = lf->n; lf->bufferToRead = lf->buffer; lf->n = 0; } return lf->bufferToRead; } int LoadLuaFile(lua_State* L, const char* fileName, bool isLuac = false) { int ret = 0; lua_pushfstring(L, "@%s", fileName); HANDLE hFile = NULL; hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); DWORD size = GetFileSize(hFile, NULL); BYTE buffer[40960]; DWORD flag; ReadFile(hFile,buffer,size,&flag,0); CloseHandle(hFile); LoadFile file; file.n = 0; if (isLuac) { file.hasHead = true; file.n = size-1; file.buffer = (char*)(buffer+1); } else { file.hasHead = false; file.n = size; file.buffer = (char*)(buffer); } ret = lua_load(L, getFile, &file, lua_tostring(L, -1), NULL); return ret; }