How to traverse a Lua table in C?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

How to traverse a Lua table in C?

Jon Kleiser-2
Dear Lua people,

I'm about to write some C functions to provide Mac Lua with some
(hopefully) flexible dialog boxes. My idea was to let my Lua script define
the dialog box on the fly through a Lua table/list of item specs, somewhat
like this:

  name, pwd = dialog{{type=btn, name="OK"},{type=btn, name="Cancel"},
                     {type=fld, name="Name"},{type=pwfld, name="Password"}}

What I would now like to know, is what's the most elegant way to transfer
(the values of) such a nested table from the calling Lua script to the C
function? Should I let the calling script push the entire table as one
object onto lua2C, or should I let the script push lots of smaller units?

In "5.5 - How do I traverse a table in C?*" in the Lua FAQ, the suggestion is:

  lua_pushobject(table);
  lua_pushcfunction(f);
  lua_call("foreach");

I'm suspecting, however, that this technique may not be very useful to me.
Will it be safe to have a Lua script call a C function that in turn calls
lua_call("foreach") ? If it is safe, I may just end up with a lot of
smaller units on lua2C, instead of a table ...

(I guess the proper name for "lua_call" is now "lua_callfunction".)


Sincerely,

Jon Kleiser


Reply | Threaded
Open this post in threaded view
|

Re: How to traverse a Lua table in C?

David Jeske-2
On Wed, Dec 02, 1998 at 06:28:14PM -0200, Jon Kleiser wrote:
> I'm about to write some C functions to provide Mac Lua with some
> (hopefully) flexible dialog boxes. My idea was to let my Lua script define
> the dialog box on the fly through a Lua table/list of item specs, somewhat
> like this:
> 
>   name, pwd = dialog{{type=btn, name="OK"},{type=btn, name="Cancel"},
>                      {type=fld, name="Name"},{type=pwfld, name="Password"}}

You should look at the Lua/Tk binding for ideas..

> In "5.5 - How do I traverse a table in C?*" in the Lua FAQ, the suggestion is:
> 
>   lua_pushobject(table);
>   lua_pushcfunction(f);
>   lua_call("foreach");
> 
> I'm suspecting, however, that this technique may not be very useful to me.
> Will it be safe to have a Lua script call a C function that in turn calls
> lua_call("foreach") ? If it is safe, I may just end up with a lot of
> smaller units on lua2C, instead of a table ...
> 
> (I guess the proper name for "lua_call" is now "lua_callfunction".)

The method above is much simpler than what you have to do if you want
to walk the table entirely from C-code. I have some loops in my
software which do just this, and they are pretty nasty. The problem is
that you have to convert lots of stuff to lua_refs to make sure the
pointer dosn't get pulled out from under you. 

I've attached a snipppit of code which I use to walk a lua table and
pull out data. I'd love to come up with a more structured way to do
this on the C side, but so far I've not come up with it. Here is the
small code snippit, and the attachment contains the whole method it
came from:

        // count the entries (table_len), remember to account 
        // for the index string  
        
        next_fn = lua_getglobal("next"); // get the "next(a,b);" function
        temp_ref = LUA_NOOBJECT; // set to nil
              
        
        do {
            
                lua_beginblock();
                lua_pushobject(lua_getref(table_ref));
                if (temp_ref == LUA_NOOBJECT) {       
                        lua_pushnil();                
                } else {                              
                        lua_pushobject(lua_getref(temp_ref));
                }       
                if (lua_callfunction(next_fn)) {
                        // an error occured!!!  
                        lua_error("error calling next() builtin Lua function");
                }       
                        
                // now get off the two return values
                
                tbl_idx = lua_getresult(1);  // index, should be a string 
                                                // or number          
                tbl_val = lua_getresult(2);  // value, should be a table, 
                                                // unless it's the indexname

                if (lua_istable(tbl_val)) {
                        // the value is another table, so store and count it
                        table_len++; 
                }       
                        
                if (!lua_isnil(tbl_idx)) {
                        // we're not going to exit the loop this time!
                        lua_pushobject(tbl_idx);
                        temp_ref = lua_ref(0); // not locked
                        
                }       
                lua_endblock();
        } while (!lua_isnil(tbl_idx));
        

-- 
David Jeske (N9LCA) + http://www.chat.net/~jeske/ + [hidden email]
void SpriteType::parseSpriteTable(SPRITECHUNK **dest, lua_Object a_tbl) {
	lua_Object next_fn, temp_ref, tbl_idx, tbl_val; 
	int table_len = 0;
	int curindex = 0;
	int cur_count = 0;
	int index_ref, value_ref;
	int table_ref;
	char *index_string;
	IMAGELIST *a_list = NULL; // in case this is a list
	IMAGE *an_image = NULL;   // in case this is an image

	// check that we have a table

	dbgMsg(c_excessive,"SpriteType::parseSpriteType() entered.\n");
	if (!lua_istable(a_tbl)) {
		lua_error("SpriteType::parseSpriteTable() passed non-table.");
	}

	lua_pushobject(a_tbl);
	table_ref = lua_ref(0);

	// get the index string
	{
		lua_Object index_string_obj;
		lua_pushobject(lua_getref(table_ref));
		lua_pushstring("IndexedBy");
		if (lua_isstring(index_string_obj = lua_gettable())) {
			index_string = lua_getstring(index_string_obj);
		} else {
			index_string = NULL;
		}
	}

	// count the entries (table_len), remember to account for the index string

	next_fn = lua_getglobal("next"); // get the "next(a,b);" function
	temp_ref = LUA_NOOBJECT; // set to nil



	do {

		lua_beginblock();
		lua_pushobject(lua_getref(table_ref));
		if (temp_ref == LUA_NOOBJECT) {
			lua_pushnil();
		} else {
			lua_pushobject(lua_getref(temp_ref));
		}
		if (lua_callfunction(next_fn)) {
			// an error occured!!!
			lua_error("error calling next() builtin Lua function");
		}
		
		// now get off the two return values
		
		tbl_idx = lua_getresult(1);  // index, should be a string or number
		tbl_val = lua_getresult(2);  // value, should be a table, unless it's the indexname
	
		if (lua_istable(tbl_val)) {
			// the value is another table, so store and count it
			table_len++;
		}
		
		if (!lua_isnil(tbl_idx)) {
			// we're not going to exit the loop this time!
			lua_pushobject(tbl_idx);
			temp_ref = lua_ref(0); // not locked
			
		}
		lua_endblock();		
	} while (!lua_isnil(tbl_idx));

	

	// check if we have another sprite chunk or an actual image

	if (index_string) {
		// we have another list of sprite chunks

		printf("Table(IndexedBy = %s) with %d elements\n",index_string,table_len);
		// allocate the table!!!

		// should this be "table_len - 1" below???
		(*dest) = (SPRITECHUNK *)malloc(sizeof(SPRITECHUNK) + (sizeof(IMAGELISTENTRY) * table_len)); 
		(*dest)->content_type = TYPE_IMAGELIST;
			
		a_list = &((*dest)->content.im_list);
		a_list->list_len = table_len;
		
		// check if the index is a builtin

		if (index_string[0] == '@') {
			// it's a builtin!
			a_list->index_type = PREDEF_INDEX;
			a_list->predef_index_type = lookup_builtin(index_string);
		} else {
			// it's a lua variable
			a_list->index_type = LUA_VAR;

			if (strlen(index_string) > sizeof(a_list->luavar_name)) {
				lua_error("IndexedBy name too big in table"); // should do better than this
			}
			strcpy(a_list->luavar_name, index_string);
		}


	// now go through the table AGAIN and do something which each element
	dbgMsg(c_excessive,"starting table parse\n");

	temp_ref = LUA_NOOBJECT; // set to nil
	curindex = 0;
	cur_count = 0;
	int reached_end = 0;

	

	do {
		lua_beginblock();
		lua_pushobject(lua_getref(table_ref));
		if (cur_count++ == 0) {
			dbgMsg(c_excessive,"DrawAt(): pushed nil\n");
			lua_pushnil();
		} else {
			lua_pushobject(lua_getref(temp_ref));
		}

		if (lua_callfunction(next_fn)) {
			// an error occured!!!
			lua_error("error calling next() builtin Lua function");
		}
			
		// now get off the two return values
			
		tbl_idx = lua_getresult(1);  // index, should be a string or number
		tbl_val = lua_getresult(2);  // value, should be a table, unless it's the indexname

		lua_pushobject(tbl_idx);
		index_ref = lua_ref(0);
		lua_pushobject(tbl_val);
		value_ref = lua_ref(0);

		if (lua_isnil(tbl_idx)) {
			// no more elements in the table...
			dbgMsg(c_excessive,"end of table reached\n");
			break; // exit the while loop!
		}
		
		if (lua_isstring(tbl_idx)) {
			reached_end = lua_isnil(lua_getref(index_ref)); // what's with this?
				
			dbgMsg(c_excessive,"handle element(%d/%d/%d): %s\n",curindex,table_len,
				reached_end,lua_getstring(lua_getref(index_ref)));
			// don't count it because it's a string
		} 
				
	
		if (lua_istable(lua_getref(value_ref))) {
				//the value is another table, so store and count it
			if (curindex < table_len) {
				if (lua_isstring(lua_getref(index_ref))) {
					strcpy(a_list->list[curindex].name,lua_getstring(lua_getref(index_ref))); // size check!!
					lua_beginblock();
					this->parseSpriteTable(&(a_list->list[curindex].ptr),lua_getref(value_ref)); // recurse
					lua_endblock();
				} else {
					dbgMsg(c_error,"non-string index on sprite chunk!\n");
					lua_error("can't allow sprite chunk with non-string index currently");
				}
			}
			curindex++;
				
		}

		temp_ref = index_ref;
		dbgMsg(c_excessive,"Finished parse (%d/%d/%d)\n",
				curindex,table_len,lua_isnil(lua_getref(temp_ref)));

		lua_endblock();
	} while ((!lua_isnil(lua_getref(temp_ref))) && (curindex < table_len));

	

	
	// and sort them

	} else {
		// we have an actual image
		char *image_name;

		
		// print it out ! 
//		lua_pushobject(a_table);
//		lua_call("printTables");

		lua_pushobject(lua_getref(table_ref));
		lua_pushnumber(1.0f);
		if ((image_name = lua_getstring(lua_gettable())) != NULL) {
			dbgMsg(c_excessive,"Loading image [%s]...\n",image_name);
		} else {
			lua_error("Failed loading image, first element not string");
		}

		// allocate the image

		(*dest) = (SPRITECHUNK *)malloc(sizeof(SPRITECHUNK));
		(*dest)->content_type = TYPE_IMAGE;

		an_image = &((*dest)->content.im_data);
		this->loadImage(an_image,image_name);
	}

	
	return;
Reply | Threaded
Open this post in threaded view
|

Re: How to traverse a Lua table in C?

Luiz Henrique de Figueiredo
In reply to this post by Jon Kleiser-2
>From: David Jeske <[hidden email]>
>
>On Wed, Dec 02, 1998 at 06:28:14PM -0200, Jon Kleiser wrote:
>> I'm about to write some C functions to provide Mac Lua with some
>> (hopefully) flexible dialog boxes. My idea was to let my Lua script define
>> the dialog box on the fly through a Lua table/list of item specs, somewhat
>> like this:
>> 
>>   name, pwd = dialog{{type=btn, name="OK"},{type=btn, name="Cancel"},
>>                      {type=fld, name="Name"},{type=pwfld, name="Password"}}
>
>You should look at the Lua/Tk binding for ideas..

right. the "proper" way is to be declarative:

dialog{button{name="OK"}, button{name="Cancel"},
       field{name="Name"}, password{name="Password"}}

if you need the field "type", then the constructors add these automatically:

function button(t)
	t.type=btn
	...
	return t
end

--lhf

Reply | Threaded
Open this post in threaded view
|

Lua extensions

Mark Tigges
Hi Luiz,

How are the extension types coming?  I am really using the Python ones
heavily and so are other people here too. It might be hard for me to
switch now.  Have you finished them?


Best regards,
mark.