lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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