It always return a String which is (at least I guess) the table identifier someone can help in anyway?
Thats my function:
function listFiles(dir)
local ffi = require("ffi")
ffi.cdef[[char ** PHYSFS_enumerateFiles ( const char * dir );]]
local liblove = ffi.os == "Windows" and ffi.load("love") or ffi.C
local tb={}
tb=liblove.PHYSFS_enumerateFiles(dir)
return tb
end
It should return me a String with filecontents of the "Dir" I pass to it, but it doesnt. Can't figure out why.
You should read the reference properly. The enumeration function returns a pointer to string pointers, after the last string follows a NULL pointer. Conversion from char* to Lua string can be done with ffi.string.
Related
I need some help converting a luajit pointer to a string and back.
First I define the ctype:
ffi.cdef[[
typedef struct {
unsigned char Bytes[16];
} EncryptionKeys[100000000];
void* malloc(size_t);
void free(void*);
]]
Then use malloc to allocate some memory and then create the 'EncryptionKeys' variable.
local EncryptionKeyMemoryAddress = ffi.C.malloc(ffi.sizeof("EncryptionKeys"))
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
I first convert the variable into a lua string using:
ffi.string(EncryptionKeyMemoryAddress)
But I can't figure out how to convert it back!
Can someone please help me?
FYI: I am passing the 'EncryptionKeyMemoryAddress' variable to one of the function parameters for a lua lane (https://lualanes.github.io/lanes/).
Edit:
Here is the section of code that I am working on:
This is for the client managers module of my server that manages a list of lua states that all have access to any clients connected to the server. They all use a shared section of memory that I want them to have access to using a pointer.
local ClientFFIString = [[
typedef struct {
unsigned char Bytes[16];
} EncryptionKeys[100000000];
void* malloc(size_t);
void free(void*);
]]
ffi.cdef(Matchpools.FFIString)
local EncryptionKeyMemoryAddress = ffi.C.malloc(ffi.sizeof("EncryptionKeys"))
--------------------------------------------
function ClientManagers.CreateNewClientManager()
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
EncryptionKeys[0].Bytes[0] = 24
print("___a", EncryptionKeys[0].Bytes[0])
local NewIndex = #ClientManagers.List+1
ClientManagers.List[NewIndex] = ClientManagerFunc(
ClientFFIString,
ffi.string(EncryptionKeysMemoryAddress)
)
end
--------------------------------------------
local ClientManagerFunc = Lanes.gen("*", function(ClientFFIString, EncryptionKeysMemoryAddress)
ffi = require("ffi")
ffi.cdef(ClientFFIString)
local EncryptionKeys = ffi.cast("EncryptionKeys(&)", EncryptionKeyMemoryAddress)
print("___a", EncryptionKeys[0].Bytes[0])
-- I want this to be 24 just like it is in the function that created this lua state
local ClientManagerRunning = true
while ClientManagerRunning do
--local dt = GetDt()
--UpdateClientData(dt)
--UpdateMatchmaking(dt)
end
end)
You can convert Lua string to your structure pointer (and later use it as an array):
ffi.cdef"typedef struct {unsigned char Bytes[16];} Key;"
ptr=ffi.cast("Key *", your_string)
print("First Byte of the First Key in your Array:", ptr[0].Bytes[0])
UPDATE:
Let's test how it works for an array containing three keys:
local ffi = require'ffi'
ffi.cdef"typedef struct {unsigned char Bytes[16];} Key;"
local your_string = string.char(11):rep(16)..string.char(22):rep(16)..string.char(33):rep(16)
local ptr=ffi.cast("Key *", your_string)
print("First Byte of the First Key in your Array:", ptr[0].Bytes[0])
print("First Byte of the Second Key in your Array:", ptr[1].Bytes[0])
print("First Byte of the Third Key in your Array:", ptr[2].Bytes[0])
It prints 11, 22, 33
UPDATE 2:
Pass the address of buffer instead of the content of buffer
In the main thread
-- allocate the buffer
local Keys = ffi.cast("EncryptionKeys&", ffi.C.malloc(ffi.sizeof("EncryptionKeys")))
-- write to the buffer
Keys[2].Bytes[5] = 42
-- create string containing 64-bit address
local string_to_send = tostring(ffi.cast("uint64_t", Keys))
-- Send string_to_send to a lane
Inside the lane
-- receive the string
local received_string = .....
-- restore the buffer pointer
local Keys = ffi.cast("EncryptionKeys&", loadstring("return "..received_string)())
-- read the data from the buffer
print(Keys[2].Bytes[5])
I would like to have a C function return a string table array (e.g. {"a", "b", "c"}) to a Lua script via LuaJIT.
Which is the best way to do it?
I thought of returning a single concatenated string with some separator (e.g. "a|b|c") then splitting it in Lua, but I was wondering if there is a better way.
EDIT: I'm using LuaJIT FFI to call C functions.
I think the easiest way to accomplish this would be to have the C code return a struct containing an array of strings and a length to Lua and write a little Lua to reify it to your desired data structure.
In C:
typedef struct {
char *strings[];
size_t len;
} string_array;
string_array my_func(...) {
/* do what you are going to do here */
size_t nstrs = n; /* however many strings you are returning */
char** str_array = malloc(sizeof(char*)*nstrs);
/* put all your strings into the array here */
return {str_array, nstrs};
}
In Lua:
-- load my_func and string_array declarations
local str_array_C = C.ffi.my_func(...)
local str_array_lua = {}
for i = 0, str_array_C.len-1 do
str_array_lua[i+1] = ffi.string(str_array_C.strings[i])
end
-- str_array_lua now holds your list of strings
I tried to call function tan of math.h this way (directly copy the declaration) and it works:
local ffi = require("ffi")
ffi.cdef[[
double tan(double x);
]]
print(ffi.C.tan(45))
But when I tried to call the function localtime of time.h the same way:
local ffi = require("ffi")
ffi.cdef[[
struct tm *localtime(const time_t *tp);
]]
print(ffi.C.localtime(1234544))
And get error:
lua: C:\Users\xiang\Desktop\bm.lua:4: declaration specifier expected near 'time_t'
stack traceback:
[C]: in function 'cdef'
C:\Users\xiang\Desktop\bm.lua:4: in main chunk
[C]: at 0x00401f00
[Finished in 0.1s with exit code 1]
I've checked the official manual this and this but still confused.
Every function you would like to call from FFI, it needs to be defined before. If not LuaJIT does not how to parse a FFI function call, how to do data-type conversion from Lua to C (and viceversa), etc.
Keeping that in my mind, to make your code work you would need to define time_t and struct tm. time_t is generally defined as a signed integer. You can find the definition of struct tm in localtime docs (man localtime).
ffi.cdef[[
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
struct tm *localtime(const int32_t *tp);
]]
In addition, function localtime expects a pointer value, not a constant integer. So it would be necessary to pass a c-data pointer storing an integer to localtime. There's a sort of LuaJIT idiom for that.
local time = ffi.new("int32_t[1]")
time[0] = 1234544
local tm = C.localtime(time)
Since arrays and pointers in C, although not the exactly same, are interchangeable in most cases.
Lastly, you cannot print a struct tm directly. Should store it into a variable and print out the fields you're interested.
print(tm.tm_sec)
You cannot use time_t as it isn't native C type. Replace it with proper native type or use a corresponding struct typedef. Then it should work.
Given a function like exec, how do I call it from lua ffi given an unknown number of arguments.
The function prototype being:
int execv(const char *path, char *const argv[]);
i.e.
function myexecv(...)
local arg = { ... }
local carg = ffi.new("char *const[?]", #arg)
for i = 1, #arg do
carg[i-1] = arg[i]
end
return ffi.C.execv(carg[0], carg)
end
Which doesn't work.
cannot convert 'string' to 'char *const'
I thought there might be a short cut way of initializing or creating something I can pass into argv. How do I do that?
I think I have figured it out and it seems to work.
I looked at another library and found a different way of calling ffi.new and expanded that slightly. Also because of the type you couldn't convert a string directly so I first create it as a normal const char*. And pass the length + 1 (to null terminate it). Then pass arg as the initializer.
Then recast it to the correct type before calling the real execv.
function myexecv(...)
local arg = {...}
arg = ffi.new("const char*[?]", #arg+1, arg)
arg = ffi.cast("char *const*", arg)
return ffi.C.execv(arg[0], arg)
end
Well, the above did work at some point. But stopped working. I kept getting "Bad Address" errors. It would seem that the copy operation for ffi.new wasn't working as expected. So I've changed the code to the following and this does work reliably.
function myexecv(...)
local arg = {...}
local argv = ffi.new("const char*[?]", #arg+1, arg)
argv[#arg] = nil
return ffi.C.execv(argv[0], argv)
end
Without recast:
local ffi = require"ffi"
ffi.cdef"int execv(const char*path, const char*const argv[]);"
local function myexecv(...)
local arg = {...}
arg = ffi.new("const char*[?]", #arg+1, arg)
return ffi.C.execv(arg[0], arg)
end
myexecv("/bin/ls", "-l")
I've been trying to use libtooling to rename classes in source, and have hit a snag wrt function returns: there doesn't seem to be an API to get the source extent of just the return type.
I could hack it by assuming the return type is before the function id, but this doesn't handle trailing return types in C++11.
Does anyone have a better suggestion?
Thanks!
// simplified example replacing only value type returns
virtual void run(const ast_matchers::MatchFinder::MatchResult& Result) {
SourceManager& src = *result_.SourceManager;
const FunctionDecl* const function =
result_.Nodes.getDeclAs<FunctionDecl>("function");
CharSourceRange range = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(function->getLocStart(),
function->getLocation.getLocWithOffset(-1)),
src, LangOptions());
_replace->insert(Replacement(src, range, "newClass));
}