2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit 9ee1138f authored by Artur Grunau's avatar Artur Grunau
Browse files

Add support for all signal arities to SWIG wrappers for siglot

SWIG wrappers for siglot only supported unary signals until now. This
commit adds support for all remaining signal arities to make it possible
to connect to arbitrary signals from Lua.

The implementation makes heavy use of templates but, due to the
limitations of VS 2010 (no variadic templates), still contains lots of
duplicated boilerplate code.

`sigslot.i` has been moved from `scripting/` to `ext/sigslot/` to keep
it close to the code it's wrapping.

References #1
parent 2d4761ba
......@@ -2,7 +2,7 @@
%include factory.i
%include std_string.i
%import "ext/tgt/bindings/tgt.i"
%include "scripting/sigslot.i"
%include "ext/sigslot/sigslot.i"
%{
#include "core/datastructures/abstractdata.h"
#include "core/datastructures/imagedata.h"
......
This diff is collapsed.
%module sigslot
%include lua_fnptr.i
%{
#include "tbb/recursive_mutex.h"
#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 {};
typedef tbb::recursive_mutex LuaStateMutexType;
/**
* 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(SWIGLUA_REF slot_fn)
: _slot_fn(slot_fn)
, _dummy_dest(nullptr)
{
// Retrieve from the registry the mutex associated with the function's Lua state
lua_pushlightuserdata(slot_fn.L, static_cast<void*>(slot_fn.L));
lua_gettable(slot_fn.L, LUA_REGISTRYINDEX);
// The mutex should be stored as light userdata
assert(lua_islightuserdata(slot_fn.L, -1));
_lua_state_mutex = static_cast<LuaStateMutexType*>(lua_touserdata(slot_fn.L, -1));
lua_pop(slot_fn.L, 1);
}
_lua_connection1(SWIGLUA_REF slot_fn, LuaStateMutexType* lua_state_mutex)
: _slot_fn(slot_fn)
, _dummy_dest(nullptr)
, _lua_state_mutex(lua_state_mutex)
{}
virtual ~_lua_connection1() {
swiglua_ref_clear(&_slot_fn);
if (_dummy_dest != nullptr)
delete _dummy_dest;
}
virtual _connection_base1<arg1_type, mt_policy>* clone() {
SWIGLUA_REF slot_fn;
{
LuaStateMutexType::scoped_lock lock(*_lua_state_mutex);
swiglua_ref_get(&_slot_fn);
swiglua_ref_set(&slot_fn, _slot_fn.L, -1);
lua_pop(_slot_fn.L, 1);
}
return new _lua_connection1(slot_fn, _lua_state_mutex);
}
virtual _connection_base1<arg1_type, mt_policy>* duplicate(sigslot::has_slots<mt_policy>* pnewdest) {
/*
* Because Lua connections do not have any external destination objects that could be
* copied (which in turn would require duplicating the connections for the copy), this
* method should never be invoked.
*/
return nullptr;
}
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;
}
{
LuaStateMutexType::scoped_lock lock(*_lua_state_mutex);
// 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 {
/*
* Because Lua connections do not have any destination objects, a dummy one has to be
* created here and returned to comply with sigslot's API.
*/
if (_dummy_dest == nullptr)
_dummy_dest = new has_slots<mt_policy>();
return _dummy_dest;
}
/**
* Check if this connection's slot function is the same as the given Lua function.
*
* @param slot_fn reference to a Lua function acting as a slot
* @return true if this connection wraps the given Lua function, false otherwise
*/
bool wrapsSlotFunction(SWIGLUA_REF slot_fn) {
if (slot_fn.L != _slot_fn.L)
return false;
swiglua_ref_get(&_slot_fn);
swiglua_ref_get(&slot_fn);
bool result = lua_rawequal(slot_fn.L, -1, -2) == 1;
lua_pop(_slot_fn.L, 2);
return result;
}
private:
SWIGLUA_REF _slot_fn; ///< Reference to a Lua function acting as a slot
mutable has_slots<mt_policy>* _dummy_dest; ///< Dummy destination object needed to support getdest()
LuaStateMutexType* _lua_state_mutex; ///< Mutex guarding access to the above function's Lua state
};
}
}
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);
}
/**
* Disconnect a Lua function from this signal.
*
* @param slot_fn reference to a Lua function acting as a slot
*/
void disconnect(SWIGLUA_REF slot_fn) {
typedef sigslot::_signal_base1<arg1_type, mt_policy>::connections_list connections_list;
sigslot::lock_block_write<mt_policy> lock($self);
connections_list::iterator it = $self->m_connected_slots.begin();
connections_list::iterator itEnd = $self->m_connected_slots.end();
while (it != itEnd) {
sigslot::_lua_connection1<arg1_type, mt_policy>* lua_connection =
dynamic_cast<sigslot::_lua_connection1<arg1_type, mt_policy>*>(*it);
if (lua_connection != nullptr && lua_connection->wrapsSlotFunction(slot_fn)) {
delete lua_connection;
$self->m_connected_slots.erase(it);
return;
}
++it;
}
}
}
};
}
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