C functions with shared state

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

C functions with shared state

Tim Hunter
In Lua I can define two functions that share state through a closure:

function newf(state)
   local function a()
      -- do something with state
  end
  local function b()
     -- do something with state
  end
   return a, b
end

a, b = newf(state)

Can I do this in C? How?

The reason I ask is that, in my environment I don't have access to the normal srand and rand functions in the C library. What I have is a random-number generator function (only callable from C) that takes its seed as an argument and updates the seed for the next call, like this:

long seed, r;

r = myrand(&seed);

I'd like to use this function to generate random numbers, but define two functions, callable from Lua, to maintain the seed. The first function would act like srand and set the seed, the second function would act like rand, calling the random number generator and, upon its return, updating the seed for the next call.


Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Sam Roberts-2
On Mon, May 28, 2007 at 07:02:31PM -0400, Tim Hunter wrote:
> In Lua I can define two functions that share state through a closure:
...
> Can I do this in C? How?

With this

http://www.lua.org/manual/5.1/manual.html#lua_pushcclosure

Cheers,
Sam


Reply | Threaded
Open this post in threaded view
|

RE: C functions with shared state

Jerome Vuarand-2
In reply to this post by Tim Hunter
Tim Hunter wrote:
> In Lua I can define two functions that share state through a closure:
> 
> function newf(state)
>     local function a()
>        -- do something with state
>    end
>    local function b()
>       -- do something with state
>    end
>     return a, b
> end
> 
> a, b = newf(state)
> 
> Can I do this in C? How?
> 
> The reason I ask is that, in my environment I don't have access to
> the normal srand and rand functions in the C library. What I have is
> a random-number generator function (only callable from C) that takes
> its seed as an argument and updates the seed for the next call, like
> this:    
> 
> long seed, r;
> 
> r = myrand(&seed);
> 
> I'd like to use this function to generate random numbers, but define
> two functions, callable from Lua, to maintain the seed. The first
> function would act like srand and set the seed, the second function
> would act like rand, calling the random number generator and, upon
> its return, updating the seed for the next call.    

In your Lua example, the common state is stored as an upvalue. You can
also have upvalues in C. But you can also achieve the same effect with a
function environment, and in your case, I think a function environment
is more suited. Here is an example:

/****************************************************/
/* myrand.c */
#include <lua.h>
#include <lauxlib.h>

long myrand(long* seed)
{
    *seed += 1;
    /* return a not really random number */
    return *seed;
}

int lua__myrand(lua_State* L)
{
    long seed, value;
    /* Get seed from environment */
    lua_getfield(L, LUA_ENVIRONINDEX, "seed");
    seed = lua_tonumber(L, -1);
    lua_pop(L, 1);
    /* Call your C function */
    value = myrand(&seed);
    /* Update environment */
    lua_pushnumber(L, seed);
    lua_setfield(L, LUA_ENVIRONINDEX, "seed");
    /* Return random number */
    lua_pushnumber(L, value);
    return 1;
}

/* Before calling your lua__myrand you must be sure it has an
environment.
   'newf' will do that job. */
int lua__newf(lua_State* L)
{
    int state;
    long seed = luaL_checknumber(L, 1);
    /* Create an environment table */
    lua_newtable(L);
    lua_pushnumber(L, seed);
    lua_setfield(L, -2, "seed");
    state = lua_gettop(L);
    /* Push a */
    lua_pushcfunction(L, lua__myrand);
    /* Set its environment to state */
    lua_pushvalue(L, state);
    lua_setfenv(L, -2);
    /* Push b */
    lua_pushcfunction(L, lua__myrand);
    /* Set its environment to state */
    lua_pushvalue(L, state);
    lua_setfenv(L, -2);
    /* Return a and b */
    return 2;
}

static luaL_Reg functions[] = {
    {"newf", lua__newf},
    {0, 0},
};

LUALIB_API int luaopen_myrand(lua_State* L)
{
    lua_newtable(L);
    luaL_register(L, 0, functions);
    return 1;
}

------------------------------------------------------
-- test.lua
-- Then from Lua load the module
local myrand = require 'myrand'
-- Create a pair of functions
local a,b = myrand.newf(0)
-- And generate your randomnumbers, with a shared seed
repeat
    print(a(), b())
until the_end_of_time


Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Luiz Henrique de Figueiredo
In reply to this post by Sam Roberts-2
> With this
> 
> http://www.lua.org/manual/5.1/manual.html#lua_pushcclosure

Or you can use the registry.

Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Tim Hunter
In reply to this post by Jerome Vuarand-2
Jerome Vuarand wrote:
In your Lua example, the common state is stored as an upvalue. You can
also have upvalues in C. But you can also achieve the same effect with a
function environment, and in your case, I think a function environment
is more suited.

Okay, I understand your example for using the environment, and thank you Mr. Vuarand very much for the code!

However, I'd like to also understand using lua_pushcclosure as well, as suggested by Mr. Roberts and Sr. de Figueiredo. I understand how to use lua_pushcclosure for a single function but how would I use it to define two functions that share an upvalue? Do I just push the seed value on the stack and then call lua_pushcclosure for "srand" and then push the seed value again and call lua_pushcclosure again for "rand"?

long seed = 12345;

lua_pushinteger(L, seed);
lua_pushcclosure(L, srand, 1);
lua_pushinteger(L, seed);
lua_pushcclosure(L, rand, 1);


Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Sam Roberts-2
On Mon, May 28, 2007 at 08:12:58PM -0400, Tim Hunter wrote:
> Jerome Vuarand wrote:
> >In your Lua example, the common state is stored as an upvalue. You can
> >also have upvalues in C. But you can also achieve the same effect with a
> >function environment, and in your case, I think a function environment
> >is more suited. 
> 
> Okay, I understand your example for using the environment, and thank you 
> Mr. Vuarand very much for the code!
> 
> However, I'd like to also understand using lua_pushcclosure as well, as 
> suggested by Mr. Roberts and Sr. de Figueiredo. I understand how to use 
> lua_pushcclosure for a single function but how would I use it to define 
> two functions that share an upvalue? Do I just push the seed value on 
> the stack and then call lua_pushcclosure for "srand" and then push the 
> seed value again and call lua_pushcclosure again for "rand"?

I think Jerome's right the the fenv is more suited. I forgot there isn't
a one-shot for sharing the same upvalue among multiple c functions. I
was thinking of luaL_openlib(), but it requires the seed to be in a
table to be shared.

> long seed = 12345;
> 
> lua_pushinteger(L, seed);
> lua_pushcclosure(L, srand, 1);
> lua_pushinteger(L, seed);
> lua_pushcclosure(L, rand, 1);

You have to do more like
lua_pushtable(L)
lua_pushinteger(L, DEF_SEEED)
lua_setfield(L, -2, "seed");
lua_pushvalue(L, -1);
lua_pushcclosure(L, seedfn, 1);
lua_pushvalue(L, -1);
lua_pushcclosure(L, randfn, 1);

luaL_openlib() does some of this work for you, but not making the table.

Then in seedfn you get the table as upvalue 1, and set the "seed" field.

In randfn you get the table as upvalue 1, and get/update the "seed"
field.

Sam


Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Jerome Vuarand
2007/5/28, Sam Roberts <[hidden email]>:
On Mon, May 28, 2007 at 08:12:58PM -0400, Tim Hunter wrote:
I think Jerome's right the the fenv is more suited. I forgot there isn't
a one-shot for sharing the same upvalue among multiple c functions. I
was thinking of luaL_openlib(), but it requires the seed to be in a
table to be shared.

[...]

You have to do more like
lua_pushtable(L)
lua_pushinteger(L, DEF_SEEED)
lua_setfield(L, -2, "seed");
lua_pushvalue(L, -1);
lua_pushcclosure(L, seedfn, 1);
lua_pushvalue(L, -1);
lua_pushcclosure(L, randfn, 1);

That's exactly what I meant. You can share the upvalue only if it is
of a type handled by reference in Lua. That is not the case for
numbers, so you have to wrap it in a table, or eventually a full
userdata to gain some speed.

Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Tim Hunter
JÃrÃme Vuarand wrote:
2007/5/28, Sam Roberts <[hidden email]>:
I think Jerome's right the the fenv is more suited. I forgot there isn't
a one-shot for sharing the same upvalue among multiple c functions. I
was thinking of luaL_openlib(), but it requires the seed to be in a
table to be shared.

That's exactly what I meant. You can share the upvalue only if it is
of a type handled by reference in Lua. That is not the case for
numbers, so you have to wrap it in a table, or eventually a full
userdata to gain some speed.

I understand. Thanks for your advice, gentlemen!


Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

Duck-2
In reply to this post by Tim Hunter
>The reason I ask is that, in my environment I 
>don't have access to the normal srand and rand >functions in the C library. What I have is a
 
This isn't an answer to your question but it may be a solution to the overall problem :-)

Have a look at:

http://www.tecgraf.puc-rio.br/~lhf/ftp/lua

and specifically:

http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/5.0/lrandom.tar.gz

which is lhf's Mersenne Twister library for Lua.

You get a library by lhf, a Lua lesson, a really decent and trusted pseudo random number generator, and a traditionally-flavoured seed() function all in a few KB of compiled object code...

I always compile this library into my Lua binary so that I have no excuse for ending up relying on some dodgy random function (e.g. rand()/srand()).

Pseudorandoms are, well, pseudo...but you may as well be as legitimately pseudo as you can :-)

Reply | Threaded
Open this post in threaded view
|

Re: C functions with shared state

fatfatson
a naive question:
 
> You have to do more like
> lua_pushtable(L)
> lua_pushinteger(L, DEF_SEEED)
> lua_setfield(L, -2, "seed");
> lua_pushvalue(L, -1);
> lua_pushcclosure(L, seedfn, 1);
> lua_pushvalue(L, -1);
> lua_pushcclosure(L, randfn, 1);

 
when the second "lua_pushvalue(L,-1)" executes, is it duplicating the C-closure to the top?? but the next line needs a table as its upvalue??