lua-users home
lua-l archive

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




El mié, 20 de oct. de 2021 a la(s) 15:01, Sean Conner (sean@conman.org) escribió:
It was thus said that the Great Jairo A. del Rio once stated:
> Hi, list. I'm trying to understand how to write Lua modules from C (I use
> GSL as an example). Code is below.

  I have many C based Lua modules written by hand, you can look at:

        https://github.com/spc476/lua-conmanorg/tree/master/src


Cool. I'm gonna take a look at them soon.
 
  The closest one I have to what you are trying to do is tcc.c.  I don't
think you need to really understand what it does (it wraps the Tiny C
compiler as a Lua module).

  In any case, let's go through your code:

> #include <gsl/gsl_vector.h>
> #include "lua.h"
> #include "lauxlib.h"
>
> #define VECTOR_MT "GSL vector"
>
> static int vector_new(lua_State *L)
> {
>   int i;
>   int n = lua_gettop(L);
>   gsl_vector *t = gsl_vector_alloc(n);
>   for (i = 0; i < n; i++)
>   {
>     gsl_vector_set(t, i, luaL_checknumber(L,i+1));
>   }
> //Is this right?
>   gsl_vector *r = (gsl_vector *)lua_newuserdata(L, sizeof(gsl_vector));
>   *r = *t;
>   luaL_getmetatable(L, VECTOR_MT);
>   lua_setmetatable(L, -2);
>   return 1;
> }

  Almost there---

        static int vector_new(lua_State *L)
        {
          int i;
          int n = lua_gettop(L);
          gsl_vector *v = gsl_vector_alloc(n);
          for (i = 0 ; i < n ; i++)
            gsl_vector_set(v,i,luaL_checknumber(L,i + 1));

          gal_vector **r = lua_newuserdata(L,sizeof(gsl_vector *)); /* [1] */
          *r = v;
          luaL_setmetatable(L,VECTOR_MT); /* [2] */
          return 1;
        }

[1]     Since the GLS library is doing the allocation, let it.  Here, we are
        defining r to be a pointer to a pointer to a GSL vector, which is
        the space that Lua will manage.  You will need to add a __gc
        metamethod though.  This will work:

        static int vector___gc(lua_State *L)
        {
          gsl_vector **v = luaL_checkudata(L,1,VECTOR_MT); /* [3] */
          if (*r != NULL)
          {
            gsl_vector_free(*r); /* [4] */
            *r = NULL;
          }

          return 0;
        }

[2]     If you are using Lua 5.1, you can replace this function with:

                luaL_getmetatable(L,VECTOR_MT);
                lua_setmetatable(L,-2);

[3]     If you are using a C compiler (and NOT a C++ compiler) you don't
        need the pointer casts.  Also, what you are getting back here is a
        pointer to a pointer to a GSL thing.

It makes sense
 
[4]     Since we have a pointer to a pointer to a GSL objet, this will
        dereference the pointer-to-pointer to get the pointer.

> static int vector_get(lua_State *L)
> {
>   gsl_vector *v = (gsl_vector *)lua_touserdata(L,1);
>   int max = (int)v->size;
>   int n = luaL_checkinteger(L,2)-1;
>   if ((0<=n) && (n <= max))
>     lua_pushnumber(L, gsl_vector_get(v,n));
>   else
>     lua_pushnil(L);
>   return 1;
> }

        gsl_vector **r = luaL_checkudata(L,1,VECTOR_MT);
        gsl_vector  *v = *r; /* get to the actual object */

  The rest is fine.

> static int vector_m_add(lua_State *L)
> {
>   gsl_vector *v1 = (gsl_vector *)lua_touserdata(L,1);
>   gsl_vector *v2 = (gsl_vector *)lua_touserdata(L,2);
>   if ((v1 -> size) == (v2 -> size))
>   {
> // I'm not sure whether this is right or not, but it seems to work so far
>     gsl_vector *t = gsl_vector_alloc(v1 -> size);
>     gsl_vector_memcpy(t, v1);
>     gsl_vector_add(t,v2);
>     gsl_vector *r = (gsl_vector *)lua_newuserdata(L, sizeof(gsl_vector));
>     luaL_getmetatable(L, VECTOR_MT);
>     lua_setmetatable(L, -2);
>     *r = *t;
>   }
>   else
>   {
>     lua_pushnil(L);
>   }
>   return 1;
> }

  Follow the code in vector_new().

  [ snip rest of code---no comments, looked decent ]

> I have two questions:
>
> 1. Am I right wrt "new", i.e., the way userdata is created?

  Not quite.  You're close though.

> 2. According to some docs I've read, userdata content should be freed by
> defining a function for __gc. However, I don't understand when or how to do
> that, as defining a function using gsl_vector_free in the case above is
> giving me an error prompt complaining about an invalid pointer.

  That's probably because you're passing in memory allocated by Lua and the
GSL library is getting confused.  See the vector___gc() function for how to
do this safely.

> I don't want a ready-made application to create bindings to GSL or an
> already existing one (I think two are freely available in GitHub) as I only
> use it as an example. I don't want a Lua solution, either (I know how to do
> that using pure Lua). Instead, I want to better understand the way Lua
> modules are defined when C is used, and perhaps to better understand C
> given that pointers are not intuitive for me. Thank you in advance and
> sorry for any inconvenience caused by this noob question.

  Don't apologize---you tried, and you showed your work.  That's commendable
in and of itself.  Yes, pointers in C can be confusing, especially pointers
to pointers (which, unfortunately, you need here).  I hope the code I
presented will help you in your endevour.


Yes. In fact, by reading your code I think I understand C pointers a bit better. With Luiz's comments and yours included, my module finally works!  Thank you very much.

Best regards,

Jairo
 
  -spc