Commit 785ba7de authored by Artur Grunau's avatar Artur Grunau
Browse files

Initial support for using Lua functions as slots

Certain types of pipelines need to react to changes in their constituent
processors. CAMPVis uses a signal-slot mechanism (based on the sigslot
library) for this purpose. Up until now, however, there was no way to
connect to a signal from a pipeline defined in Lua.

This commit adds initial support for connecting Lua functions to
sigslot's signals. The current implementation only works with unary
signals, and should be considered a proof of concept. To test it,
AbstractProcessor's s_validated signal has been wrapped using SWIG.

References #1
parent d41681ec
......@@ -2,6 +2,7 @@
%include factory.i
%include std_string.i
%import "ext/tgt/bindings/tgt.i"
%include "scripting/sigslot.i"
%{
#include "core/datastructures/abstractdata.h"
#include "core/datastructures/imagedata.h"
......@@ -20,6 +21,24 @@
%}
%inline {
static const char* const SOURCE_DIR = CAMPVIS_SOURCE_DIR;
// Template specialisations and instantiations required to get signals to work in Lua
namespace sigslot {
template<>
struct LuaConnectionArgTraits<campvis::AbstractProcessor*> {
static const char* const typeName;
};
const char* const LuaConnectionArgTraits<campvis::AbstractProcessor*>::typeName = "campvis::AbstractProcessor *";
}
}
%template(sigslot_signal1_AbstractProcessor) sigslot::signal1<campvis::AbstractProcessor*>;
namespace campvis {
class AbstractProperty {
......@@ -288,6 +307,10 @@ namespace campvis {
};
const std::string getName() const = 0;
%immutable;
sigslot::signal1<AbstractProcessor*> s_validated;
%mutable;
};
/* AbstractPipeline */
......@@ -335,10 +358,6 @@ namespace campvis {
};
}
%inline {
static const char* const SOURCE_DIR = CAMPVIS_SOURCE_DIR;
}
%luacode {
function campvis.newPipeline (name, o)
......
%module sigslot
%include lua_fnptr.i
%{
#include "ext/sigslot/sigslot.h"
%}
%inline {
namespace sigslot {
/**
* Signal arguments need to be wrapped before they can be passed to slots defined in Lua. That
* requires the textual representation of their respective types to be known. Unfortunately,
* there is no portable way to get a string describing a type in C++, and SWIG doesn't expose
* such functionality. Consequently, we use a trait for that purpose; it needs to be specialised
* for all types used as arguments of signals that are exposed to Lua, e.g.:
*
* template<>
* struct LuaConnectionArgTraits<campvis::AbstractProcessor*> {
* static const char* const typeName;
* };
*
* const char* const LuaConnectionArgTraits<campvis::AbstractProcessor*>::typeName = "campvis::AbstractProcessor *";
*/
template<typename T>
struct LuaConnectionArgTraits {};
/**
* Custom signal-slot connection type that accepts Lua functions as slots.
*
* SWIG's lua_fnptr extension and one of the structures it defines, SWIGLUA_REF, are used to
* handle Lua functions on the C++ side.
*/
template<class arg1_type, class mt_policy>
class _lua_connection1 : public _connection_base1<arg1_type, mt_policy>
{
public:
_lua_connection1() : _slot_fn() {}
_lua_connection1(SWIGLUA_REF slot_fn) : _slot_fn(slot_fn) {}
virtual ~_lua_connection1() {
swiglua_ref_clear(&_slot_fn);
}
virtual _connection_base1<arg1_type, mt_policy>* clone() {
return nullptr;
}
virtual _connection_base1<arg1_type, mt_policy>* duplicate(sigslot::has_slots<mt_policy>* pnewdest) {
return clone();
}
virtual void emitSignal(arg1_type a1) {
const char* const typeName = LuaConnectionArgTraits<arg1_type>::typeName;
swig_type_info* argTypeInfo = SWIG_TypeQuery(typeName);
if (argTypeInfo == nullptr) {
std::cerr << "SWIG wrapper for " << typeName << " not found" << std::endl;
return;
}
// Put this connection's slot and all arguments on Lua's stack
swiglua_ref_get(&_slot_fn);
SWIG_NewPointerObj(_slot_fn.L, a1, argTypeInfo, 0);
if (lua_pcall(_slot_fn.L, 1, 0, 0) != LUA_OK) {
const char* errorMsg = lua_tostring(_slot_fn.L, -1);
if (errorMsg == nullptr)
std::cerr << "(error object is not a string)" << std::endl;
else
std::cerr << "An error occured while calling a Lua slot function: " << errorMsg << std::endl;
lua_pop(_slot_fn.L, 1);
}
}
virtual has_slots<mt_policy>* getdest() const {
return nullptr;
}
private:
SWIGLUA_REF _slot_fn;
};
}
}
namespace sigslot {
template<class arg1_type, class mt_policy = sigslot::SIGSLOT_DEFAULT_MT_POLICY>
class signal1 {
public:
signal1();
signal1(const sigslot::signal1<arg1_type, mt_policy>& s);
%extend {
/**
* Connect this signal to a Lua function.
*
* @param slot_fn reference to a Lua function acting as a slot
*/
void connect(SWIGLUA_REF slot_fn) {
sigslot::lock_block_write<mt_policy> lock($self);
sigslot::_lua_connection1<arg1_type, mt_policy>* conn =
new sigslot::_lua_connection1<arg1_type, mt_policy>(slot_fn);
$self->m_connected_slots.push_back(conn);
}
}
};
}
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