C++11 lua wrapper

Everything todo with programming goes HERE.
Post Reply
User avatar
voodoo
Core Dumper
Posts: 124
Joined: Wed Sep 27, 2006 11:51 am

C++11 lua wrapper

Post by voodoo »

Before I lost it and in case it is of some interest to some1, I've pushed a lua wrapper I've written to learn C++11 and lua API... It is working fine on OSX/Xcode and on ubuntu with gnu compiler version 4.6. hmmm 3 IIRC.

here it is

Just to give some input about how it works:
There a first set of files to provide basic non OOP interface to push/get data from lua stack in a C++ polymorphic way (files luapp11_base.* + included).
There's another file (luapp11_wrappers.hpp defining wrappers and functions to register C/C++ functions/objects to lua.
Finally, there's a few C++ objects to provide a OOP interface to lua stack.

file main.cpp in the unit test directory provides some examples. Here are some basic cases:

Code: Select all

     // Start a new MainState and run a script
    // It will be closed automatically when L goes out of scope.
    main_state L;
    L->run("print('A message from lua.')");
    
    // Set/get global variables for pod types
    // Note that getter can throw in case of type mismatch
    L->set_global("i", 3);
    L->set_global("d", .14159);
    L->run("pi=i+d print('In lua: pi=',i+d)");
    double pi = L->get_global<double>("pi");
    L->get_global("pi", pi);
    std::cout << "In C++: pi= " << pi << std::endl;
    
    // How to shot yourself in the foot (take care of pointers and references!)
    // strings can be set/get as const char* or as std::string
    L->set_global<const char*>("a_string", "Everything looks fine!");
    auto str1 = L->get_global<std::string>("a_string");   // safe
    std::cout << str1 << " And it is fine! @" << (void*)str1.c_str() << std::endl;
    auto str2 = L->get_global<const char*>("a_string");   // undefined behavior
    std::cout << str2 << " But it's not! @" << (void*)str2 << std::endl;
    std::cout << "This last pointer is owned by lua and can be garbaged collected while you're still using it.\n";
    std::cout << "Make a string copy or use std::string instead of const char * when retrieving strings from lua.\n";

    // Set function and closure
    // Basic lua_CFunction (taken from lua API programming guide from lua website)
    L->set_global("l_sin", &l_sin); // l_sin is a lua_CFunction to compute sinus. Prototype: static int l_sin (lua_State *L);
    L->run("print('l_sin: ', l_sin(3.14159))");
    // CClosure ie lua_CFunction with up values
    L->set_global("counter", &counter, 0);
    L->run("print('counter 1st call: ', counter())"); // print 1
    L->run("print('        2nd call: ', counter())");    // print 2
    L->run("print('        3rd call: ', counter())");    // print 3
    // Random functions work too, it builds automatically an implicit wrapper around it.
    // We'll see wrappers later on
    L->set_global("add", &add);
    L->run("print('add(3,.14159)=', add(3,.14159))");
    // Finally, lambdas work too, using a special set_global_closure member function
    L->set_global_closure("add_lambda", [](double l, double r)->double {return l+r;});
    L->run("print('add_lambda(1,.618)=', add_lambda(1,.618))");
    
    // callbacks
    // Note that std::tuple is used below to return multiple values.
    L->run("function myadd(a, b) return a+b end\n");
    L->run("function multiple(s, x) return '2*'..s, 2*x end\n");
    callable<float(float, float)> callback1((reference(L, "myadd")));
    std::cout << "myadd(3,.14159)=" << callback1.call(3, .14159) << std::endl;
    callable<std::tuple<std::string, float>(std::string, float)> callback2((reference(L, "multiple")));
    std::cout << "tuple returns: " << callback2.call("PI", 3.14159) << std::endl;
    callable<float(float, float)> callback3(L,"function(a, b) return a+b end");
    std::cout << "Anonymous function: " << callback3.call(1.61803, 3.14159) << std::endl;
Registrating C++ objects is easy. Inheritance is supported.

Code: Select all

class A {
    std::string value_a;
public:
    A(std::string val): value_a(val) {}
    ~A() {}
    
    std::string getA() {return value_a;}
};

class B {
    std::string value_b;
public:
    B(std::string val): value_b(val) {}
    ~B() {}
    
    std::string getB() {return value_b;}
};

class C: public A, public B {
    std::string value_c;
public:
    C(std::string val_a, std::string val_b, std::string val_c):A(val_a), B(val_b), value_c(val_c) {}
    
    std::string getC() {return value_c;}
};

std::string test_derived(B& b) {
    return b.getB();
}

.../...

    // Registration and Inheritance with references and pointers
    Class<A>("A", Construct<std::string>())
    .Def("get_a", &A::getA)
    .Register(L);
    Class<B>("B", Construct<std::string>())
    .Def("get_b", &B::getB)
    .Register(L);
    Class<C, Bases<A, B>>("C", Construct<std::string, std::string, std::string>())
    .Def("get_c", &C::getC)
    .Register(L);
    L->set_global("test_derived_b", test_derived);
    L->run("a=A('A obj')");
    L->run("b=B('B obj')");
    L->run("c=C('A in a C obj','B in a C obj','C in a C obj')");
    L->run("print(a:get_a(), b:get_b())");
    L->run("print(c:get_a(), c:get_b(), c:get_c())");
    L->run("print(test_derived_b(a))"); // LUA ERROR: <<Type mismatch: expecting a B but got a A instead!>> 
    L->run("print(test_derived_b(b))"); // B obj
    L->run("print(test_derived_b(c))"); // B in a C obj
There's support for std::tuple and C++11 smart pointers (shared_ptr, weak_ptr, unique_ptr).
There's also some support for sandboxing but this might requires some refactoring.
Const support might be messy. There's no const in lua so I've totally ignored it so far...

It's not fully polished (far from it), I might try to finish it once I had time and motivation again :p

Any comment welcome anyway.
Last edited by voodoo on Sat Sep 28, 2013 9:58 pm, edited 1 time in total.
User avatar
delinquent
Match Winner
Posts: 760
Joined: Sat Jul 07, 2012 3:07 am

Re: C++11 lua wrapper

Post by delinquent »

Might be useful to have an option of re-inputting data, in the case of a mismatch. I know jack all about Lua though.
User avatar
voodoo
Core Dumper
Posts: 124
Joined: Wed Sep 27, 2006 11:51 am

Re: C++11 lua wrapper

Post by voodoo »

Sorry, might be my limited english skills but I failed to see what you mean. Can you elaborate ?
User avatar
delinquent
Match Winner
Posts: 760
Joined: Sat Jul 07, 2012 3:07 am

Re: C++11 lua wrapper

Post by delinquent »

Code: Select all

  L->run("print(test_derived_b(a))"); // LUA ERROR: <<Type mismatch: expecting a B but got a A instead!>>
Here, it could be interesting to try substituting A with B, just to see what result would return.

perhaps:

Code: Select all

  line = io.read()     -- read a line
    n = tonumber(line)   -- try to convert it to a number
    if n == nil then
      error(line .. " is not a valid number")
    else
      print(n*2)
    
Making A and B appear to be the same number.


That's just using string function, I don't know if it work in reality. Like I said, my programming skills leave much to be desired.
User avatar
Z-Man
God & Project Admin
Posts: 11585
Joined: Sun Jan 23, 2005 6:01 pm
Location: Cologne
Contact:

Re: C++11 lua wrapper

Post by Z-Man »

What you see above is essentially a test run of such a program complete with output as comments. It is meant to demonstrate that you need to expect an error at that point.

voodoo: Nice. Always love projects like this.
User avatar
voodoo
Core Dumper
Posts: 124
Joined: Wed Sep 27, 2006 11:51 am

Re: C++11 lua wrapper

Post by voodoo »

@Deliquent: The line you've quoted is essentially to illustrate how lua will appropriately return an error message for incorrect type but will work fine with the right class or any derived one. Basically, this behavior can be changed within C++ by re registering a class with new definition. Note that there's more examples within the unit test part but they might be as useless as examples above to help some1 understand how the whole thing work... It makes heavy usage of variadic template to be as flexible and simple as possible but that makes it also hard to read.

Thanks anyway. I'll appreciate any feedback...
Post Reply