From 1ff01145a59f8210e9ef1c44e791ba12d586fe79 Mon Sep 17 00:00:00 2001 From: Joseph Mirabel <jmirabel@laas.fr> Date: Thu, 15 Feb 2018 13:34:53 +0100 Subject: [PATCH] Add SignalWrapper which allows to bind a Python function to a signal. --- src/CMakeLists.txt | 1 + src/dynamic-graph-py.cc | 4 ++ src/signal-base-py.cc | 53 +++++++++++++++++++++++++ src/signal-wrapper.cc | 55 ++++++++++++++++++++++++++ src/signal-wrapper.hh | 85 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+) create mode 100644 src/signal-wrapper.cc create mode 100644 src/signal-wrapper.hh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc0f459..65f1962 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,6 +73,7 @@ ADD_LIBRARY(${PYTHON_MODULE} factory-py.cc pool-py.cc signal-caster-py.cc + signal-wrapper.cc ) # Remove prefix lib diff --git a/src/dynamic-graph-py.cc b/src/dynamic-graph-py.cc index 99ad52c..ab7519b 100644 --- a/src/dynamic-graph-py.cc +++ b/src/dynamic-graph-py.cc @@ -23,6 +23,7 @@ #include <dynamic-graph/signal-base.h> #include "exception.hh" +#include "signal-wrapper.hh" namespace dynamicgraph { namespace python { @@ -30,6 +31,7 @@ namespace dynamicgraph { // Declare functions defined in other source files namespace signalBase { extern PyObject* create(PyObject* self, PyObject* args); + extern PyObject* createSignalWrapper(PyObject* self, PyObject* args); extern PyObject* getTime(PyObject* self, PyObject* args); extern PyObject* setTime(PyObject* self, PyObject* args); extern PyObject* getName(PyObject* self, PyObject* args); @@ -151,6 +153,8 @@ static PyMethodDef dynamicGraphMethods[] = { // Signals {"create_signal_base", dynamicgraph::python::signalBase::create, METH_VARARGS, "create a SignalBase C++ object"}, + {"create_signal_wrapper", dynamicgraph::python::signalBase::createSignalWrapper, METH_VARARGS, + "create a SignalWrapper C++ object"}, {"signal_base_get_time", dynamicgraph::python::signalBase::getTime, METH_VARARGS, "Get time of a SignalBase"}, {"signal_base_set_time", dynamicgraph::python::signalBase::setTime, diff --git a/src/signal-base-py.cc b/src/signal-base-py.cc index 2c2115c..6cf5fd7 100644 --- a/src/signal-base-py.cc +++ b/src/signal-base-py.cc @@ -25,6 +25,7 @@ #include "convert-dg-to-py.hh" #include "exception.hh" +#include "signal-wrapper.hh" using dynamicgraph::SignalBase; @@ -56,6 +57,58 @@ namespace dynamicgraph { return PyCObject_FromVoidPtr((void*)obj, destroy); } + template <class T> void* createSignalWrapperTpl (const char* name, PyObject* o, std::string& error) + { + typedef SignalWrapper<T, int> SignalWrapper_t; + if (!SignalWrapper_t::checkCallable (o, error)) { + return NULL; + } + + SignalWrapper_t* obj = new SignalWrapper_t(name, o); + return (void*) obj; + } + +#define SIGNAL_WRAPPER_TYPE(IF, Enum, Type) \ + IF (command::Value::typeName(command::Value::Enum) \ + .compare(type) == 0) { \ + obj = createSignalWrapperTpl<Type> (name, object, error); \ + } + + /** + \brief Create an instance of SignalWrapper + */ + PyObject* createSignalWrapper(PyObject* /*self*/, PyObject* args) + { + char *name = NULL; + char *type = NULL; + PyObject* object = NULL; + + if (!PyArg_ParseTuple(args, "ssO", &name, &type, &object)) + return NULL; + + void* obj = NULL; + std::string error; + SIGNAL_WRAPPER_TYPE( if, BOOL ,bool) + // SIGNAL_WRAPPER_TYPE(else if, UNSIGNED ,bool) + SIGNAL_WRAPPER_TYPE(else if, INT ,int ) + SIGNAL_WRAPPER_TYPE(else if, FLOAT ,float ) + SIGNAL_WRAPPER_TYPE(else if, DOUBLE ,double) + // SIGNAL_WRAPPER_TYPE(else if, STRING ,bool) + SIGNAL_WRAPPER_TYPE(else if, VECTOR ,Vector) + // SIGNAL_WRAPPER_TYPE(else if, MATRIX ,bool) + // SIGNAL_WRAPPER_TYPE(else if, MATRIX4D ,bool) + else { + error = "Type not understood"; + } + + if (obj == NULL) { + PyErr_SetString(dgpyError, error.c_str()); + return NULL; + } + // Return the pointer + return PyCObject_FromVoidPtr(obj, destroy); + } + /** \brief Destroy an instance of InvertedPendulum */ diff --git a/src/signal-wrapper.cc b/src/signal-wrapper.cc new file mode 100644 index 0000000..2e80a9a --- /dev/null +++ b/src/signal-wrapper.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2018, Joseph Mirabel +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// +// This file is part of dynamic-graph-python. +// dynamic-graph-python is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// dynamic-graph-python is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. You should have +// received a copy of the GNU Lesser General Public License along with +// dynamic-graph-python. If not, see <http://www.gnu.org/licenses/>. + +#include <Python.h> + +#include <signal-wrapper.hh> + +namespace dynamicgraph { + namespace python { + namespace signalWrapper { + void convert (PyObject* o, bool & v) { v = (o == Py_True); } + void convert (PyObject* o, int & v) { v = (int)PyInt_AS_LONG (o); } + void convert (PyObject* o, float & v) { v = (float)PyFloat_AS_DOUBLE (o); } + void convert (PyObject* o, double& v) { v = PyFloat_AS_DOUBLE (o); } + void convert (PyObject* o, Vector & v) + { + v.resize(PyTuple_Size(o)); + for (int i = 0; i < v.size(); ++i) + convert(PyTuple_GetItem(o,i), v[i]); + } + } + + template <class T, class Time> + bool SignalWrapper<T,Time>::checkCallable (PyObject* c, std::string& error) + { + if (PyCallable_Check(c) == 0) { + PyObject* str = PyObject_Str(c); + error = PyString_AsString(str); + error += " is not callable"; + Py_DECREF(str); + return false; + } + return true; + } + + template class SignalWrapper<bool , int>; + template class SignalWrapper<int , int>; + template class SignalWrapper<float , int>; + template class SignalWrapper<double, int>; + template class SignalWrapper<Vector, int>; + } // namespace dynamicgraph +} // namespace python diff --git a/src/signal-wrapper.hh b/src/signal-wrapper.hh new file mode 100644 index 0000000..d37d345 --- /dev/null +++ b/src/signal-wrapper.hh @@ -0,0 +1,85 @@ +// Copyright (c) 2018, Joseph Mirabel +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// +// This file is part of dynamic-graph-python. +// dynamic-graph-python is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// dynamic-graph-python is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. You should have +// received a copy of the GNU Lesser General Public License along with +// dynamic-graph-python. If not, see <http://www.gnu.org/licenses/>. + +#include <Python.h> + +#include <dynamic-graph/linear-algebra.h> +#include <dynamic-graph/signal.h> + +namespace dynamicgraph { + namespace python { + namespace signalWrapper { + void convert (PyObject* o, int & v); + void convert (PyObject* o, bool & v); + void convert (PyObject* o, float & v); + void convert (PyObject* o, double& v); + // void convert (PyObject* o, std::string& v); + void convert (PyObject* o, Vector & v); + // void convert (PyObject* o, Eigen::MatrixXd& v); + // void convert (PyObject* o, Eigen::Matrix4d& v); + } + + template <class T, class Time> + class SignalWrapper : public Signal<T, Time> + { + public: + typedef Signal<T,Time> parent_t; + + static bool checkCallable (PyObject* c, std::string& error); + + SignalWrapper (std::string name, PyObject* _callable) : + parent_t (name) + , callable (_callable) + // , argsTuple (NULL) + // , argTime (NULL) + // , argValue (NULL) + { + typedef boost::function2<T&,T&,Time> function_t; + Py_INCREF(callable); + function_t f = boost::bind (&SignalWrapper::call, this, _1, _2); + this->setFunction (f); + + // argsTuple = PyTuple_New(1); + // argTime = Py + } + + virtual ~SignalWrapper () + { + Py_DECREF(callable); + // Py_DECREF(args); + }; + + private: + T& call (T& value, Time t) + { + char format[] = "i"; + PyObject* obj = PyObject_CallFunction(callable, format, t); + if (obj == NULL) + std::cerr << "Could not call callable" << std::endl; + else { + signalWrapper::convert (obj, value); + Py_DECREF(obj); + } + return value; + } + PyObject* callable; + // PyObject* argsTuple; + // PyObject* argTime; + // PyObject* argValue; + }; + + } // namespace dynamicgraph +} // namespace python -- GitLab