Say I have this metatable for a custom struct vector2_t which is inside a module mymod like this:
local mymod = {}
local ffi = require("ffi")
local C = ffi.C
ffi.cdef[[
typedef struct
{
double x;
double y;
} vector2_t;
]]
local vector2_t
vector2_t = ffi.metatype("vector2_t", {
__eq = function(lhs, rhs)
if lhs.x == rhs.x and lhs.y == rhs.y
then return true else return false end
end
-- Member functions follow...
})
vcmp.vector2 = vector2_t
-- I use this method because the script is integrated in C/C++ as a string and not
-- as a file and I need a constant name that isn't decided by the file name
package.preload["mymod"] = function(mod) return mymod end
And in another script I have this callback/event listener function which must receive a vector2_t as it's argument:
local mymod = require "mymod"
local some_pos = mymod.vector2(32, 29.7)
-- That pos argument must be an instance of mymod.vector2_t
function position_update(pos)
print("New Pos: " .. pos.x .. ", " .. pos.y .. "\n")
some_pos.x = pos.x
some_pos.y = pos.y
end
Now, I must call that callback/event listener function from C/C++ and pass an instance of that vector2_t (along with it's associated metatable) as the parameter to that function.
typedef struct
{
double x;
double y;
} vector2_t;
void call_position_update(lua_State* L, double x, double y)
{
// Retrieve the function
lua_getglobal(L, "position_update");
// Validate it
if (!lua_isfunction(L, lua_gettop(L)))
{
lua_pop(L, 1);
return;
}
// Create an instance of vector2_t
vector2_t *pos = (vector2_t *)lua_newuserdata(L, sizeof(vector2_t));
// Assign the values to the new instance
pos->x = x;
pos->y = y;
// How do I get the meta table vector2_t on to the stack?
// Reach to the module somehow...
// Get the meta table from the module
luaL_getmetatable(L, "vector2_t");
// Assign the meta table to the vector2_t instance already on the stack
lua_setmetatable(L, -2);
// I'm assuming that the vector2_t instance is already on the stack so there's nothing else to push
// Call the function with 1 argument which should be that vector2_t onto the stack
if (!lua_pcall(L, 1, 0, 0))
{
printf("Error calling function 'position_update': %s\n", lua_tostring(_Lua, -1));
}
}
I'm a bit lost and I don't know how to pas an instance of that vector2_t as the function parameter. I'm sorry for posting so much code bu I wanted to be sure that I explained correctly.
cdata and userdata are completely different things. The only interaction they have is that you can get an FFI void* pointer to userdata. Notably, there is no C API for cdata objects. Mixing them is sure to cause you a lot of headaches.
Pick either the Lua C API or the FFI, and stick to it as much as possible.
To directly answer your question:
how to pas [sic] an instance of that vector2_t as the function parameter
To a Lua C API function, it gets passed on the stack, just like other values. Note that it will be a cdata typed object, not a userdata object. Notably, you can't get a pointer to it.
How do I get the meta table vector2_t on to the stack?
You can't, since you don't make the metatable accessible to outside scripts in your first script. luaL_getmetatable only works with metatables created with luaL_newmetatable
Related
¿Is posible reuse lua_newuserdata() returned pointer?
The idea is not allocate a new userdatum every time for the same object and allow equality check (example) done by lua.
obj1 = c_api__foobar();
obj2 = c_api__foobar();
if obj1 == obj2 then
print("equal")
else
print("they are supposed to be equal")
end
well look is not possible, attemping to reuse the pointer will throw something like calling 'c_api__foobar' on bad self (FOOBAR expected, found FOOBAR) error.
static int expose_foobar(lua_State* L) {
void* existing_userdata = /* previous value returned by lua_newuserdata() */;
LUA.luaL_getmetatable(L, "FOOBAR")
LUA.lua_setmetatable(L, -2);
return 1;
}
tested in lua 5.4
I've initialized a votes array and two functions to store the votes in our array as :
uint[2] votes = [0,0];
function vote_a() public{
votes[0] += 1;
}
function vote_b() public{
votes[1] += 1;
}
Now, I've created a "results" function which should return a string "tie", "a wins" or "b wins" on the basis of number of votes while also reassigning the number of votes to 0
function results() public returns(string memory){
uint a = votes[0];
uint b = votes[1];
votes[0]=0;
votes[1]=0;
if (a==b)
return "tie";
else if (a>b)
return "a wins";
else
return "b wins";
}
but it does not show the returned result in remix ide like a view function. And I cannot modify the state of the function to view as it'd throw an error for changing value of votes array elements. Is there any way to achieve both conditions.
This happen when you'alterating state variables defined in view functions.
Thus, view functions can only reads data from your smart contract.
To solve your issue, I tried to split the content about results() function in two functions. The first function that I called setResults() is a similar to setter functions (in other programming languages), so allows only for contract owner to handle the array values.
Second function, allows you to view the result about the comparison between a and b elements.
In the following lines, I put your smart contract adjusted where I inserted some notes:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test {
uint[2] votes = [0,0];
address owner;
// NOTE: I set a 'owner' variable with the address value who have deployed for the first time the smart contract
constructor() {
owner = msg.sender;
}
// NOTE: Modifier that allow only for smart contract owner the access to specific function
modifier onlyOwner() {
require(msg.sender == owner, "You're not the owner!");
_;
}
function vote_a() public{
votes[0] += 1;
}
function vote_b() public{
votes[1] += 1;
}
function results() public view returns(string memory){
uint a = votes[0];
uint b = votes[1];
if (a==b)
return "tie";
else if (a>b)
return "a wins";
else
return "b wins";
}
// NOTE: I created a new function that allows you to handle the data inside array
function setResults() public onlyOwner {
votes[0] = 0;
votes[1] = 0;
}
}
IMPORTANT: Before call results() function, remember you to call setResults() for change the array values.
I am writing in C a userdata type for use in Lua. It has some array-type properties and various methods aswell. Right now if u is of this type, I use u:set(k,v) resp. u:get(k) to access data and e.g. u:sort() as method. For this I set __index to a table containing these methods. Now if I want to access the data using u[k] = v or u[k], I need to set __newindex and __index to set resp get. But then the other methods are no longer accessible...
What's the best way to deal with this in C? I am guessing I need to write a function in C to register as __index and somehow deal with it there. Maybe check if key belongs to a Lua table of methods and if so call it.
Any help/hints would be appreciated. I did not find examples like this, although it seems a very natural thing to do (to me.)
edit: Added my C version of the solution in Lua posted in the answer below. This is more or less a direct translation, so all credit goes to #gilles-gregoire .
The following C function is registered as __index metamethod.
static int permL_index(lua_State *L) {
struct perm **pp = luaL_checkudata(L, 1, PERM_MT);
int i;
luaL_getmetatable(L, PERM_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if ( lua_isnil(L, -1) ) {
/* found no method, so get value from userdata. */
i = luaL_checkint(L, 2);
luaL_argcheck(L, 1 <= i && i <= (*pp)->n, 2, "index out of range");
lua_pushinteger(L, (*pp)->v[i-1]);
};
return 1;
};
This is the code that does that,
int luaopen_perm(lua_State *L) {
luaL_newmetatable(L, PERM_MT);
luaL_setfuncs(L, permL_methods, 0);
luaL_setfuncs(L, permL_functions, 0);
lua_pop(L, 1);
luaL_newlib(L, permL_functions);
return 1;
};
where permL_methods is
static const struct luaL_Reg permL_methods[] = {
{ "__index", permL_index },
{ "__eq", permL_equal },
{ "__tostring", permL_tostring },
{ "__gc", permL_destroy },
[...]
{ NULL, NULL }
};
and permL_functions is
static const struct luaL_Reg permL_functions[] = {
{ "inverse", permL_new_inverse },
{ "product", permL_new_product },
{ "composition", permL_new_composition },
[...]
{ NULL, NULL }
};
This looks like a problem which can be solved with nested metatables. You need one metatable for the methods (like your sort() method), and a second one for index operations. That second metatable is actually the metatable of the methods metatable.
Let me write this as lua code. You need 3 tables:
-- the userdata object. I'm using a table here,
-- but it will work the same with a C userdata
u = {}
-- the "methods" metatable:
mt = {sort = function() print('sorting...') end}
-- the "operators" metatable:
op_mt = {__index = function() print('get') end}
Now, the tricky part is here: lua will first lookup u when you will call a method.
If it does not find it, it will lookup in the table pointed by the __index field of u's metatable... And Lua will repeat the process for that table!
-- first level metatable
mt.__index = mt
setmetatable(u, mt)
-- second level metatable
setmetatable(mt, op_mt)
You can now use your u like this:
> u:sort()
sorting...
> = u[1]
get
nil
EDIT: a better solution by using a function for the __index metamethod
Using a function for the __index metamethod is probably the right way to this:
u = {}
mt = {sort = function() print('sorting...') end}
setmetatable(u, mt)
mt.__index = function(t, key)
-- use rawget to avoid recursion
local mt_val = rawget(mt, key)
if mt_val ~=nil then
return mt_val
else
print('this is a get on object', t)
end
end
Usage:
> print(u)
table: 0x7fb1eb601c30
> u:sort()
sorting...
> = u[1]
this is a get on object table: 0x7fb1eb601c30
nil
>
I tried to call a function from external DLL in Python.
The function prototype is:
void Myfunction(int32_t *ArraySize, uint64_t XmemData[])
This function creates a table of uint64 with "ArraySize" elements. This dll is generated by labview.
Here is the Python code to call this function:
import ctypes
# Load the library
dllhandle = ctypes.CDLL("SharedLib.dll")
#specify the parameter and return types
dllhandle.Myfunction.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_uint64)]
# Next, set the return types...
dllhandle.Myfunction.restype = None
#convert our Python data into C data
Array_Size = ctypes.c_int(10)
Array = (ctypes.c_uint64 * Array_Size.value)()
# Call function
dllhandle.Myfunction(Array_Size,Array)
for index, value in enumerate(Array):
print Array[index]
When executing this I got the error code:
dllhandle.ReadXmemBlock(Array_Size,Array)
WindowsError: exception: access violation reading 0x0000000A
I guess that I don't pass correctly the parameters to the function, but I can't figure it out.
I tried to sort simple data from the labview dll like a uint64, and that works fine; but as soon as I tried to pass arrays of uint64 I'm stuck.
Any help will be appreciated.
It looks like it's trying to access the memory address 0x0000000A (which is 10). This is because you're passing an int instead of a pointer to an int (although that's still an int), and you're making that int = 10.
I'd start with:
import ctypes
# Load the library
dllhandle = ctypes.CDLL("SharedLib.dll")
#specify the parameter and return types
dllhandle.Myfunction.argtypes = [POINTER(ctypes.c_int), # make this a pointer
ctypes.c_uint64 * 10]
# Next, set the return types...
dllhandle.Myfunction.restype = None
#convert our Python data into C data
Array_Size = ctypes.c_int(10)
Array = (ctypes.c_uint64 * Array_Size.value)()
# Call function
dllhandle.Myfunction(byref(Array_Size), Array) # pass pointer using byref
for index, value in enumerate(Array):
print Array[index]
I'm writing a "C" userdata array structure.
As setter and getter i want normal array access (u[0] = 1 u[0]) like it's discussed here:
[c array share][1]Share Array between lua and C.
For that i need to set __index and __newindex to the set and get functions in c.
Additional i want object-oriented access, too, "like u:mymethod()". My trouble is, that i need to set now __index to the metatable itself.
Is there a way, to achieve both?
Just one of many possible ways to achieve this:
local userdata = { _array = { "A", "B", "C" } }
local mt = { }
local methods = { }
function mt.__index(userdata, k)
if methods[k] then
return methods[k]
else
return rawget(userdata, "_array")[k]
end
end
function mt.__newindex(userdata, k, v)
if methods[k] then
error "can't assign to method!"
else
rawget(userdata, "_array")[k] = v
end
end
function methods.count(userdata)
return #rawget(userdata, "_array")
end
setmetatable(userdata, mt)
userdata[3] = "Z"
print(userdata[1])
print(userdata[2])
print(userdata[3])
print(userdata:count())
userdata.count = 0
edit: As lhf pointed in his comment, it is not dangerous to use metatable as it's __index table at all, because c-methods should always check on what self they operate.