Click here to Skip to main content
Click here to Skip to main content

LuaDotNet: a thin wrapper around Lua and Luabind for .NET

By , 7 Aug 2003
 

Sample Image - luanetwrapper.gif

Introduction

This article presents a thin wrapper around Lua, see [1], and Luabind, see [2], for .NET: with it you can embed a scripting engine with a C, C++ backbone into your .NET applications.

If you are not familiar with Lua and Luabind, a small introductory example is also given below.

Lua, an embeddable C scripting language

Here are some quotes extracted from the Lua "about" page:

"Lua is a powerful light-weight programming language designed for extending C, C++ applications."
"Lua is a language engine that you can embed into your application. This means that, besides syntax and semantics, Lua has an API that allows the application to exchange data with Lua programs and also to extend Lua with C functions. In this sense, Lua can be regarded as a language framework for building domain-specific languages."

Usually, using Lua in an application is done with the following steps:

  1. Create a Lua state:
  2. Bind methods to Lua
  3. Execute Lua scripts
  4. Clean and deallocate state

Create a Lua state

"The Lua library is fully reentrant: it has no global variables. The whole state of the Lua interpreter (global variables, stack, etc.) is stored in a dynamically allocated structure of type lua_State. A pointer to this state must be passed as the first argument to every function in the library, except to lua_open, which creates a Lua state from scratch."

As mentioned above, lua_open is used to allocate a Lua state:

lua_State* L = lua_open();

Bind methods to Lua

Suppose that the following method need to bound:

void my_print( const char* str )
{
    printf( str );
}

First, we need to make a function wrapper for my_print that receives a Lua state and returns an integer, the number of values it wants to return to Lua:

int my_print_lua(lua_State *L)
{
   /* get the number of arguments (n) and
    check that the stack contains at least 1*/
   int n = lua_gettop(L);
   if (n<1)
   {
       lua_pushstring(L, "not enough arguments");
       lua_error(L);
   }

   /* try to cast argument to string and call my_print,
    - remember that Lua is dynamically typed
    - we take the first element in the stack
   */
   my_print( lua_checkedstring( L, 1) );

   /* my_print does not return values */   
   return 0;
};

At last, the method is registered in Lua using:

lua_register(L, "pr", my_print_lua);

Remark: Handling the Lua stack can become tedious and error-prone. Hopefully, Luabind is here to simplify (a lot) the wrapping task.

Executing Lua scripts

Consider the following script:

s = 'this is a test';
pr( s );

As one can see, Lua syntax is quite straightforward. In the script, we see that the method we bound (pr) is called. To execute this script in C, lua_dostring is used:

const char* str = "s = 'this is a test';pr( s );";
lua_dostring(L, str);

The program will output:

this is a test

Clean and deallocate state

When you are finished, do not forget to call lua_close to deallocate the Lua state:

lua_close(L);

Luabind, simplifying things

As mentioned before, handling the Lua state is a tedious work that we would like to avoid. Luabind uses template meta-programming to simplify things for us.

Using Luabind, the previous steps become:

  1. Create a state:
    using namespace luabind; // luabind namespace
    lua_State* L =lua_open(L);
    luabind::open(L); // init luabind
  2. Bind method:
    module(L)
    [
        def("pr", &my_print);
    ];

Remarks:

  • We did need to write my_print_lua wrapper, Luabind does this for us.
  • Luabind can also bind classes.
  • Check the Luabind page for more information as their documentation is really well done.

LuaNET, the wrapper

So what about executing Lua script in .NET applications? This should not be a major problem, just the matter of writing a managed C++ wrapper.

Remarks:

  • A detailed class documentation can be built by running the Documentation tool on the LuaNET project.
  • All the classes live in the Lua namespace.

State, wrapping lua_State

The managed class State wraps up the lua_State structure. It handles the calls to lua_open and lua_close.

Here's a small example that creates a state, sets some variables and executes a script:

  • C#:
    Lua.State L = new Lua.State(); // creating a lua state
    L.set_Global("a",1);          // equivalent to a=1 in Lua
    L.DoString("b=a * 2;")        // executing a Lua script
    Double b = L.get_Global("b").ToDouble(); // retreive the value of b
    

Note that get_Global returns a LuaObject which is then cast to a double using ToDouble.

LuaObject, wrapping luabind::object

LuaObject enables you to set and retrieve values from Lua. Supported values are:

  • string
  • double, int
  • bool
  • table

Since Lua is dynamically typed, you need to check the type of the LuaObject before casting it. Otherwise, if cast fails, exceptions will be raised:

L.DoString("s='string';");
L.DoString("print('s: ' .. tostring(s));");

LuaObject s=state.get_Global("s");
s.ToString(); // ok
s.ToDouble(); // fails, s is a string

If the object is a table, LuaObject implements the IDictionnary interface on the table and TableEnumerator implements the IDictionnaryEnumerator interface:

L.DoString("t={1,'string'};");
LuaObject t=L.get_Global("t");

System.Collections.IDictionaryEnumerator te = t.GetEnumerator();
while( te.MoveNext() )
{
    System.Console.WriteLine("t...(" + te.Key + "," + te.Value +")");
};

Loading libraries

Lua comes with a set of default APIs to handle strings, table, IO, files etc...To load these APIs, use DefaultLibrary.Load:

Lua.Libraries.DefaultLibrary.Load( L );

Luabind is loaded using LuabindLibrary.Load:

Lua.Libraries.LuabindLibrary.Load( L );

You can wrap up your API and load them in .NET using the same method as DefaultLibrary or LuabindLibrary.

Using the demo

The demo features Lua 5.0 and Luabind. You need to recompile all the projects and launch LuaNetTest.

History

  • 8/8/2004: Initial release

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Jonathan de Halleux
Engineer
United States United States
Member
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralFinally got it to compile, but...memberjdunlap2 Oct '03 - 13:37 
We finally got it to compile, after a bunch of modifications, and about 5 hrs of work. (It comes to about 2.8MB, not 20MB, BTW.)
 
Anyhow, I have a problem: how do I expose my .NET classes to Lua? Is it even possible?
 

"Blessed are the peacemakers, for they shall be called sons of God." - Jesus

"You must be the change you wish to see in the world." - Mahatma Gandhi






GeneralRe: Finally got it to compile, but...memberJonathan de Halleux2 Oct '03 - 21:27 
jdunlap wrote:
how do I expose my .NET classes to Lua? Is it even possible?
 
At the present moment, no! But I think lua team is working on some merging project. Anyhow, I've discovred Reflection and in fact, .NET can already be scripted using the CodeDOM.

 
Jonathan de Halleux.

GeneralRe: Finally got it to compile, but...memberjdunlap2 Oct '03 - 21:51 
Thanks for your reply. Smile | :)
 

Jonathan de Halleux wrote:
At the present moment, no!
 
That's what I was afraid of - it didn't look like there was a way. Frown | :(
 

Jonathan de Halleux wrote:
I've discovred Reflection and in fact, .NET can already be scripted using the CodeDOM.
 
Yeah, there's .NET script - VB and JScript. But the problem is: we're working on a project in both C++ and .NET, and would like to be able to keep the scripting languages the same if possible.
 
I've thought about using Windows Script, but I have yet to find out how to disable "unsafe" scripting - it's crucial that the script not be able to access objects that could pose a security breach. (For the script control, there's the UseSafeSubset property, but I've yet to find an equivalent setting for the script interfaces.)
 
I've also thought about bypassing LuaBind and doing a direct .NET binding, but I'm really not experienced in creating wrappers.
 
Sigh | :sigh:
 
Thanks for your help!
 

"Blessed are the peacemakers, for they shall be called sons of God." - Jesus

"You must be the change you wish to see in the world." - Mahatma Gandhi






QuestionCould You Please Post the Binaries?memberjdunlap19 Sep '03 - 20:12 
My team is having trouble compiling the binding, even though we have tried it on 3 different machines.
 
Thanks!
 

"Blessed are the peacemakers, for they shall be called sons of God." - Jesus

"You must be the change you wish to see in the world." - Mahatma Gandhi






AnswerRe: Could You Please Post the Binaries?memberkrigh20 Sep '03 - 0:44 
i´ve got also troubles compiling.. so binaries would be useful..
GeneralRe: Could You Please Post the Binaries?memberJonathan de Halleux28 Sep '03 - 21:47 
I'm sorry but I can't !
 
When it gets compiled, luabind produces .lib of 20Mb, which I can't put on CP.
 
What compilation problems do you have ?
Do you really need lua ? You could also use Reflection ?
 
Jonathan de Halleux.

GeneralMissing Whole Directory!memberjdunlap17 Sep '03 - 12:04 
When I try to compile the wrapper, I get the message:
 
luabind\config.hpp(27) : fatal error C1083: Cannot open include file: 'boost/config.hpp': No such file or directory
 
This is caused by the line:
 
#include <boost/config.hpp>
 
If I comment that out, I get more errors from missing files that should be in that dir. But that dir doesn't even exist.
 
Could you help us here?
 

"Blessed are the peacemakers, for they shall be called sons of God." - Jesus

"You must be the change you wish to see in the world." - Mahatma Gandhi






GeneralRe: Missing Whole Directory!sussAnonymous17 Sep '03 - 12:10 
You need to download boost at http://www.boost.org[^]
 

GeneralRe: Missing Whole Directory!memberjdunlap17 Sep '03 - 12:39 
Thanks! Rose | [Rose]
 

"Blessed are the peacemakers, for they shall be called sons of God." - Jesus

"You must be the change you wish to see in the world." - Mahatma Gandhi






GeneralRe: Missing Whole Directory!memberThe_Mega_ZZTer16 Nov '06 - 7:43 
Doesn't work. Your solution references files (boost/mpl/apply_if.hpp) that aren't included in the latest boost download.
 
Please either include pre-compiled DLLs or a COMPLETE source code download needed to build the project without having to play hide and go seek with 2-3 different library packages.
 
Fortunately it appears Boost is only needed for luabind. I tried to compile the other pieces, but I ended up with about 84 syntax errors in LuaNET.
 
This is quite frustrating.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 8 Aug 2003
Article Copyright 2003 by Jonathan de Halleux
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid