How to access multidimensional table from lua in C? - c

Hello I am really stumped with this seemingly simple task.
I can access the properties of a table passed to a function in C, but cannot access the members of any subtable i create in it.
Basically I want to simply be able to extract the strings from the properties table so i can create say a "wheel" according to the users expectations.
Here is what I have so far (tried so much my brain is fried)
Lua Side:
--Function
createSomething( "wheel", { canInflate = true, properties = { "large", "full" } } )
C Side:
//I can retrieve any value easily within that table, but cannot seem to extract the table
//Within it named "properties", i can access the table, but cannot extract the strings inside
if( lua_istable(L, 2) ) {
lua_getfield(L, 2, "canInflate"); // Let's extract the value for the key 'someKey'. Pushes the value on the top of the stack
static int canInflate = lua_toboolean(L, -1); // get the value of bool now at the top of stack (index: -1)
//printf("can inflate is %d\n", canInflate);
//lua_pop(L, 1); // pop the value now that we are done with it
}
//try to get the properties table
if ( lua_istable(L, 2) ) {
lua_getfield(L, 2, "properties");
const char *str = lua_tostring(L, -1);
printf( "properties 1 = %s\n", str); // NULL
lua_pop(L, 2);
}
Any help on this would be greatly appreciated

The problem you're having is with how you specify tables in Lua: the following 3 statements have exactly the same result:
t = { 'full','large'}
t = { [1] = 'full', [2] = 'large'}
t={};t[1]='full';t[2]='large'
What you want is to use the strings as keys instead of values (as is done in your code and the above samples):
t={full=true,large=true}
-- or
t={}; t.full=true; t.large=true
If you use the strings as keys your C code should work.

Related

Postgres C extension aggregate: How to detect first time aggregate function is called

I'm writing a C extension aggregate function for PostgreSQL , and in C code I would like to know if it is the first time that transition function of the aggregate be called.
For example, I define a aggregate function such as:
CREATE AGGREGATE my_aggregate (text) (
sfunc = my_transfunc,
stype = text,
finalfunc = my_finalfn,
initcond = '');
Then in C code of my_transfunc, how can I know if it is the first time my_transfunc be called ( but not the second, third ... time).
Datum my_transfunc(PG_FUNCTION_ARGS) {
// How to check if the first time function called
if (first_time) { then do something }
else { do some other things }
}
I don't want to use global or static variable here as this made my function is not threaded-safe which violent the requirement for my function.
Generally this is a matter of a proper setting of initcond. Typically you do not need to know whether the function is executed for the first time if only the algorithm is designed properly.
In your case, assuming that the function returns non-empty string, you can check whether the argument is empty (i.e. is equal to initcond). Of course, you can set initcond to a special value instead of an empty string.
Datum my_transfunc(PG_FUNCTION_ARGS) {
text *arg = PG_GETARG_TEXT_PP(0);
int32 arg_size = VARSIZE_ANY_EXHDR(arg);
if (arg_size == 0) { // arg == initcond }
else { // do some other things }
}

perl - How to replace an empty value in an array without using a variable for comparison?

I have an array where each element comes from a line delimited by tab.
Initial code:
#!/usr/bin/perl -w
use strict;
The code below is a piece of the code.
sub parser_domains {
my #params = #_;
my $interpro_line = "";
my #interpro_vector = ( );
my $idr_sub_id = $params[0];
my $idr_sub_start = $params[1]+1;
my $idr_sub_end = $params[2]+1;
my $interpro_id = "";
my $interpro_start_location = 0;
my $interpro_end_location = 0;
my $interpro_db = "";
my $interpro_length = 0;
my $interpro_db_accession = "";
my $interpro_signature = "";
my $interpro_evalue = "";
my $interpro_vector_size = 0;
my $interpro_sub_file= "";
my $idr_sub_lenght = ($idr_sub_end-$idr_sub_start)+1;
$interpro_sub_file = "$result_directory_predictor/"."$idr_sub_id/"."$idr_sub_id".".fsa.tsv";
#open file; if it fails, print a error and exits.
unless( open(TSV_FILE_DATA, $interpro_sub_file) ) {
print "Cannot open file \"$interpro_sub_file\"\n\n";
return;
}
my #interpro_file_line = <TSV_FILE_DATA>;
close TSV_FILE_DATA;
foreach $interpro_line (#interpro_file_line) {
#interpro_vector = split('\t',$interpro_line);
$interpro_id = $interpro_vector[0];
$interpro_db = $interpro_vector[3];
$interpro_db_accession = $interpro_vector[4];
$interpro_start_location = $interpro_vector[6];
$interpro_end_location = $interpro_vector[7];
$interpro_signature = $interpro_vector[11];
$interpro_length = ($interpro_end_location-$interpro_start_location) + 1;
if ($interpro_signature eq ""){
$interpro_signature = "NOPIR";
printf IDP_REGION_FILE "\nFound a $interpro_db domain with no IPR: starts at $interpro_start_location and ends at $interpro_end_location\n";
printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and and ends at $idr_sub_end\n";
printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";
domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
}
else{
for $entry_line (#entry_file_line) {
#entry_vector = split('\t',$entry_line);
$entry_ac = $entry_vector[0];
$entry_type = $entry_vector[1];
$entry_name = $entry_vector[2];
chomp($entry_name);
if ($interpro_signature eq $entry_ac) {
printf IDP_REGION_FILE "\nFound a $interpro_db domain with Interpro Signature $entry_ac: starts at $interpro_start_location and ends at $interpro_end_location\n";
printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
printf IDP_REGION_FILE "The Interpro Signature $entry_ac belongs to type $entry_type\n";
printf IDP_REGION_FILE "The name of $entry_ac is $entry_name\n";
printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and ends at $idr_sub_end\n";
printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";
domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
}
}
}
}
}
A example of tsv file (interproscan):
P51587 14086411a2cdf1c4cba63020e1622579 3418 Pfam PF09103 BRCA2, oligonucleotide/oligosaccharide-binding, domain 1 2670 2799 7.9E-43 T 15-03-2013
P51587 14086411a2cdf1c4cba63020e1622579 3418 ProSiteProfiles PS50138 BRCA2 repeat profile. 1002 1036 0.0 T 18-03-2013 IPR002093 BRCA2 repeat GO:0005515|GO:0006302
P51587 14086411a2cdf1c4cba63020e1622579 3418 Gene3D G3DSA:2.40.50.140 2966 3051 3.1E-52 T 15-03-2013
...
The scripts works perfectly, but the comparison $interpro_signature eq "" provides a warning.
Use of uninitialized value $interpro_signature in string eq at /home/duca/eclipse-workspace/idps/idp_parser_interpro.pl line 666.
So, I searched and tried manners to replace the empty value into the array before the comparison. I would like the empty value by "NOIPR".
I'm working with 9 completed genomes, and I have more than 324000 proteins to parse.
How can I replace the empty values in my array?
Thanks.
Your array may not have 12 elements (or the 12-th element may be undef)
my $interpro_signature = $interpro_vector[11] // 'some_default_value';
The // is the defined-or operator.
The error Use of uninitialized value means that the variable hasn't been initialized, or it's been set to undef.
See perldiag and use it regularly. Run code with perl -Mdiagnostics ... on errors, regularly.
The use warnings; is actually better than -w.
Update to a substantial edit of the question
From shown data it appears that yet other fields may not be given in the file; so proof all variables with defaults, much like for the array element at index 11 above. This is what you want to do in general anyway. For example, if there are all fields in the file but some may be empty (two tabs with nothing in between)
my #interpro_defaults = ('id_default', 'db_default', ...);
my ($interpro_id, $interpro_db, ...) =
map {
$interpro_vector[$_] // $interpro_defaults[$_]
} 0 .. $#interpro_defaults;
This relies on order (of variables) in the list, what can be error prone with variables; see below.
If some fields are simply not there there may be (far) more work to do.
There are too many separate variables, all related and named as $interpro_X (and then there are $idr_Y and $entry_Z, but fewer and perhaps manageable).
Can you not bundle them in a container-type variable or a data structure?
A hash %interpro seems suitable, with keys X (so, $interpro{id} etc). Then you can use them more easily and can perform some actions on the whole lot. You still have to watch for order when initializing since they are read sequentially, but it should be clearer this way. For example
my #interpro_vars = qw(id db db_accesssion ...);
my #interpro_vector = qw(id_default db_default ...);
my %interpro;
#interpro{#interpro_vars} = #interpro_vector;
# or simply
#interpro{qw(id db ...)} = qw(id_default db_default ...);
I've defined arrays with keys and values first and then used them, in case that you may want to later have those lists in arrays. If that's not the case you can initialize the hash with lists (the last line).
Here
my %h;
#h{LIST-keys} = LIST-values;
is a way to assign the list of LIST-values to the set of keys of the hash %h given in LIST-keys. They are assigned one for one, in the given order of both lists (which had better match in size). There is the # sigil in front of hash's keys since we are having a list (of keys) there, not a hash. Note that the hash must have been declared somewhere. See slices in perldata.
The problem is that your 3rd line contains only 9 elements. So
#interpro_vector = split('\t',$interpro_line);
for that line assigns only 9 elements to #interpro_vector but you then access $interpro_vector[11] (i.e. the 12th element) and that doesn't exist. You can now either check that #interpro_vector contains (at least) 12 elements:
if (#interpro_vector >= 12) {
...
}
Or you can use the defined-or operator as #zdim suggested to use a default value in case $interpro_vector[11] isn't defined:
$interpro_signature = $interpro_vector[11] // '';
The above line is equivalent to
if (defined $interpro_vector[11]) {
$interpro_signature = $interpro_vector[11];
} else {
$interpro_signature = '';
}
Now
if ($interpro_signature eq "") {
...
}
will work.

C Table implemented by an array

Basically I'm trying to implement a table with help of an array. The array functionality is already given and should not be altered. What I'm having trouble configuring is my table_remove function. Keep in mind that I'm not striving for the most effective way to do this.
What I tried to do is loop through the entire table to find if there is a key that matches.
If the key is found, save the position.
If not exit.
So after the position is found I set it to free the key & value on that position in hope that it will 'remove' the key & value pair from that position. However, my given test program returns "Removing the last element from a table does not result in an empty table.". If I add in the end "array_setVal(array, NULL, index) then the entire program crashes (Probably because it tries to write on that null position further on in the test).
So now I'm wondering if I'm approaching this issue the wrong way and have to do further operations to actually remove the value from the position without sort of messing up the position itself so next time I use table_insert, the position will be empty and another key & value pair will be inserted in that spot.
int saveRemovedIndex = -1;
/* Check if the key already exists */
for(int indexCounter = 0; indexCounter <= MAXINDEX; indexCounter++){
i = array_inspectValue(t->values, indexCounter);
if (t->cf(i->key,key)==0) {
saveRemovedIndex = indexCounter;
break;
}
}
/* Checks if the key actually exists in the table.
* If it exists, remove it. Else quit.*/
if (saveRemovedIndex != -1) {
i = array_inspectValue(t->values, saveRemovedIndex);
if(t->keyFree!=NULL) {
t->keyFree(i->key);
}
if(t->valueFree!=NULL) {
t->valueFree(i->value);
}
} else {
return;
}

Lua userdata array access and methods

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
>

Scope Issue: double array not accessible in if statement

I have a class method that is supposed to take an object and populate a few values in place. This is at the stage of functional demonstration, so the implementation will be better later. right now I just would like this to work.
In the code below, the districtID integer is successfully passed to the if statements. The rgb double array does not make it into the if statement scope. The values set at initialization make it all the way to the districtPoint.color without getting set inside the if statement.
the code below will not compile as is. I would like to know how to get the rgb variable to be visible within the if statement scope.
(note: I tried the naive solution of initializing the variables within the if statement. This clears the error, but doesn't let the new rgb variables out of the if scope)
// This method populates properties
+(void)setContantPropertiesForID:(DistrictPoint *)districtPoint
{
int districtID = [districtPoint.districtID intValue];
double rgb[3] = {0,0,0};
if (districtID == 1) {
districtPoint.title = #"District 1";
rgb = {1.0,0.0,0.0}; // error is expected expression
} else if (districtID == 2) {
districtPoint.title = #"District 1";
rgb = {0.0,1.0,0.0};
} else if (districtID == 3) {
districtPoint.title = #"District 1";
rgb = {0.0,0.0,1.0};
} else {
districtPoint.title = nil;
rgb = {1.0,1.0,1.0}; // error condition
}
districtPoint.color = [UIColor colorWithRed:rgb[0] green:rgb[1] blue:rgb[2] alpha:0.5];
}
This has nothing to do with the if statement. You can use the curly-braces notation to set an array's elements only when initializing (as you do, in fact, earlier in the code).

Resources