lua-users home
lua-l archive

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


Oliver Schoenborn wrote:
Hello, I'm somewhat unhappy with the existing bindings to C++. The major ones

   * lua++: clean and simple, but requires custom lua
   * cpplua: some good ideas, but some less good, and doesn't seem to
     be maintained
   * luabind: most powerful, but requires boost, and syntax a little
     unnatural (IMO)
   * swig and tolua++: don't allow for variable argument lists (AFAICT)
     or for logging callbacks and gets/sets

I have had to use basic lua binding to C for on-the-job projects, and I'm thinking that because of the above, perhaps yet another C++ alternative that has the following characteristics might fill a need:

   * no dependency on boost
   * usable with any lua (esp core dist)
   * combines the best of cpplua, lua++ and luabind, without
     necessarily going as far as luabind, remains usable if some parts
     are dealt with swig
   * basically hides all notion of stack (user wouldn't have to know
     there is one)
   * support logging of callbacks and sets/gets


Any comments on the above would be greatly appreciated (e.g., maybe such a binding already exists?).

The idea is to host it on SourceForge (e.g.), it would be great to have lua users involved (with more experience that I do, e.g. on stack use and patterns of communication between C++ and lua interpreter).

Examples of use:

void func(const LuaArgs& args) {
   if (args.size() < 1)
   {
       LuaLog(err, "error message");
       return; // automatically causes return 0 to Lua
   }
   if (args[2]) // do something with args[2]
      ...
   luaLog(trace, "func() called");

   LuaObject a("a"); // will be "nil" if doesn't exist
   int aa = a; // auto convert to int, or throw if can't
   int bb = (a.nil() ? 0 : a); // won't throw, don't care if error
   std::string sa = a; // converts to string? or throws?

   // automatically pushes appropriate lua types onto stack
   // and handles return value count
   luaReturn("aString", aFloat);
}

int main()
{
   LuaInterpreter lua;
   lua.newLog(trace, stuff); // use lua code to implement this?
   lua.newTable("ns");
   lua.register("ns.func", func); // will be wrapped so LuaReturn works
   LuaObject a = lua.newValue("a", 1); // let C++ compiler determine type

   lua.doString("ns.func(2)");
   lua.doFile("file.lua"); // may call ns.func()
}

The log functionality needs a fair bit of thinking but want it to be simple to use.

You might want to see what I did in fusion, which is a piece of Emma (http://emma3d.org). It is much simpler than the other solutions out there. For instance, to make a C++ object that is accessible to Lua you go:

    class MyClass : public Fusion::Object {
        ...
        DECLARE_CLASS(MyClass, Object);
    }

To add a prototype for this class of object you simply go:

    engine->setProperty("MyClass", MyClass::newClassObject());

where engine is an instance of the Fusion::Engine class, essentially a wrapper around the Lua interpreter.

Every class has an initializeClass() method which is used to expose things to Lua. For instance, if your class has a member function like this:

    double toNumber(const String& s) { return (double) s; }

you would add the following to initializeClass:

    registerFunction("toNumber", &MyClass::toNumber);

In Lua you would go:

    local c = MyClass()
    local n = c:toNumber("12345")

Fusion takes care of getting values from the stack, type conversion and pushing the result onto the stack. It does all this through the use of static template functions to "thunk" between Lua and C++. ALl this is hidden from the C++ programmer. The above is all that is needed.

Fusion also has a couple of classes which wrap the Lua stack. The Args object is merely a wrapper around the stack itself. And Value is a wrapper around values in the stack. You can do things like:

    int someFunc(lua_State* L)
    {
        // stack: self
        Args args(L);
        args.push("to");
        args.push("Number");
        args.concat(2);
        args.pushTable(1);
        args.push(12.7);
        args.call(1,1);
        Value val = args[-1];
        args.pop();
        double d = Value.getNumber();
        return 0;
    }

Or the Engine and Object classes can hide the stack entirely. You can go (assuming 'obj' is an object of type MyClass):

    Value retval;
    obj->callProperty("toNumber", retval, "12345");
    double d = retval.getNumber();

It is a nice little package which links to a dll of around 176K on win32. It has no dependencies other than Lua and is designed for Lua 5.1.

When I get the chance I will release this as a package and do better docs. But please use or leverage it as you see fit...

--
chris marrin                ,""$,
chris@marrin.com          b`    $                             ,,.
                        mP     b'                            , 1$'
        ,.`           ,b`    ,`                              :$$'
     ,|`             mP    ,`                                       ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`
"As a general rule,don't solve puzzles that open portals to Hell"'