2
\$\begingroup\$

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?

\$\endgroup\$
5
  • \$\begingroup\$ Could you explain 3. a bit more? I am pretty sure Lua is able to do 1 but I am not sure I am understanding what the last one would do to venture a guess as to if Lua does it or can be altered to do it. (Might be as simple as looking through the global tables or an objects table to see if a method of the same name is defined already or not). \$\endgroup\$
    – James
    Mar 3, 2012 at 0:13
  • \$\begingroup\$ Sure! The script may or may not contain, say a function called hero_dies(). I can just imagine the problems that would emerge from C++ attempting to call a nonexistent script function, so I would like to be able to test for existence before calling, otherwise fail silently. If the Lua binding by default ignores nonexistent calls, then I won't need to worry about (3). \$\endgroup\$
    – Vortico
    Mar 3, 2012 at 0:20
  • \$\begingroup\$ Oh, I was thinking the other way around. Let me see if I can not figure out what happens (would assume the call just fails in a handle-able manner but seeing as I have never had that situation, I am not going to bet an answer on it :)) \$\endgroup\$
    – James
    Mar 3, 2012 at 0:26
  • \$\begingroup\$ (1) is the major issue though. I was able to call script functions from C++ and static C++ functions from scripts, but I couldn't figure out how to set a global "game" variable in the scripting environment which would correspond to the "Game *game" pointer defined in C++'s main(). The script writer would then have global access to this object, and manipulating the game properties through the scripting language would become extremely efficient. \$\endgroup\$
    – Vortico
    Mar 3, 2012 at 0:28
  • \$\begingroup\$ Boost::Python can do 1 and 3. When you write C++ code you use boost to write python class description (basically you will make two classes, not two implementations, one for C++ one for python), so... python has class and you can use reflection to do 3. \$\endgroup\$
    – edin-m
    Mar 18, 2012 at 12:17

2 Answers 2

6
\$\begingroup\$

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<Widget>(L, 1);
    Widget* child = luaW_check<Widget>(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<Widget, Style, &Widget::GetStyle> },
    { "SetStyle", luaU_set<Widget, Style, &Widget::SetStyle> },
    { "Style", luaU_getset<Widget, Style, &Widget::GetStyle, &Widget::SetStyle> },

    { NULL, NULL }
};

int luaopen_Widget(lua_State* L)
{
    luaW_register<Widget>(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);
}
\$\endgroup\$
7
  • \$\begingroup\$ 1 and 2 are easy from what I recall (and I also agree that most wrapping libs out there are overly complicated and so usually roll my own). But what I am trying to find out is if there is a test for the existence of a function or method. Most of the calls to execute a lua function from C do not appear to return. Assuming that if the script compiled the syntax was atleast ok, that just leaves invalid arguments vs undefined call, but I have not been able to find a way to determine which is which yet :) \$\endgroup\$
    – James
    Mar 3, 2012 at 1:04
  • \$\begingroup\$ Are you familiar with how to call Lua functions from the C API? If you know how to do that, just do a nil check before calling your function. It's super easy. I'll update my answer with an example momentarily \$\endgroup\$
    – Alex Ames
    Mar 3, 2012 at 1:07
  • \$\begingroup\$ In the API summary: "Objects can be created in either Lua or C++, and passed back and forth." I'm sold. I will try this out tonight. \$\endgroup\$
    – Vortico
    Mar 3, 2012 at 1:19
  • \$\begingroup\$ I'm somewhat familiar with the Lua API, but only two days worth. Do I use lua_isnil(L, 1)? Is index 1 always the name of the called function? \$\endgroup\$
    – Vortico
    Mar 3, 2012 at 1:22
  • \$\begingroup\$ If you're not familiar with the Lua API (either the C API or the language itself) I suggest you read through Programming In Lua. You should be able to get through the whole tutorial in a weekend or so. There's also the Lua Reference Manual. To answer your question, Lua works with stack. 1 is the bottom of the stack. If you want to count from the top down, you can use negative numbers. For exmaple, -1 is the value at the top of the stack. It'll make more sense if you read the tutorial. \$\endgroup\$
    – Alex Ames
    Mar 3, 2012 at 1:29
2
\$\begingroup\$

Personally, I am a fan of AngelScript.

It does exactly the things you want, it has a close to C++ syntax.

You can easily create different scripting contexts, and for some types of scripts, have functionality x, for others, y. For example if you want to have a game loop in a script, and AI specific functions in other, while hiding the other one's API.

You can call functions by their name, you can export global functions and class members easily. You can define global properties as well.

So you could basically configure your script to know a Game object, which is your actual Game instance. Then call functions exported on it.

Its really straightforward, but if you want I can add little snippets of how it could be done. Though, we re talking about 1-line setups !

\$\endgroup\$
2
  • \$\begingroup\$ This also looks like a possibility. It seems like it's designed to "take over" more of the C++ work than what I had intended, but the C++ glue code looks simple enough to make the change worth it. In your experiences, how have you been able to debug your AngelScript code? \$\endgroup\$
    – Vortico
    Mar 4, 2012 at 0:56
  • \$\begingroup\$ Well, when something goes wrong, it warns you through a message callback. Also, in debug mode it outputs a call stack to a file. Besides, from the nature of the scripting language, I believe it makes your life easy to make safe environments, where only a limited, granted to work functionality can be called. \$\endgroup\$
    – Grimshaw
    Mar 4, 2012 at 1:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .