1 |
|
|
#ifndef DYNAMIC_GRAPH_PYTHON_MODULE_HH |
2 |
|
|
#define DYNAMIC_GRAPH_PYTHON_MODULE_HH |
3 |
|
|
|
4 |
|
|
#ifdef PINOCCHIO_WITH_URDFDOM |
5 |
|
|
// If pinocchio is used, we have to include pinocchio header before boost mpl |
6 |
|
|
#include <pinocchio/fwd.hpp> |
7 |
|
|
#endif |
8 |
|
|
|
9 |
|
|
#include <dynamic-graph/entity.h> |
10 |
|
|
|
11 |
|
|
#include <boost/mpl/for_each.hpp> |
12 |
|
|
#include <boost/mpl/vector.hpp> |
13 |
|
|
#include <boost/python.hpp> |
14 |
|
|
#include <dynamic-graph/python/dynamic-graph-py.hh> |
15 |
|
|
|
16 |
|
|
namespace dynamicgraph { |
17 |
|
|
namespace python { |
18 |
|
|
|
19 |
|
|
constexpr int AddSignals = 1; |
20 |
|
|
constexpr int AddCommands = 2; |
21 |
|
|
|
22 |
|
|
namespace internal { |
23 |
|
|
|
24 |
|
|
template <typename T, int Options = AddCommands | AddSignals> |
25 |
|
5 |
bp::object makeEntity1(const char* name) { |
26 |
|
5 |
Entity* ent = entity::create(T::CLASS_NAME.c_str(), name); |
27 |
✓✗✗✓
|
5 |
assert(dynamic_cast<T*>(ent) != NULL); |
28 |
✓✗ |
5 |
bp::object obj(bp::ptr(static_cast<T*>(ent))); |
29 |
✓✗✓✗
|
5 |
if (Options & AddCommands) entity::addCommands(obj); |
30 |
✓✗✓✗
|
5 |
if (Options & AddSignals) entity::addSignals(obj); |
31 |
|
5 |
return obj; |
32 |
|
|
} |
33 |
|
|
template <typename T, int Options = AddCommands | AddSignals> |
34 |
|
|
bp::object makeEntity2() { |
35 |
|
|
return makeEntity1<T, Options>(""); |
36 |
|
|
} |
37 |
|
|
|
38 |
|
|
} // namespace internal |
39 |
|
|
|
40 |
|
|
/// \tparam Options by default, all the signals and commands are added as |
41 |
|
|
/// attribute to the Python object. This behaviour works fine for |
42 |
|
|
/// entities that have static commands and signals. |
43 |
|
|
/// If some commands or signals are added or removed dynamiccally, then |
44 |
|
|
/// it is better to disable the default behaviour and handle it |
45 |
|
|
/// specifically. |
46 |
|
|
template <typename T, |
47 |
|
|
typename bases = boost::python::bases<dynamicgraph::Entity>, |
48 |
|
|
int Options = AddCommands | AddSignals> |
49 |
|
6 |
inline auto exposeEntity() { |
50 |
|
|
// std::string hiddenClassName ("_" + T::CLASS_NAME); |
51 |
✓✗ |
12 |
std::string hiddenClassName(T::CLASS_NAME); |
52 |
|
|
namespace bp = boost::python; |
53 |
✓✗✓✗
|
6 |
bp::class_<T, bases, boost::noncopyable> obj(hiddenClassName.c_str(), |
54 |
|
|
bp::init<std::string>()); |
55 |
|
|
/* TODO at the moment, I couldn't easily find a way to define a Python |
56 |
|
|
constructor |
57 |
|
|
* that would create the entity via the factory and then populate the |
58 |
|
|
* python object with its commands. |
59 |
|
|
* This is achieved with a factory function of the same name. |
60 |
|
|
obj.def ("__init__", bp::raw_function(+[](bp::object args, bp::dict) { |
61 |
|
|
if (bp::len(args) != 2) |
62 |
|
|
throw std::length_error("Expected 2 arguments."); |
63 |
|
|
bp::object self = args[0]; |
64 |
|
|
self.attr("__init__")(bp::extract<std::string>(args[1])); |
65 |
|
|
Entity* ent = entity::create(T::CLASS_NAME.c_str(), name); |
66 |
|
|
if (dynamic_cast<T*>(ent) == NULL) |
67 |
|
|
std::cout << "foo" << std::endl; |
68 |
|
|
assert(dynamic_cast<T*>(ent) != NULL); |
69 |
|
|
self = bp::object(bp::ptr(static_cast<T*>(ent))); |
70 |
|
|
//dynamicgraph::Entity& unused = |
71 |
|
|
bp::extract<dynamicgraph::Entity&>(self); |
72 |
|
|
//entity::addCommands(self); |
73 |
|
|
}) |
74 |
|
|
; |
75 |
|
|
*/ |
76 |
✓✗ |
6 |
bp::def(T::CLASS_NAME.c_str(), &internal::makeEntity1<T, Options>); |
77 |
✓✗ |
6 |
bp::def(T::CLASS_NAME.c_str(), &internal::makeEntity2<T, Options>); |
78 |
|
4 |
if (!(Options & AddCommands)) obj.def("add_commands", &entity::addCommands); |
79 |
|
4 |
if (!(Options & AddSignals)) obj.def("add_signals", &entity::addSignals); |
80 |
|
12 |
return obj; |
81 |
|
|
} |
82 |
|
|
|
83 |
|
|
} // namespace python |
84 |
|
|
} // namespace dynamicgraph |
85 |
|
|
|
86 |
|
|
#endif // DYNAMIC_GRAPH_PYTHON_MODULE_HH |