lua-users home
lua-l archive

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


Sam Roberts wrote:
On Fri, Nov 17, 2006 at 11:09:39AM -0800, Don Hopkins wrote:
it doesn't require that your code be written in C++, and it doesn't

YMMV. I tried to use swig to bind a C library into ruby for a few days,
and came away pretty frustrated.
I haven't used the Ruby back-end. I hope someone improves it.

SWIG has excellent support for Python, which has a single well defined object system.
Have you tried using SWIG's "shadow classes" with Python?
It's an option you can turn on and off, but I think it's on by default.
SWIG writes some Python source code into a module that defines Python shadow classes with methods bound to the low level ClassName_MethodName functions, which you can use as normal classes and subclass properly from Python.

Not all SWIG back-ends support shadow classes -- it's an optional feature depends on the language's class system. Since Lua has a "roll your own" class system, there's not a lot SWIG can assume about how you want objects to work, so SWIG's Lua back-end doesn't currently support that feature, nor does it generate any Lua wrapper code. But it could!

I've made some modifications and extensions to SWIG, to support my own quirky object system (which may or may not be how you want to do objects). I took a good look at tolua++, which is great Lua-savvy code, to pick up some useful tricks: it uses lua_setfenv to attach a Lua "peer object" to the userdata's environment. I modified some of the SWIG runtime templates and made some typemaps to support wrapping C++ objects with Lua "peer objects" that additionally inherit from their own Lua classes, and easily call back into user defined Lua objects from C++. I posted the code to this list a couple of months ago, I've been using it, and I'm pretty happy with how it works. I might have fixed some bugs since then, so just ask and I'll send you a fresh copy of my changes.

http://lua-users.org/lists/lua-l/2006-09/msg00562.html

Since everybody wants to roll their own object system in Lua, it would cool to parameterize the SWIG Lua runtime templates so you can hook in glue for any object system you want. But for now I just hacked my own in!

The impression I got was that swig was very good at binding OO C++ into
OO languages (I guess because c++ has enough compiler keywords that make
it clear what is an object, and what the ctor/dtor is, etc.), but what I
had was fairly typical "OO in C" libraries:
typdef struct Foo Foo;
  int foo_new(Foo** foo, int arg);
  int foo_use(Foo* foo, const char*in, size_t in_sz, char* out, size_t* out_sz);
    # non-zero return is an error
  void foo_destroy(Foo** foo);

I thought it would be easy to bind, because the library, while having a
lot of APIs, had a very, very consistent set of calling conventions, but
I had trouble finding documentation on this. There was lots of doc
coverage of how to do cool things with C++ and templates, but the simple
case of C libraries was surprisingly under discussed.

Anyhow, I think I could do great things with swig if I knew it well, but
the learning curve was pretty steep at the time. I hand-coded the
bindings.

Sam
Yes, you're right that SWIG is good for heavy duty C++ and best for OO languages, and it supports some languages better than others, and the documentation is mediocre. But it's quite powerful and ambitious: SWIG knows all about the C++ type system and even templates, and it's based on pattern matching of C++ types and function signatures. Thanks to the massive complexity of C++, SWIG's learning curve is pretty steep, and it keeps on going a long way up as it articulates C++'s byzantine fractal surface area. It's nice to know all the C++ stuff is there and it does the right thing when you need it. It even has typemap libraries that support most of the standard library templates (YYMV depending on the language, of course).

It's possible to use SWIG for C stuff (like the Ming library interface) and simple C++ stuff without worrying about all the advanced C++ stuff (like Gnu Radio's signal processing module C++ template wrappers). You can also wrap a low level C library in a simple "SWIG friendly" C++ shell with error checking and bullet proofing so it won't crash or let you do anything illegal from the scripting language.

I've found an easy way to plug a non-object-oriented C library into an object oriented language is to write a thin C++ class around it, and SWIG-ify that C++ class, instead of the raw C library. Then SWIG will generate shadow classes (if the back-end supports it) that you can subclass to add new behavior, and use custom methods and properties in the peer object. Sometimes the library's interface is too low level to be used conveniently from a scripting language (cough cough Win32 cough X11 gag). For example, I'm using Cepstral's "Swift" text to speech C library, and I've written some C++ classes to wrap and simplify the C interface, which call back into Lua with events during speech synthesis (phonemes to animate a mouth, bookmarks, etc).

If you expose a bunch of superfluous symbols and useless functions, it makes your program bigger by writing all those extra wrappers. So make an "appliance shell" to reduce the API's surface area to just what's needed. Instead of defining zillions of enumerated symbols, just pass the symbol names as strings, or pass in structured Lua tables and objects (if you're willing to write language-specific code).

You can write language-specific typemaps and extension methods that convert between C++ and native data types (like making structured Lua tables and manipulating Lua objects). Use typemaps to automatically convert between C++ types and scripting language types. Use %extend to extend the wrapper classes with script-friendly utility and convenience functions.

I like to use %extend to add extra "helper functions" into the wrappers that don't exist in the actual C++ objects, that you can call from the scripting language. That is really useful when you're trying to make a script-friendly version of some nasty C++ object you got from somewhere whose api you don't have control over.

   -Don