Tuesday, May 21, 2019

Scripting language with class instance support


I have come across the need to use a scripting engine for my C++ game, but after experimenting with many languages since the last few days, nothing has truly stood out as the obvious choice for a language and/or binding library.



I would like have the ability to



  1. call methods of a specific Game instance from within the scripting language,

  2. call arbitrary functions in the scripts from C++, and

  3. test for function existence in the scripts using strings.


Lua looks nice, but I'm unsure of its ability to do 1 and 3. Python and Boost::Python are great, but the same applies. Regardless, here's an example of an ideal implementation.


C++


class Game
{

...
void printNumber(int arg);
};

int main( )
{
Game *game = new Game();

// Make method available to script
functionRegister("printNumber", game->printNumber);


// Test and call function in script
if ( functionExists("greet") )
functionCall("greet", "Hello world");
}

Script pseudocode


function greet( greeting )
print( greeting )
game.printNumber( 42 )

end

The output may be


Hello world
42

Is there a language and library that will pull this off?



Answer



I'm fairly sure Lua can do everything you need relatively simply. I use Lua and C++ in my game. I looked at various wrappers like LuaBind, or using a generator like Swig, but I decided I didn't want any of that stuff and I wrote my own wrapper which I ended up making open source in case other people found it useful.


Using my little library you can do stuff like this in C++:



// Wrap a C++ function
static int Widget_AddChild(lua_State* L)
{
// Typesafe way to extract userdata from Lua
Widget* parent = luaW_check(L, 1);
Widget* child = luaW_check(L, 2);
lua_pushboolean(L, parent->AddChild(child));
}

static luaL_reg Widget_Metatable[] =

{
// Register the function you wrote above
{ "AddChild", Widget_AddChild },

// Widgets also have some getter and setter functions,
// Using these templates you can automatically generate
// wrapper functions for them
{ "GetStyle", luaU_get },
{ "SetStyle", luaU_set },
{ "Style", luaU_getset },


{ NULL, NULL }
};

int luaopen_Widget(lua_State* L)
{
luaW_register(L, "Widget", NULL, Widget_Metatable);
return 1;
}


In lua you could now do something like this:


local w = Widget.new()
w.foo = 10 -- Foo is stored on w, it's not accessible to other instances
-- You can also add values accessible to all Widgets
function Widget.metatable:newfunction()
print(self:GetWidth())
end
w:newfunction()

With all that, you can fairly easily call C++ function from Lua (and vice versa, but I didn't illustrate that here). Once you know how to interact with the Lua API, writing a function to 'safely' call (i.e. call a function only if it exists) a Lua function should be easy as well.



Edit:


If you want to check for the existence of a function before calling it, like I said, just check that the value is a function before calling it.


lua_getglobal(L, "myFunc"); // You can get your function from anywhere, this is just for example
if (lua_isfunction(L, -1)) // Check the top of the stack, make sure it's a function
{
lua_call(L, 0, 0);
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...