Commit 4e87e812 authored by Artur Grunau's avatar Artur Grunau
Browse files

Add several helper classes for accessing Lua state

Interacting with Lua using its low-level C API quickly becomes cumbersome
and error-prone. In such cases introducing an object-oriented glue layer
makes accessing Lua state considerably easier.

This commit adds several helper classes to campvis-scripting. The abstract
LuaTable and its concrete sub-classes RegularLuaTable and GlobalLuaTable
should make working with Lua tables more pleasant by reducing common
operations to a single method call. LuaVmState's task is to simplify the
management of Lua VM's state with various high level methods that do
automatic error checking.

The new classes are largely undocumented. This will be fixed in a future
commit.

References #1
parent 59e4f6c7
#include "globalluatable.h"
#include "regularluatable.h"
namespace campvis {
GlobalLuaTable::GlobalLuaTable(LuaVmState& luaVmState)
: LuaTable(luaVmState)
{}
GlobalLuaTable::~GlobalLuaTable() {}
bool GlobalLuaTable::isValid() {
// Each Lua VM has a global table
return true;
}
std::shared_ptr<LuaTable> GlobalLuaTable::getTable(const std::string& name) {
return std::shared_ptr<LuaTable>(new RegularLuaTable(this->shared_from_this(), name));
}
void GlobalLuaTable::callInstanceMethod(const std::string& name) {
this->pushField(name);
_luaVmState.callLuaFunc(1, 0);
}
void GlobalLuaTable::pushField(const std::string& name) {
lua_getglobal(_luaVmState.rawState(), name.c_str());
}
}
#ifndef GLOBALLUATABLE_H__
#define GLOBALLUATABLE_H__
#include "luatable.h"
#include "luavmstate.h"
namespace campvis {
class GlobalLuaTable : public LuaTable
{
public:
GlobalLuaTable(LuaVmState& luaVmState);
virtual ~GlobalLuaTable();
virtual bool isValid();
virtual std::shared_ptr<LuaTable> getTable(const std::string& name);
virtual void callInstanceMethod(const std::string& name);
protected:
virtual void pushField(const std::string& name);
};
}
#endif // GLOBALLUATABLE_H__
#ifndef LUATABLE_H__
#define LUATABLE_H__
#include <memory>
#include <string>
#include "luavmstate.h"
namespace campvis {
class RegularLuaTable;
class GlobalLuaTable;
class LuaTable : private std::enable_shared_from_this<LuaTable>
{
friend RegularLuaTable;
friend GlobalLuaTable;
public:
LuaTable(LuaVmState& luaVmState) : _luaVmState(luaVmState) {}
virtual ~LuaTable() {}
virtual bool isValid() = 0;
virtual std::shared_ptr<LuaTable> getTable(const std::string& name) = 0;
virtual void callInstanceMethod(const std::string& name) = 0;
protected:
virtual void pushField(const std::string& name) = 0;
LuaVmState& _luaVmState;
};
}
#endif // LUATABLE_H__
#include "luavmstate.h"
#include <iostream>
#include "globalluatable.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
namespace campvis {
LuaVmState::LuaVmState(bool loadStdLibs /*= true*/) : _luaState(0)
{
_luaState = luaL_newstate();
// load standard Lua libraries
if (loadStdLibs)
luaL_openlibs(_luaState);
}
LuaVmState::~LuaVmState() {
lua_close(_luaState);
}
void LuaVmState::logLuaError() {
const char* errorMsg = lua_tostring(_luaState, -1);
if (errorMsg == nullptr)
std::cerr << "(error object is not a string)" << std::endl;
else
std::cerr << errorMsg << std::endl;
lua_pop(_luaState, 1);
}
bool LuaVmState::execFile(const std::string& scriptPath) {
// run a Lua script here; true is returned if there were errors
if (luaL_dofile(_luaState, scriptPath.c_str())) {
this->logLuaError();
return false;
}
return true;
}
bool LuaVmState::execString(const std::string& scriptString) {
if (luaL_dostring(_luaState, scriptString.c_str())) {
this->logLuaError();
return false;
}
return true;
}
std::shared_ptr<GlobalLuaTable> LuaVmState::getGlobalTable() {
return std::shared_ptr<GlobalLuaTable>(new GlobalLuaTable(*this));
}
lua_State *LuaVmState::rawState() const {
return _luaState;
}
void LuaVmState::callLuaFunc(int nargs, int nresults) {
if (lua_pcall(_luaState, nargs, nresults, 0) != LUA_OK) {
this->logLuaError();
}
}
}
#ifndef LUAVMSTATE_H__
#define LUAVMSTATE_H__
#include <memory>
#include <string>
#include "swigluarun.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
struct lua_State;
namespace campvis {
class GlobalLuaTable;
class LuaVmState
{
public:
LuaVmState(bool loadStdLibs = true);
~LuaVmState();
bool execFile(const std::string& scriptPath);
bool execString(const std::string& scriptString);
std::shared_ptr<GlobalLuaTable> getGlobalTable();
template<typename T>
bool injectObjectPointer(T* objPointer, const std::string& typeName, const std::string& luaVarName);
struct lua_State* rawState() const;
/**
* Call the Lua function that's at the top of the stack.
*/
void callLuaFunc(int nargs, int nresults);
private:
void logLuaError();
struct lua_State* _luaState; ///< Lua state managed by LuaVmState
};
template<typename T>
bool LuaVmState::injectObjectPointer(T* objPointer, const std::string& typeName, const std::string& luaVarName) {
swig_type_info* objTypeInfo = SWIG_TypeQuery(_luaState, typeName.c_str());
if (objTypeInfo == nullptr) {
std::cerr << "SWIG wrapper for " << typeName << " not found" << std::endl;
return false;
} else {
SWIG_NewPointerObj(_luaState, objPointer, objTypeInfo, 0);
lua_setglobal(_luaState, luaVarName.c_str());
return true;
}
}
}
#endif // LUAVMSTATE_H__
#include "regularluatable.h"
namespace campvis {
RegularLuaTable::RegularLuaTable(std::shared_ptr<LuaTable> parent, std::string name)
: LuaTable(parent->_luaVmState)
, _parent(parent)
, _name(name)
{}
RegularLuaTable::~RegularLuaTable() {}
bool RegularLuaTable::isValid() {
bool result = false;
_parent->pushField(_name);
if (lua_istable(_luaVmState.rawState(), -1) == 1)
result = true;
// Pop the table
lua_pop(_luaVmState.rawState(), 1);
return result;
}
std::shared_ptr<LuaTable> RegularLuaTable::getTable(const std::string& name) {
return std::shared_ptr<LuaTable>(new RegularLuaTable(this->shared_from_this(), name));
}
void RegularLuaTable::callInstanceMethod(const std::string& name) {
_parent->pushField(_name);
lua_getfield(_luaVmState.rawState(), -1, name.c_str());
_parent->pushField(_name);
_luaVmState.callLuaFunc(1, 0);
// Pop the table
lua_pop(_luaVmState.rawState(), 1);
}
void RegularLuaTable::pushField(const std::string& name) {
_parent->pushField(_name);
lua_getfield(_luaVmState.rawState(), -1, name.c_str());
}
}
#ifndef REGULARLUATABLE_H__
#define REGULARLUATABLE_H__
#include "luatable.h"
namespace campvis {
class RegularLuaTable : public LuaTable
{
public:
RegularLuaTable(std::shared_ptr<LuaTable> parent, std::string name);
virtual ~RegularLuaTable();
virtual bool isValid();
virtual std::shared_ptr<LuaTable> getTable(const std::string& name);
virtual void callInstanceMethod(const std::string& name);
protected:
virtual void pushField(const std::string& name);
private:
std::shared_ptr<LuaTable> _parent;
std::string _name;
};
}
#endif // REGULARLUATABLE_H__
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment