Lua-cxx - C++11 APIs for creating Lua modules

Aaron Faanes <[email protected]> Wed, 28 May 2014 18:53:33 -0500
Newsgroups gmane.comp.lang.lua.bind.user
Message-ID <CAEuNnPTb+egGPdZp4uQAfEcDfZKVaw0FiQFXQ9S-uFvjVHTfjg@mail.gmail.com>
--===============1089521126207946948==
Content-Type: multipart/alternative; boundary=001a11340c865a7e9b04fa7e86b0

--001a11340c865a7e9b04fa7e86b0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Hello!

I would like to share Lua-cxx, a MIT-licensed project that I wrote that
aids in writing modules and bindings for Lua. I posted this to the Lua
mailing list, but I figured those
who use Luabind would be interested to see another C++ take on Lua's API,
so I decided to post here as well. :)

Compared to Luabind, Lua-cxx is lower-level. Lua-cxx does not introduce an
object model for Lua, nor does it use Luabind's policy model for arguments.
It does use templates widely, including those features like variadic
templates added in C++11, to provide a similar level of expressive power as
Luabind.

Lua-cxx, like Luabind, doesn't provide a complete fa=C3=A7ade over Lua's C =
API.
On the contrary, I find Lua's C API to be amazingly well-designed, so
I've tried to ensure that Lua-cxx can be intermixed freely with Lua's C
API. In fact, most of the Lua C API has no analog in Lua-cxx - I just use
the original. ;)

    // Add all arguments
    int add_several(lua::state* const state)
    {
        // Get each argument
        int sum =3D 0;
        for (int i =3D 1; i <=3D lua_gettop(state); ++i) {
            sum +=3D lua::get<int>(state, i);
        }

        // Return the value
        lua::push(state, sum);
        lua_replace(state, 1);
        return 1;
    }

That being said, there are several places where Lua-cxx greatly simplify
common tasks. For instance, Lua has a number of lua_push* functions that
can be replaced with Lua-cxx's lua::push template and appropriate
specializations. You can extend this specialization with your own types,
and Lua-cxx's other features will immediately support them.

C++11 added variadic templates, which can be used to provide a way to push
a function of any arity into Lua without needing to write the marshalling
code yourself or running a preprocessor:

// Standard C API is, of course, supported
int create_foo(lua::state* const);
lua::push(state, create_foo);

// Fundamental types work, too
int sum(int a, int b);
lua::push(state, sum);

// As are pointers to userdata and conversions to C++ strings.
void changeTitle(QWindow* window, const std::string& title);
lua::push(state, changeTitle);

// Even lambdas work too, with a bit of help
lua::push_function< int(int, int) >(state, [](int first, int second) {
    return first + second;
});

Beyond this, Lua-cxx also has out-of-the-box support for Qt's QObject
model, as well as rudimentary support for Gtk's GObject model, so you can
push these types and their properties and methods will automatically be
exposed to Lua. You can then specialize further on a specific subtype to
add behavior not provided by that library's metadata.

Here's a simple example of what can be done:

    // Create a new Lua environment to play with.
    auto env =3D lua::create();

    // Introduce a global into Lua
    env["foo"] =3D "No time";

    // Run some Lua code directly
    lua::run_string("assert(foo =3D=3D 'No time')");

    // Retrieve a global
    auto value =3D env["foo"].get<std::string>();

And here's another, more complex variant that shows how a Lua module for
Qt's QWindow class could be created:

    #include <luacxx/stack.hpp>
    #include <luacxx/type/standard.hpp>
    #include <luacxx/type/function.hpp>

    #include <QWindow>

    int QWindow_new(lua::state* const state)
    {
        if (lua_gettop(state) > 1) {
            auto parent =3D lua::get<QObject*>(state, 2);
            lua_settop(state, 0);

            if (parent) {
                if (parent->inherits("QWindow")) {
                    lua::make<QWindow>(state,
static_cast<QWindow*>(parent));
                } else {
                    lua::make<QWindow>(state,
static_cast<QScreen*>(parent));
                }
                return 1;
            }

            // Otherwise, fall through
        }

        // Create a QWindow within Lua
        lua::make<QWindow>(state, static_cast<QWindow*>(nullptr));
        return 1;
    }

    int luaopen_QWindow(lua::state* const state)
    {
        lua::thread env(state);

        env["QWindow"] =3D lua::value::table;
        env["QWindow"]["new"] =3D QWindow_new;

        return 1;
    }

    // Within Lua

    require "QWindow";

    local window =3D QWindow:new();
    window.width =3D 300;
    window.height =3D 300;
    window.title =3D "Hello, world";
    window:show();

I've tried to document it well and keep things tidy. The source in total is
almost 7,000 lines including documentation and unit tests. The
documentation for each file can be formatted for easier viewing using the
tiny bash script called cpod. It's in ./src. It just does a few regexes to
make the source look like perldoc and calls that, so you'll need that too.

Thanks for reading; I hope you take a look! The documentation in
src/stack.hpp or perhaps the unit tests in src/tests/core.cpp are good
first places to start.

https://github.com/dafrito/lua-cxx.git

-- Aaron

--=20
Aaron Faanes <[email protected]>

--001a11340c865a7e9b04fa7e86b0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div><div>Hello!<br><br></div>I would like to share Lua-cx=
x, a MIT-licensed project that I wrote that aids in writing modules and bin=
dings for Lua. I posted this to the Lua mailing list, but I figured those<b=
r>

who use Luabind would be interested to see another C++ take on Lua&#39;s AP=
I, so I decided to post here as well. :)<br><br></div>Compared to Luabind, =
Lua-cxx is lower-level. Lua-cxx does not introduce an object model for Lua,=
 nor does it use Luabind&#39;s policy model for arguments. It does use temp=
lates widely, including those features like variadic templates added in C++=
11, to provide a similar level of expressive power as Luabind.<br>

<br><div>Lua-cxx, like Luabind, doesn&#39;t provide a complete fa=C3=A7ade =
over Lua&#39;s C
 API. On the contrary, I find Lua&#39;s C API to be amazingly well-designed=
,
 so I&#39;ve=C2=A0tried to ensure that Lua-cxx can be intermixed freely wit=
h=20
Lua&#39;s C API. In fact, most of the Lua C API has no analog in Lua-cxx -=
=C2=A0I
 just use the original. ;)</div>
<div><br></div><div>=C2=A0=C2=A0=C2=A0 // Add all arguments<br>=C2=A0=C2=A0=
=C2=A0 int add_several(lua::state* const state)<br>=C2=A0=C2=A0=C2=A0 {</di=
v><div>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Get=C2=A0each argument=
<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 int sum =3D 0;<br>=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 for (int i =3D 1; i &lt;=3D lua_gettop(st=
ate); ++i) {<br>


=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 sum +=3D=
 lua::get&lt;int&gt;(state, i);<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 }<br></div><div><br></div><div>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 //=C2=A0Return the=C2=A0value<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 lua::push(state, sum);<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
 lua_replace(state, 1);<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 retur=
n 1;<br>=C2=A0=C2=A0=C2=A0 }</div>


<div><br></div><div>That being said, there are several places where=20
Lua-cxx greatly simplify common tasks. For instance, Lua has a number of
 lua_push* functions that can be replaced with Lua-cxx&#39;s=20
lua::push template and appropriate specializations. You can extend this=20
specialization with your own types, and Lua-cxx&#39;s other features will=
=20
immediately support them.</div>
<div><br></div><div>C++11 added variadic templates, which can be used to
 provide a way to push a function of any arity into Lua without needing=20
to write the marshalling code yourself or running a preprocessor:</div><div=
><br>
</div><div>// Standard C API is, of course, supported</div><div>int create_=
foo(lua::state* const);</div><div>lua::push(state, create_foo);</div><div><=
br></div><div>// Fundamental types work, too</div><div>int sum(int a, int b=
);</div>


<div>lua::push(state, sum);</div><div><br></div><div>// As are pointers to =
userdata and conversions to C++ strings.</div><div>void changeTitle(QWindow=
* window, const std::string&amp; title);</div><div>lua::push(state, changeT=
itle);</div>


<div><br></div><div>// Even lambdas work too, with a bit of help</div><div>=
lua::push_function&lt; int(int, int) &gt;(state, [](int first, int second) =
{</div><div>=C2=A0=C2=A0=C2=A0 return first + second;</div><div>});</div><d=
iv><br></div>


<div>Beyond this, Lua-cxx also has out-of-the-box support for Qt&#39;s=20
QObject model, as well as rudimentary support for Gtk&#39;s GObject model,=
=20
so you can push these types and their properties and methods=20
will automatically be exposed to Lua. You can then specialize further on
 a specific subtype to add behavior not provided by that library&#39;s=20
metadata.</div>
<div><br></div><div>Here&#39;s a simple example of what can be done:<br></d=
iv><div><div><br></div><div><div>=C2=A0=C2=A0=C2=A0 // Create a new Lua env=
ironment to play with.<br>=C2=A0=C2=A0=C2=A0 auto env =3D lua::create();</d=
iv><div><br></div><div>=C2=A0=C2=A0=C2=A0 // Introduce a global into Lua<br=
>


=C2=A0=C2=A0=C2=A0 env[&quot;foo&quot;] =3D &quot;No time&quot;;</div><div>=
<br></div><div>=C2=A0=C2=A0=C2=A0 // Run some Lua code directly<br>=C2=A0=
=C2=A0=C2=A0 lua::run_string(&quot;assert(foo =3D=3D &#39;No time&#39;)&quo=
t;);</div><div><br></div><div>=C2=A0=C2=A0=C2=A0 // Retrieve a global<br>


=C2=A0=C2=A0=C2=A0 auto value =3D env[&quot;foo&quot;].get&lt;std::string&g=
t;();<br></div></div><div><br></div><div>And here&#39;s another, more compl=
ex variant=C2=A0that shows how a Lua module for Qt&#39;s QWindow class coul=
d be created:</div>


<div><br></div><div>=C2=A0=C2=A0=C2=A0 #include &lt;luacxx/stack.hpp&gt;<br=
>=C2=A0=C2=A0=C2=A0 #include &lt;luacxx/type/standard.hpp&gt;<br>=C2=A0=C2=
=A0=C2=A0 #include &lt;luacxx/type/function.hpp&gt;<br><br>=C2=A0=C2=A0=C2=
=A0 #include &lt;QWindow&gt;<br><br>=C2=A0=C2=A0=C2=A0 int QWindow_new(lua:=
:state* const state)<br>


=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (lua_=
gettop(state) &gt; 1) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 auto parent =3D lua::get&lt;QObject*&gt;(state, 2);<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 lua_sett=
op(state, 0);<br><br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 if (parent) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (parent-&gt;inherits(&quot=
;QWindow&quot;)) {<br>


=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 lua::make&lt;QWindow&gt;(state, sta=
tic_cast&lt;QWindow*&gt;(parent));<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } else {<br>=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 lua::make&lt;QWindow&gt;(state, static_ca=
st&lt;QScreen*&gt;(parent));<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>


=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 return 1;<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0 }<br><br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 // Otherwise, fall through<br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 }<br><br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 // Create a QWindow within Lua<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 lua::make&lt;QWindow&gt;(state, static_cast&lt;QWindow*&gt;(nullptr)=
);<br>


=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return 1;<br>=C2=A0=C2=A0=C2=A0 =
}<br><br>=C2=A0=C2=A0=C2=A0 int luaopen_QWindow(lua::state* const state)<br=
>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 lua::th=
read env(state);<br><br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 env[&quo=
t;QWindow&quot;] =3D lua::value::table;<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 env[&quot;QWindow&quot;][&quot;new&quot;] =3D QWindow_new;<br>


<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return 1;<br>=C2=A0=C2=A0=C2=
=A0 }<br><br>=C2=A0=C2=A0=C2=A0 // Within Lua<br><br>=C2=A0=C2=A0=C2=A0 req=
uire &quot;QWindow&quot;;<br><br>=C2=A0=C2=A0=C2=A0 local window =3D QWindo=
w:new();<br>=C2=A0=C2=A0=C2=A0 window.width =3D 300;<br>=C2=A0=C2=A0=C2=A0 =
window.height =3D 300;<br>=C2=A0=C2=A0=C2=A0 window.title =3D &quot;Hello, =
world&quot;;<br>


=C2=A0=C2=A0=C2=A0 window:show();<br></div></div><div><br></div><div><div>I=
&#39;ve tried to
 document it well and keep things tidy. The source in total is almost=20
7,000 lines including documentation and unit tests. The documentation=20
for each file can be formatted for easier viewing using the tiny bash=20
script called cpod. It&#39;s in ./src. It just does a few regexes to make=
=20
the source look like perldoc and calls that, so you&#39;ll need that too.</=
div>
<div><br></div><div>Thanks for reading; I hope you take a look! The=20
documentation in src/stack.hpp or perhaps the unit tests in=20
src/tests/core.cpp are good first places to start.</div><div><br></div><div=
><a href=3D"https://github.com/dafrito/lua-cxx.git" target=3D"_blank">https=
://github.com/dafrito/lua-cxx.git</a></div>
</div><div><br></div><div>-- Aaron</div><br><div><div>-- <br><div>Aaron Faa=
nes &lt;<a href=3D"mailto:[email protected]" target=3D"_blank">dafrito@gmai=
l.com</a>&gt;
</div></div></div></div>

--001a11340c865a7e9b04fa7e86b0--


--===============1089521126207946948==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

------------------------------------------------------------------------------
Time is money. Stop wasting it! Get your web API in 5 minutes.
www.restlet.com/download
http://p.sf.net/sfu/restlet
--===============1089521126207946948==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
luabind-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/luabind-user

--===============1089521126207946948==--