Jump to content

Creating C++ classes for use in LUA

Roland

1,693 views

For this Zero project I have a need to call C++ classes from LUA. I have gone the same way as Josh has and used 'tolua++' for this. This works like this

 

1 - Create PKG

For each class you need to export you have to write a corresponding 'pkg' file which is a version of the C++ header file suitable for the 'tolua++' program.

 

2 - Use 'tolua++'

Then send the 'pkg' file to 'tolua++' which then will generate a source file with the LUA-export version of the class and a header file which defines the function to call in order to export the class.

 

3 - Add & Compile

The two generated files should be included in your C++ project and you have to call the function defined in the header at some time after LUA has been initialized by Leadwerks. After compilation and linking you should be able to use the C++ class in you LUA scripts

 

Here is a simple example:

 

C++ header file: CppTest.h

#pragma once
#include <string>
class CppTest
{
int _value;
std::string _string;

public:
CppTest();
virtual ~CppTest();

int get_value() const;
void set_value( int value );

std::string get_string() const;
void set_string( const std::string& value );
};

 

C++ source file: CppTest.cpp

#include "CppTest.h"

CppTest::CppTest()
{
_value = 0;
}

CppTest::~CppTest()
{
}

int CppTest::get_value() const
{
return _value;
}

void CppTest::set_value( int value )
{
_value = value;
}

std::string CppTest::get_string() const
{
return _string;
}

void CppTest::set_string( const std::string& value )
{
_string = value;
}

 

PKG file: CppTest.pkg

$#include <string>
$#include "CppTest.h"

class CppTest : public Temp
{
CppTest();
virtual ~CppTest();

int get_value() const;
void set_value( int value );

std::string get_string() const;
void set_string( const std::string& value );
};

 

After you written the CppTest.pkg file you have to compile it using 'tolua++' like this. Note that I use the same filename for the outputs but with an '_' added. That way its easy to keep track of things

 

tolua++ -o CppTest_.cpp -n CppTest -H CppTest_.h CppTest.pkg

 

Now tolua should have generated two files.

 

Generated CppTest_.h

/*
** Lua binding: CppTest
** Generated automatically by tolua++-1.0.92 on 08/18/16 11:36:39.
*/

/* Exported function */
TOLUA_API int tolua_CppTest_open (lua_State* tolua_S);

 

Generated CppTest_.cpp (shortned down)

/*
** Lua binding: CppTest
** Generated automatically by tolua++-1.0.92 on 08/18/16 11:36:39.
*/

#ifndef __cplusplus
#include "stdlib.h"
#endif
#include "string.h"

#include "tolua++.h"

/* Exported function */
TOLUA_API int tolua_CppTest_open (lua_State* tolua_S);

#include <string>
#include "CppTest.h"

/* function to release collected object via destructor */
#ifdef __cplusplus

static int tolua_collect_CppTest (lua_State* tolua_S)
{
CppTest* self = (CppTest*) tolua_tousertype(tolua_S,1,0);
delete self;
return 0;
}
#endif
--- snip --- snip --- snip ---
--- snip --- snip --- snip ---
--- snip --- snip --- snip ---

/* Open function */
TOLUA_API int tolua_CppTest_open (lua_State* tolua_S)
{
tolua_open(tolua_S);
tolua_reg_types(tolua_S);
tolua_module(tolua_S,NULL,0);
tolua_beginmodule(tolua_S,NULL);
#ifdef __cplusplus
tolua_cclass(tolua_S,"CppTest","CppTest","Temp",tolua_collect_CppTest);
#else
tolua_cclass(tolua_S,"CppTest","CppTest","Temp",NULL);
#endif
tolua_beginmodule(tolua_S,"CppTest");
tolua_function(tolua_S,"new",tolua_CppTest_CppTest_new00);
tolua_function(tolua_S,"new_local",tolua_CppTest_CppTest_new00_local);
tolua_function(tolua_S,".call",tolua_CppTest_CppTest_new00_local);
tolua_function(tolua_S,"delete",tolua_CppTest_CppTest_delete00);
tolua_function(tolua_S,"get_value",tolua_CppTest_CppTest_get_value00);
tolua_function(tolua_S,"set_value",tolua_CppTest_CppTest_set_value00);
tolua_function(tolua_S,"get_string",tolua_CppTest_CppTest_get_string00);
tolua_function(tolua_S,"set_string",tolua_CppTest_CppTest_set_string00);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
return 1;
}


#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501
TOLUA_API int luaopen_CppTest (lua_State* tolua_S) {
return tolua_CppTest_open(tolua_S);
};
#endif

You should include both of those in your project. You also have to call tolua_CppTest_open somewhere after Leadwerks has initialized LUA. I do it here in my App.cpp. Remember to #include "CppTest_.h" at the top of App.cpp.

 

App.cpp (shortned down)

#include "App.h"
#include "CppTest_.h"

using namespace Leadwerks;

App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {}

App::~App() { delete world; delete window; }

bool App::Start()
{
int stacksize = Interpreter::GetStackSize();

//Get the global error handler function
int errorfunctionindex = 0;
#ifdef DEBUG
Interpreter::GetGlobal("LuaErrorHandler");
errorfunctionindex = Interpreter::GetStackSize();
#endif

//Create new table and assign it to the global variable "App"
Interpreter::NewTable();
Interpreter::SetGlobal("App");

std::string scriptpath = "Scripts/Main.lua";
if (FileSystem::GetFileType("Scripts/App.Lua") == 1) scriptpath = "Scripts/App.Lua";

// ADDED to initialize the CppTest LUA implemenation
tolua_CppTest_open(Interpreter::L);

//Invoke the start script
if (!Interpreter::ExecuteFile(scriptpath))
{
System::Print("Error: Failed to execute script \"" + scriptpath + "\".");
return false;
}
--- snip --- snip --- snip ---
--- snip --- snip --- snip ---
--- snip --- snip --- snip ---

LuaParser

I read somewhere that Josh has a parser that automates this a bit by parsing lines in the header file that is commented with //lua and generates pkg-files. What a good idea. As I think programming is better that watching lousy programs on TV, I made my own version of this and called it "LuaParser". I have attached this program for those who like to use it. Here is what it does

 

1. Parses all C++ header files (*.h) in the folder and subfolders for lines commented with //lua

2. For such files it creates a pkg file suitable for tolua++ compilation

3. It complies the generated pkg files into _.h and _.cpp files to be included into you project

 

Here is same example from above declared for LuaParsing

 

C++ header file: CppTest.h prepared for LuaParser

#pragma once
#include <string>//lua

class CppTest
{
int _value;
std::string _string;

public:
CppTest();//lua
virtual ~CppTest();//lua

int get_value() const;//lua
void set_value( int value );//lua

std::string get_string() const;//lua
void set_string( const std::string& value );//lua
};


 

Extract the LuaParser.zip into you Source folder and open a command prompt there. The just type LuaParser and hit Enter. A number of files ending with '_' in the name will be generated. Include them in your project and call the function in each of the '_.h' files as mentioned.

 

Windows version

LuaParserWin-1.5.zip

 

Linux version

LuaParserLinux-1.0.tar.gz - support discontinued

 

History

1.0 Initial version

1.1 Comment header in PKG files was inside class declaration instead of at top of file

1.2 Didn't handle class inheritance

1.3 - 1.5 Various minor fixes

 

You can read more about tolua++ here

tolua++ - Reference Manual



4 Comments


Recommended Comments

The entity class already has functions on getting script values.

 


virtual void SetString(const std::string& name, const std::string& s);

virtual std::string GetString(const std::string& name);

virtual void SetObject(const std::string& name, Object* o);

virtual Object* GetObject(const std::string& name);

virtual bool GetBool(const std::string& name);

virtual void SetFloat(const std::string& name, const float f);

virtual float GetFloat(const std::string& name);

 

What does your system do that those functions don't? I'm curious.

Share this comment


Link to comment

Hi @reepblue.

This was just a simple example but whats in essential goes on is that a C++ object can expose is self, its methods (functions) to LUA, It does not necessarily has to be some kind if entity based object, it can be anything. In the simple example shown LUA creates an instance of the C++ object and call methods (functions) in that object. For simplicity I just choose'd to show two methods for setting and getting a value, however it could be any kind of method doing anything like saving and loading things from your hard-drive or sending data over the internet .. just anything. The idea is to expose C++ objects to LUA so that LUA can use them as they were LUA objects. Hope this made it more clear.

 

As another example I saw a discussion about "Check For Any Key Pressed / Alt Key Mapping" here on the forum. You could then make a C++ class or function that checks for any key pressed (which is easy to do in C++) and expose that class to LUA for usage.

Share this comment


Link to comment

didn't know about the .pkg file

i was testing to expose a cpp class to lua in linux and realize that tolua++5.1 did not generate the same output as tolua++ that I used on windows a couple of years ago!

found this post and after some trials.. i got my class working. I have to write my .pkg file by hand, but this is a minor issue.

thank's

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...