I'm thinking about how to design my APIs, I plan to have a C++ application with a scripting layer in LUA .
For a couple of key points in my design I would like to give the ability to the user to create a function object that represents what he wants to do in LUA, than send this function object from LUA to C/C++ .
In pseudo code, in C++ I have a class T
class T {
...
int num1 = 0;
float num2 = 0.0f;
std::string str{"NONE"};
...
};
And I would like to manipulate an instance of T with a function object provided from LUA like so
void applyFunc(T t,F f){
f(t);
}
The problem is that I can't find nothing in LUA that creates a function object like a C++11 lambda or std::function or any other object that can be considered a function object.
Really my point is: how to define a C++ compatible function object in LUA ?
The problem is that I can't find nothing in LUA that creates a function object like a C++11 lambda or std::function or any other object that can be considered a function object.
That's what the function keyword does.
This is a lambda. It's easy enough to pass these to C++ and let the C++ code call them.
As for this:
void applyFunc(T t,F f){
f(t);
}
In principle it's easy: push the C++ object pointer onto the Lua stack as a userdata and call the Lua function. The problem is the Lua code can't do anything with a C++ pointer.
If you want the Lua code to be able to manipulate the object you pass it, you'll need to write manipulator methods in C++ and expose them to Lua. You usually do that by creating a metatable for the userdata.
There are libraries that this that automatically for C++. If you want to do it by hand (my preference), you should probably start here.
If you Google "C++ object Lua __index" that should net you numerous examples. I could code up an example later, but I'm at work at the moment.
Related
I'm not sure if my question makes any sense, but I certainly know it is near impossible to get the results from Google. Firstly, what I do not want. I don't want to call some function that prints "hello world" or adds two numbers together, I want to load a Lua script from a C++ program, but allow the script to modify variables of the C++ program, using functions. For example, imagine I have a C++ program like this:
class Foo
{
private:
int number = 0;
public:
void setNumber(const int& newNumber) {number = newNumber;}
}
int main()
{
Foo foo;
//Load and execute Lua script, with foo object
return 0;
}
How could I allow the Lua script to do foo.setNumber() (preferably without foo.)? This may be a very simple question, but as mentioned above, almost all information on Google when searching "Call C++ Function from Lua" assume there is no program, but just a .cpp/hpp file with some functions that you want to call.
I'm on Linux (Ubuntu), but the program needs to compile on all platforms (Windows and Mac)
This is asked here fairly regularly.
To roll your own binding you should:
Master Lua metatables completely.
Read the Programming in Lua stuff on the C API, particularly the part on classes. Alternatively you can read the manual, read the source (API headers especially), and do some googling, but the book will probably save you some time.
Broadly, you expose a C++ class instance to Lua by creating a Lua "userdata" containing a pointer to the class instance and passing this to the Lua script. A userdata is an opaque type; the Lua script can't actually do anything with it (other than pass it around) unless you give it a metatable. At the very least you must implement the __index metamethod on the userdata, which allows your C++ code to intercept attempts to index the userdata and return something meaningful, and the __gc metamethod, which allows your C++ code to delete the exposed C++ object when the corresponding Lua userdata is garbage collected.
For instance, you create a function called createFoo which creates a Foo instance, wraps the pointer as a userdata, applies a metatable implementing __index to it, and returns it to the Lua script.
When the user runs foo.setNumber, your C++ __index metamethod is called with the userdata and the string "setNumber". It's up to you what you return and this determines what foo.setNumber evaluates to in the Lua script. You want foo.setNumber to evaluate to a lua_CFunction which expects a Foo userdata as its first parameter, so that your class methods can be called idiomatically from Lua (i.e. foo:setNumber(12), which is syntax sugar for foo.setNumber(foo, 12)).
It's a very low level and manual process, and once you get the hang of it you're going to end up create a library/templates/macros whatever to do the boilerplate for you. At that point you may want to evaluate the myriad C++ binding libraries that exist. However, thanks to the Law of Leaky Abstractions it's a very good idea to learn to do this manually first.
I'm exporting some c++ classes to Lua with SWIG. I have declared boost::filesystem::path in the SWIG interface file like this:
namespace boost
{
namespace filesystem
{
class path {};
}
}
Now I want to call a function declared in a lua script which should take a boost::filesystem::path& as parameter to pass it to another object. I only need to be able to pass the path to the object. I don't need to use any functionality from the path object.
function on_path_selected(the_path)
another_object:set_path(the_path)
end
I am going to call the Lua function from c++ using it's index.
lua_rawgeti(L, LUA_REGISTRYINDEX, m_function_index);
lua_push[SOMETHING](L, path_object); // <-- HOW TO ?
lua_pcall(L,1,0,0)
THE QUESTION: How to push a boost::filesystem::path as a parameter to the Lua function?
This is actually fairly complicated. The expected use of SWIG is to create modules for Lua. The Lua script should be the one deciding what gets called and what doesn't. It isn't really meant for embedded use, where you use SWIG to expose some C++ objects and then call Lua code directly from your application.
That's not to say that it's impossible, just complicated.
All SWIG-based C++ objects are passed through Lua as pointers. Thus ownership is a question; you can't just shove a pointer to a stack object into Lua.
The safest way to do this is to pass a new copy of the object to Lua. That way, Lua owns the pointer. SWIG will know that Lua owns the pointer, and will attach a proper garbage collection mechanism to it to clean it up. So everything should be fine, memory wise.
But doing this requires properly "boxing" (for want of a better term) that object the way that SWIG wants it done. This requires using certain SWIG macros.
Given how you have bound the path type to SWIG, you would do something like this to stick it onto a Lua stack:
swig_type_info *pathType = SWIG_TypeQuery("boost::filesystem::path *");
boost::filesystem::path *pArg = new boost::filesystem::path(the_path);
SWIG_NewPointerObj(L, pArg, pathType, 1);
SWIG_TypeQuery fetches the type of any object that has been bound by SWIG to Lua. This type info object is needed for SWIG_NewPointerObj, which takes a pointer to that type. Both of these are macros. SWIG_NewPointerObj gives Lua ownership of the pointer; Lua's garbage collector will delete it thanks to SWIG's metatables. Also SWIG_NewPointerObj pushes the object onto the lua_State stack.
Once it's on the stack, you can pretty much do whatever you want with it. Return it from a function to Lua, pass it to a Lua function as an argument, stick it in a global variable, etc. It's a Lua value.
Now, if you type this code into your project, odds are good that you'll get a compile error when the compiler sees swig_type_info. This type is defined internally within the source file generated by SWIG's command-line.
You have two options:
Put this source code into the .swig file itself. Yes, really. You can define regular C++ functions there, within verbatum sections (the %{ %} delimited blocks). These functions will be copied directly into SWIG's generated code. You can access them by putting prototypes in headers. This is the simplest and easiest way to work. This is often used for creating special interfaces, where a pre-existing C++ function isn't appropriate for a Lua API (or simply doesn't exist).
You can generate an appropriate header that contains these definitions with the -external-runtime argument. This has to be a different SWIG execution step from the step that generates the .cpp file. See, it doesn't actually process the SWIG file or anything. All it needs is the target language (-lua) and whether you're using C++ (-c++).So just have a command that does swig -c++ -lua -external-runtime someheader.h, and that's all you need to get the types and macros.
Include that header in whatever source you want to attach SWIG-bound objects to Lua in.
(sidenote: This is game programming)
Binding entire classes to Lua using LuaBind is easy:
class test
{
test()
{
std::cout<<"constructed!"<<std::endl;
}
void print()
{
std::cout<<"works!"<<std::endl;
}
}
//somewhere else
module[some_lua_state]
[
class_<test>("test")
.def(constructor<>())
.def("print",&test::print)
];
Now I can create instances of the class in Lua and use it:
lua_example.lua
foo = test() //will print "constructed!" on the console
foo:print() //will print "works!" on the console
However, now I'd like to bind a specific instance of test to Lua. This would enable me to pass objects down to Lua, e.g. an instance of the Player-class and do something like:
Player:SetPosition(200,300)
As opposed to going the hard way and having something like
SetPosition("Player",200,300)
where the corresponding C++ SetPosition function needs to look up a std::map to find the player.
Is this even possible and if so, how can I do it in LuaBind?
You don't bind instances of classes to Lua. Instances of classes are just data, and you can pass data to Lua via the usual means. C++ objects are special though; because they're registered through Luabind, you have to use Luabind methods to provide them to Lua scripts.
There are several ways to give data to Lua, and Luabind covers all of them. For example, if you have an object x, which is a pointer to class X that is registered with Luabind, there are several ways you can give Lua x.
You could set the value of x into a global variable. This is done through Luabind's object interface and the globals function:
luabind::globals(L)["NameOfVariable"] = x;
Obviously, you can put that in another table, which lives in another table, etc, which is accessible from the global state. But you'll need to make sure that the tables all exist.
Another way to pass this data to Lua is to call a Lua function and pass it as a parameter. You can use the luabind::object interface to get a function as an object, then use luabind::call_function to call it. You can then just pass x as a parameter to the function. Alternatively, if you prefer the lua_pcall-style syntax, you can wrap x in a luabind::object and push it into the stack with luabind::object::push.
I'm working on a little simulation project which uses Lua to drive the behavior of individual units (ants) and using Luabind to glue the C++ and Lua sides together. Each individual ant (there are different types, derived from the base class Ant) has a Run() function, which calls the appropriate script; the script then carries out whatever actions need to be taken, calling the exposed class functions and possibly free functions. I've gotten the Run function (in C++) to successfully execute the matching Run function in the Lua script (which just prints some text at the moment).
void AntQueen::Run()
{
lua->GetObject("QueenRun")(GetID());
}
lua is just a manager class which retrieves the function from the script. The above is calling the following in a Lua file:
function QueenRun(ID)
print("The Queen is running!")
print(ID)
end
And Luabind registration looks like this for the AntQueen class:
void Register(lua_State *luaState)
{
using namespace luabind;
module(luaState)
[
class_<AntQueen, Ant>("AntQueen")
.def("Eat", &AntQueen::Eat)
.def("ExtractLarvae", &AntQueen::ExtractLarvae)
.def("GetMaxLarvaeProduced", &AntQueen::GetMaxLarvaeProduced)
.def("GetNumAvailLarvae", &AntQueen::GetNumAvailLarvae)
];
}
The way it's set up now, ants are created, removed, and found through a simple factory/manager. Each ant can be retrieved by calling static Ant* AntFactory::GetAntByID(const int ID) which just finds the ant in a hash map and returns a pointer to the ant. What I'm trying to do is get Lua to be able to do something like the following:
function QueenRun(ID)
ant = GetAntByID(ID)
larvae = ant:GetNumAvailLarvae()
print(larvae)
ant:Eat()
end
The above is just a made up example, but hopefully it shows what I'm trying to achieve. I don't want Lua to garbage collect the objects, because they are managed already on the C++ side. While testing everything out, any attempt to do the following:
ant = GetAntByID(ID)
in Lua resulted in abort() being called and the program crashing and burning.
R6010
-abort() has been called
I just seem to be missing something with how everything gets shuttled back and forth (this is my first foray into gluing Lua and C++ together beyond toy programs). I'm pretty sure passing a plain pointer isn't the way to do it; lightuserdata seems to be what I'm looking for, but it also has a bunch of restrictions.
So to sum up: What is going on here that causes abort to be called and how can I use Luabind/the Lua C API to get a pointer to a C++ object passed to Lua and call member functions on that pointer as if it were an object (without letting Lua garbage collect it)?
The solution to this problem seemed to be tied to the AntFactory class/member functions being static. As soon as I switched from registering and using this:
//C++
static int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
to an instantiated object and regular member functions like this:
//C++
int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
everything worked with no problems at all (after also pushing the factory to the global table). I think the reason for this is that trying to call static member functions on a non-instantiated C++ object fails due to Lua (or Luabind?) not being able to have an object to query for calls.
I have a little problem with the WOsclib. Not particularly with the library, it's more the callback function. The listen to specific osc commands i have to put up some callback method like
void TheOscStartMethod::Method(
const WOscMessage *message,
const WOscTimeTag& when,
const TheNetReturnAddress* networkReturnAddress)
{
std::cout << "Got the start signal";
start.alpha = 1.0;
}
start is IBOutlet UIImageView.
But the compiler says me, that start is out of scope. If I try to access start in obj-c code, it works like it should.
How can i get my Objective C Objects into the c code or at least call a objective-c function.
Thank you
Make the file an objective C++ file with extension .mm Then you can call object C and C++ objects in the same code.
XCode will call the correct compiler from the file extension (ie adding -x objective-c++ to the compile command)
Not that C++ and objective C are different languages and do not understand each others objects so to move data between them you will need to convert the data to a C type e.g. void, char int and pointers to them.
It sounds like start is an instance variable belonging to some Objective-C object and you're trying to access it just by writing its name from a C++ object. If this is the case, it should be pretty obvious why it won't work: The C++ object doesn't know anything about start. The solution is to somehow give the C++ object a reference to the Objective-C object that owns start.
You'll have to make the start object available to your other code.
You can pass it, you can pass the portions you'll be using, you can create an API for the two code bases to use. There are other options as well, all depending on precisely how you wish to use the various objects
The Solution:
I don't know if this is the best way to do it, but it works.
There must be an empty c object, which later will become our objective c object that holds all the stuff we want to access.
static gsSearchForIp* delegate = NULL;
We must define a function to set the objective c object
void setCallbackDelegate(gsSearchForIp* del)
{
delegate = del;
}
And then call it. ( I called it in the initWithFrame method)
setCallbackDelegate(self);
Now i can call a method with [delegate methodName:firstPara] in my c++ method. In this function i have access to all my stuff that I need from the gsSearchForIp class.