Skip to content

Instantly share code, notes, and snippets.

@chtlp
Forked from kizzx2/fun.cpp
Created August 8, 2014 06:54

Revisions

  1. @kizzx2 kizzx2 revised this gist Jan 11, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions fun.lua
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    -- fun.lua

    -- Because the metatable has been exposed
    -- to us, we can actually add new functions
    -- to Foo
  2. @kizzx2 kizzx2 revised this gist Jan 11, 2012. 2 changed files with 16 additions and 10 deletions.
    17 changes: 11 additions & 6 deletions fun.cpp
    Original file line number Diff line number Diff line change
    @@ -48,7 +48,7 @@ int l_Foo_constructor(lua_State * l)
    *udata = new Foo(name);

    // Usually, we'll just use "Foo" as the second parameter, but I
    // say luaL_Foo here is distinguish the difference:
    // say luaL_Foo here to distinguish the difference:
    //
    // This 2nd parameter here is an _internal label_ for luaL, it is
    // _not_ exposed to Lua by default.
    @@ -62,7 +62,7 @@ int l_Foo_constructor(lua_State * l)
    // 2| userdata |-2
    // 1| string parameter |-3
    //
    // So this line says the metatable for the user data to the luaL_Foo
    // So the following line sets the metatable for the user data to the luaL_Foo
    // metatable
    //
    // We must set the metatable here because Lua prohibits setting
    @@ -99,12 +99,12 @@ int l_Foo_add(lua_State * l)

    // The Lua stack at this point looks like this:
    //
    // 4| string |-1
    // 4| result string |-1
    // 3| metatable "luaL_foo" |-2
    // 2| userdata |-3
    // 1| string parameter |-4
    //
    // Return 1 to return the string to Lua callsite.
    // Return 1 to return the result string to Lua callsite.

    return 1;
    }
    @@ -132,13 +132,12 @@ void RegisterFoo(lua_State * l)
    // internally to identity things.
    luaL_newmetatable(l, "luaL_Foo");

    // Set the fields of the metatable we just created.
    // Register the C functions _into_ the metatable we just created.
    luaL_register(l, NULL, sFooRegs);

    // The Lua stack at this point looks like this:
    //
    // 1| metatable "luaL_Foo" |-1
    //
    lua_pushvalue(l, -1);

    // The Lua stack at this point looks like this:
    @@ -154,6 +153,12 @@ void RegisterFoo(lua_State * l)
    //
    // 1| metatable "luaL_Foo" |-1

    // The luaL_Foo metatable now has the following fields
    // - __gc
    // - __index
    // - add
    // - new

    // Now we use setglobal to officially expose the luaL_Foo metatable
    // to Lua. And we use the name "Foo".
    //
    9 changes: 5 additions & 4 deletions fun.lua
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    -- fun.lua

    -- Because the metatable has been exposed
    -- to us, we can actually add new functions
    -- to Foo
    @@ -10,9 +8,12 @@ end
    local foo = Foo.new("fred")
    local m = foo:add(3, 4)

    -- prints "fred: 3 + 4 = 7"
    -- "fred: 3 + 4 = 7"
    print(m)

    -- "Hello, I am a Foo"
    foo:speak()

    -- Let's rig the original metatable
    Foo.add_ = Foo.add
    function Foo:add(a, b)
    @@ -21,5 +22,5 @@ end

    m = foo:add(9, 8)

    -- prints "here comes the magic: fred: 9 + 8 = 17"
    -- "here comes the magic: fred: 9 + 8 = 17"
    print(m)
  3. @kizzx2 kizzx2 revised this gist Jan 11, 2012. 2 changed files with 4 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions fun.cpp
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // fun.cpp

    extern "C"
    {
    #include <lua.h>
    2 changes: 2 additions & 0 deletions fun.lua
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    -- fun.lua

    -- Because the metatable has been exposed
    -- to us, we can actually add new functions
    -- to Foo
  4. @kizzx2 kizzx2 created this gist Jan 11, 2012.
    177 changes: 177 additions & 0 deletions fun.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,177 @@
    extern "C"
    {
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    }

    #include <iostream>
    #include <sstream>

    class Foo
    {
    public:
    Foo(const std::string & name) : name(name)
    {
    std::cout << "Foo is born" << std::endl;
    }

    std::string Add(int a, int b)
    {
    std::stringstream ss;
    ss << name << ": " << a << " + " << b << " = " << (a+b);
    return ss.str();
    }

    ~Foo()
    {
    std::cout << "Foo is gone" << std::endl;
    }

    private:
    std::string name;
    };

    // The general pattern to binding C++ class to Lua is to write a Lua
    // thunk for every method for the class, so here we go:

    int l_Foo_constructor(lua_State * l)
    {
    const char * name = luaL_checkstring(l, 1);

    // We could actually allocate Foo itself as a user data but
    // since user data can be GC'ed and we gain unity by using CRT's heap
    // all along.
    Foo ** udata = (Foo **)lua_newuserdata(l, sizeof(Foo *));
    *udata = new Foo(name);

    // Usually, we'll just use "Foo" as the second parameter, but I
    // say luaL_Foo here is distinguish the difference:
    //
    // This 2nd parameter here is an _internal label_ for luaL, it is
    // _not_ exposed to Lua by default.
    //
    // Effectively, this metatable is not accessible by Lua by default.
    luaL_getmetatable(l, "luaL_Foo");

    // The Lua stack at this point looks like this:
    //
    // 3| metatable "luaL_foo" |-1
    // 2| userdata |-2
    // 1| string parameter |-3
    //
    // So this line says the metatable for the user data to the luaL_Foo
    // metatable
    //
    // We must set the metatable here because Lua prohibits setting
    // the metatable of a userdata in Lua. The only way to set a metatable
    // of a userdata is to do it in C.
    lua_setmetatable(l, -2);

    // The Lua stack at this point looks like this:
    //
    // 2| userdata |-1
    // 1| string parameter |-2
    //
    // We return 1 so Lua callsite will get the user data and
    // Lua will clean the stack after that.

    return 1;
    }

    Foo * l_CheckFoo(lua_State * l, int n)
    {
    // This checks that the argument is a userdata
    // with the metatable "luaL_Foo"
    return *(Foo **)luaL_checkudata(l, n, "luaL_Foo");
    }

    int l_Foo_add(lua_State * l)
    {
    Foo * foo = l_CheckFoo(l, 1);
    int a = luaL_checknumber(l, 2);
    int b = luaL_checknumber(l, 3);

    std::string s = foo->Add(a, b);
    lua_pushstring(l, s.c_str());

    // The Lua stack at this point looks like this:
    //
    // 4| string |-1
    // 3| metatable "luaL_foo" |-2
    // 2| userdata |-3
    // 1| string parameter |-4
    //
    // Return 1 to return the string to Lua callsite.

    return 1;
    }

    int l_Foo_destructor(lua_State * l)
    {
    Foo * foo = l_CheckFoo(l, 1);
    delete foo;

    return 0;
    }

    void RegisterFoo(lua_State * l)
    {
    luaL_Reg sFooRegs[] =
    {
    { "new", l_Foo_constructor },
    { "add", l_Foo_add },
    { "__gc", l_Foo_destructor },
    { NULL, NULL }
    };

    // Create a luaL metatable. This metatable is not
    // exposed to Lua. The "luaL_Foo" label is used by luaL
    // internally to identity things.
    luaL_newmetatable(l, "luaL_Foo");

    // Set the fields of the metatable we just created.
    luaL_register(l, NULL, sFooRegs);

    // The Lua stack at this point looks like this:
    //
    // 1| metatable "luaL_Foo" |-1
    //
    lua_pushvalue(l, -1);

    // The Lua stack at this point looks like this:
    //
    // 2| metatable "luaL_Foo" |-1
    // 1| metatable "luaL_Foo" |-2

    // Set the "__index" field of the metatable to point to itself
    // This pops the stack
    lua_setfield(l, -1, "__index");

    // The Lua stack at this point looks like this:
    //
    // 1| metatable "luaL_Foo" |-1

    // Now we use setglobal to officially expose the luaL_Foo metatable
    // to Lua. And we use the name "Foo".
    //
    // This allows Lua scripts to _override_ the metatable of Foo.
    // For high security code this may not be called for but
    // we'll do this to get greater flexibility.
    lua_setglobal(l, "Foo");
    }

    int main()
    {
    lua_State * l = luaL_newstate();
    luaL_openlibs(l);
    RegisterFoo(l);

    int erred = luaL_dofile(l, "fun.lua");
    if(erred)
    std::cout << "Lua error: " << luaL_checkstring(l, -1) << std::endl;

    lua_close(l);

    return 0;
    }
    23 changes: 23 additions & 0 deletions fun.lua
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    -- Because the metatable has been exposed
    -- to us, we can actually add new functions
    -- to Foo
    function Foo:speak()
    print("Hello, I am a Foo")
    end

    local foo = Foo.new("fred")
    local m = foo:add(3, 4)

    -- prints "fred: 3 + 4 = 7"
    print(m)

    -- Let's rig the original metatable
    Foo.add_ = Foo.add
    function Foo:add(a, b)
    return "here comes the magic: " .. self:add_(a, b)
    end

    m = foo:add(9, 8)

    -- prints "here comes the magic: fred: 9 + 8 = 17"
    print(m)