Lua C api : How to load lua files defined as modules? - c

I have the following lua script :
module("modoo",package.seeall)
foo=1
bar={12,34}
Which works fine using the cli, such as :
> dofile "mod_modoo.lua"
> =modoo.foo
1
> =modoo
table: 0x86ce058
As far as I understood, it works like a table, but whenever I try loading it as a table, a nil value is pushed onto the stack. Every other table works normally.
I thought using lua_getglobal wouldn't work with modules, but I couldn't find the proper way to load it either ; how should I do it ?

Load Lua modules with require like lua.c does. See http://www.lua.org/source/5.1/lua.c.html#dolibrary

Instead of using module and dofile, it is better practice in Lua 5.1 to simply return a table representing your module when the script is run. All functions and variables should be declared as local within the module script so that the returned table provides the only access point and no globals from other modules are trampled. The module should be imported using require as follows.
mod_modoo.lua:
return { foo = 1, bar = { 12, 34 } }
modoo_test.lua:
> local modoo = require "mod_modoo"
However, it is often nice to bring the module table (i.e. modoo) in as a global without explicitly assigning it. To do so, assign the module table a global reference in addition to returning it from the script. In that case, the module can be used as follows:
mod_modoo.lua:
modoo = { foo = 1, bar = { 12, 34 } }
return modoo
modoo_test.lua:
> require "mod_modoo"
> print(modoo.foo)
1
See the Lua Module Function Critiqued article for more information on the matter.

Related

Accessing data from a structure returned by C function in Python using ctypes

I know the subject has already been treated, but I've failed to find anything that works for me, so I guess my problem is slightly different from the others.
What I do, basically, is that I use a C function wrapped into a python code using ctypes.
My goal is to compute some quantities in the C function, store them into a single structure, and return the structure in Python where I could read all the different data in the structure.
So :
To define my structure in python, I use :
class BITE(ctypes.Structure):
_fields_ = [
("lum", ctypes.c_int),
("suce", ctypes.c_int)
]
I compile like that :
sub.call(["gcc","-shared","-Wl,-install_name,ex.so","-o","ex.so","-fPIC","ex.c"])
lum = ctypes.CDLL('ex.so')
lum = lum.lum
I declare the arguments and result types like this :
lum.restype = ctypes.POINTER(BITE)
lum.argtypes = [ctypes.POINTER(ctypes.c_float),ctypes.c_int,ctypes.POINTER(ctypes.c_float),ctypes.c_int,ctypes.POINTER(ctypes.c_float),ctypes.POINTER(ctypes.c_float),ctypes.c_int,ctypes.POINTER(ctypes.c_float),ctypes.c_int,ctypes.POINTER(ctypes.c_float),ctypes.POINTER(ctypes.c_float),ctypes.POINTER(ctypes.c_float),ctypes.POINTER(ctypes.c_float),ctypes.c_float,ctypes.c_float,ctypes.c_float,ctypes.c_float,ctypes.c_float]
Then, I call the C function "lum" in the python code
out = lum(all the different variables needed)
The problem begins here. I have my structure, but impossible to read the data stored in each fields.
A partial solution I found is to do :
out = out[0]
But then, when doing
print(out.suce)
I have a segmentation fault 11. DOn't know why. I tried to understand how to used create_string_buffer, but I didn't really understand how it works and what it supposed to do. Moreover, I tried to using as a black box, and still, nothing works.
I also tried some other solutions mentionned in other threads such as using out.contents or something like that, but it has no .contents attributes. Nor out.suce.value, which is also something I saw somewhere during my desperate research.
Of course, I checked in the C code that the structure exists and that each field of the structure exists, and has the right data in it.
Thanks.
Suppose you have a C-structure like this :
typedef struct _servParams
{
unsigned short m_uServPort;
char m_pInetAddr[MAX_LENGTH_OF_IP_ADDRESS];
} ServerParams_t;
To use it within ctypes you should create a wrapper like this :
class ServerParams_t(Structure):
_fields_ = [ ("m_uServPort", c_ushort),
("m_pInetAddr", (c_char * 16)) ]
Later on inside your python you can use the following snippet :
servParams = ServerParams_t()
servParams.m_uServPort = c_ushort(int(port))
servParams.m_pInetAddr = ip.encode()
If you need to pass it by value, simply pass servParams variable to you api. If you need to pass a pointer use byref(servParams).

Unable to fetch "ReadWrite" Variable Value in Script Component of SSIS

In Script Component [Input0_ProcessInputRow], i am trying to fetch "ReadWrite" global variable value and it throws below error.
ERROR:
The collection of variables locked for read and write access is not available outside of PostExecute.
Below is my code
If Row.Column13 = "C" Then
Variables.mTotalCreditCount = Variables.mTotalCreditCount - 1
Variables.mTotalCreditAmount = Variables.mTotalCreditAmount - CDbl(Row.Column14)
ElseIf Row.Column13 = "D" Then
Variables.mTotalDebitCount = Variables.mTotalDebitCount - 1
Variables.mTotalDebitAmount = Variables.mTotalDebitAmount - CDbl(Row.Column14)
End If
I also tried to read the value in local variable and then assign to global variable in the PostExecute() as below. No Luck
If Row.Column13 = "C" Then
mTotalCrCnt = Variables.mTotalCreditCount - 1
mTotalCrAmt = Variables.mTotalCreditAmount - CDbl(Row.Column14)
ElseIf Row.Column13 = "D" Then
mTotalDbCnt = Variables.mTotalDebitCount
mTotalDbCnt = mTotalDbCnt - 1
mTotalDbAmt = Variables.mTotalDebitAmount
mTotalDbAmt = mTotalDbAmt - CDbl(Row.Column14)
End If
Public Overrides Sub PostExecute()
MyBase.PostExecute()
Variables.ProcessCount = intProcessCount
Variables.mTotalCreditCount = mTotalCrCnt
Variables.mTotalCreditAmount = mTotalCrAmt
Variables.mTotalDebitCount = mTotalDbCnt
Variables.mTotalDebitAmount = mTotalDbAmt
End Sub
Any assistance please?
Looking to your comment it looks that you have solved the issue, but i am posting this answer to give information about how working with variables in a SSIS script and how to solve a similar issue, so it can helps other users
SSIS Variables
Variables Stores values that can be used in all SSIS components and containers.
Integration Services supports two types of variables: user-defined variables and system variables. User-defined variables are defined by package developers, and system variables are defined by Integration Services. You can create as many user-defined variables as a package requires, but you cannot create additional system variables. More info in this MSDN article
Using Variables in Script components
Every script has a ReadOnlyVariables and ReadWriteVariables Lists that can be defined on the Script page.
ReadOnlyVariables
ReadOnlyVariables can be accessed from all script Sub's and they are Read-Only as they are named.
ReadWriteVariables
The collection of ReadWriteVariables is only available in the PostExecute method to maximize performance and minimize the risk of locking conflicts. Therefore you cannot directly increment the value of a package variable as you process each row of data. Increment the value of a local variable instead, and set the value of the package variable to the value of the local variable in the PostExecute method after all data has been processed. You can also use the VariableDispenser property to work around this limitation. However, writing directly to a package variable as each row is processed will negatively impact performance and increase the risk of locking conflicts. More in this MSDN article
Methods to Work with Variables
There are 3 Methods to work with variables:
Accessing them directly after having selected them as ReadOnlyVariables or ReadWriteVariables in the script page
Using a Variables Dispenser (LockForRead and LockForWrite methods)
IDTSVariables100 vars = null;
VariableDispenser.LockForRead("User::MyVariable");
VariableDispenser.GetVariables(out vars);
string TaskName = vars["User::MyVariable"].Value.ToString();
vars.Unlock();
Using SSIS Logging Task: to read variable and log them to Execution Log, Message Box or File
There are many articles Talking about this methods and you can refer to them to learn more
VariableDispenser.GetVariables Method (Variables)
VariableDispenser.LockForWrite Method (String)
3 Ways -SSIS Read Write Variables – Script Task C# / VB.net
Read and Write variables in a Script Component in SSIS (SQL Server Integration Services) using C#
Use SSIS Variables and Parameters in a Script Task

Lua C modules: confused about including members

I am trying to register a Lua C module that I need to use like this.
local harfbuzz = require 'harfbuzz'
-- initialize blob
local blob = harfbuzz.Blob.new(…)
print(blob:length())
My understanding is that I should create a new table and add a metatable to it with the methods, and then add that table as a member Blob to the top-level lib table.
Here is the relevant snippet of code in my C file. I am not very sure what to include in the register_blob function. I tried a few things and they did not work.
static const struct luaL_Reg blob_methods[] = {
{ "length", blob_length },
{"__gc", blob_destroy },
{ NULL, NULL },
};
static const struct luaL_Reg blob_functions[] = {
{ "new", blob_new },
{ NULL, NULL }
};
static const struct luaL_Reg lib_table [] = {
{"version", get_harfbuzz_version},
{NULL, NULL}
};
int register_blob(lua_State *L) {
// QUESTION: What should I include here
}
int luaopen_luaharfbuzz (lua_State *L) {
lua_newtable(L);
register_blob(L);
luaL_setfuncs(L, lib_table, 0);
return 1;
}
What register_blob does really depends on what new_blob needs to do. The two feed into one another.
Given your use case, new_blob needs to create new objects, and those new objects have metatable members equal to your blob_methods table. So, what new_blob needs to do is:
Create a table/userdata to be returned.
Assign that table/userdata a metatable built from the contents of blob_methods table.
Do whatever other initialization work is needed on the object.
Return the object.
So what your register_blob code needs to do is build the metatable that you intend to use in step 2, then store it somewhere that new_blob can easily access it. But also somewhere that noone else can. Or at least, noone outside of C code.
Lua being well designed, it has a place for storing exactly this kind of data. It's called the Lua registry table. The registry acts like a global table. But it can only be access from C; Lua code can't touch it (well, unless you use the debug library. Or pass the registry table to Lua).
The idiomatic way (as I understand it) that the registry is used is that every C module stakes out its own table index in the registry. So your luaopen_luaharfbuzz function would create a table and store that into a key in the registry. The key would probably some string, likely named after your module. And you put all your private stuff into that table.
So your register_blob function would:
Create the table that will be used as the metatable for blob objects.
Use luaL_setfuncs to set the blob_methods into the newly created table.
Get the harfbuzz table from the registry.
Put the newly created table into a known key in the harfbuzz table from the registry.
That way, new_blob knows exactly where to go to get the metatable to use.

Powershell script only works on one machine. Function return object handling

I have a script I use to manage some Exchange attributes. I recently added some code to handle setting proxy addresses. I use a function to build the list then return a collection object to set the list in a different function. this is the jist of that function:
function buildProxyAddresses([string]$user)
{
$addressCollection = New-Object -TypeName Microsoft.ActiveDirectory.Management.ADPropertyValueCollection
$addressCollection.Add("smtp:" + $user + $domain)
#etc
#etc....
Return #(,$addressCollection)
}#endFunc buildProxyAddresses
Took me a while but I figured out how to pass the object by sticking it in array when I return it, ugly but functional. works fine, I can access the object by calling a $returnvar.item(3) on the return variable. where the third element is the ADPropertyValueCollection
Now I take the same script to my Co-Workers computer and he runs it and he gets an error that tells him :
[System.Object[]] doesn't contain a method named 'item'.
I have no idea why it runs different on his machine
Try using:
$returnvar[3]
It's likely its failing on a machine that's running an older version of powershell. Looks like the .item(x) syntax works from version 3 upwards.
However, it's not a normal way to reference an array index, the standard is to use $array[idx]

Sandboxing in Lua 5.2

I am learning from "Programing in Lua" by Roberto Ierusalimschy, and I found that in the book, the example of Sandboxing uses the function setfenv() to change the environment of a given function, but in lua 5.2 this function is no longer available.
I tried to load some values from a file (a configuration file) into a field in a table, but, in lua 5.2 I can't use setfenv ( so I can load the values in the given environment). After reading some articles about lua 5.2 I found that each function may have (or not) an upvalue called _ENV which serves as the environment, so, I tried the following code:
function sandbox(sb_func, sb_env)
if not sb_func then return nil, "sandbox function not valid" end
sb_orig_env = _ENV
_ENV = sb_env -- yes, replaces the global _ENV
pcall_res, message = pcall( sb_func )
local modified_env = _ENV -- gets the environment that was used in the pcall( sb_func )
_ENV = sb_orig_env
return true, modified_env
end
function readFile(filename)
code = loadfile(filename)
res, table = sandbox(code, {})
if res then
--[[ Use table (modified_env) ]]--
else
print("Code not valid")
end
Replacing _ENV in the 'sandbox' function works well (can't access the regular fields), but, when the 'code' is executed it seems that it ignores that I replaced _ENV, it still can access regular fields (print, loadfile, dofile, etc).
Reading a little more, I found that lua 5.2 provides a function for this purpose, this function is loadin(env, chunk), which runs the given chunk in the given environment, but, when I try to add this function to my code, the function doesn't exist ( Is not present in the global _G field).
Some help will be appreciated.
When you assign to _ENV from within sandbox, you're not overwriting the global environment--you're replacing the _ENV upvalue of the currently running code. Adding calls to print(_ENV) may help you better understand the identities of the tables involved.
For example:
function print_env()
print(_ENV)
end
function sandbox()
print(_ENV) -- prints: "table: 0x100100610"
-- need to keep access to a few globals:
_ENV = { print = print, print_env = print_env, debug = debug, load = load }
print(_ENV) -- prints: "table: 0x100105140"
print_env() -- prints: "table: 0x100105140"
local code1 = load('print(_ENV)')
code1() -- prints: "table: 0x100100610"
debug.setupvalue(code1, 1, _ENV) -- set our modified env
code1() -- prints: "table: 0x100105140"
local code2 = load('print(_ENV)', nil, nil, _ENV) -- pass 'env' arg
code2() -- prints: "table: 0x100105140"
end
The loadin function was present in some pre-release versions of Lua 5.2 but was removed before the final release. Instead, the Lua 5.2 load and loadfile functions take an env argument. You can also modify the _ENV of another function using debug.setupvalue.

Resources