Skip to content

Commit 7bbeb75

Browse files
committed
Started interfacing C++ property classes to Python
1 parent 4943963 commit 7bbeb75

File tree

2 files changed

+129
-72
lines changed

2 files changed

+129
-72
lines changed

core/pythoncdb/py_properties.cc

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,64 @@
5656
namespace cadabra {
5757
namespace py = pybind11;
5858

59+
template <typename PropT, typename... ParentTs>
60+
BoundProperty<PropT, ParentTs...>::BoundProperty()
61+
: prop(nullptr)
62+
, for_obj(nullptr)
63+
{
64+
65+
}
66+
67+
template <typename PropT, typename... ParentTs>
68+
BoundProperty<PropT, ParentTs...>::BoundProperty(Ex_ptr ex, Ex_ptr param)
69+
{
70+
Kernel* kernel = get_kernel_from_scope();
71+
auto prop_ = new PropT(); // we keep a pointer, but the kernel owns it.
72+
// std::cerr << "Declaring property " << prop->name() << " in kernel " << kernel << std::endl;
73+
kernel->inject_property(prop_, ex, param);
74+
75+
prop = prop_;
76+
for_obj = ex;
77+
}
78+
79+
template <typename PropT, typename... ParentTs>
80+
std::string BoundProperty<PropT, ParentTs...>::str_() const
81+
{
82+
std::ostringstream str;
83+
str << "Attached property ";
84+
prop->latex(str); // FIXME: this should call 'str' on the property, which does not exist yet
85+
str << " to " + Ex_as_str(for_obj) + ".";
86+
return str.str();
87+
}
88+
89+
template <typename PropT, typename... ParentTs>
90+
std::string BoundProperty<PropT, ParentTs...>::latex_() const
91+
{
92+
std::ostringstream str;
93+
94+
// HERE: this text should go away, property should just print itself in a python form,
95+
// the decorating text should be printed in a separate place.
96+
97+
str << "\\text{Attached property ";
98+
prop->latex(str);
99+
std::string bare = Ex_as_latex(for_obj);
100+
str << " to~}" + bare + ".";
101+
return str.str();
102+
}
103+
104+
template <>
105+
std::string BoundProperty<LaTeXForm>::latex_() const;
106+
107+
template <typename PropT, typename... ParentTs>
108+
std::string BoundProperty<PropT, ParentTs...>::repr_() const
109+
{
110+
// FIXME: this needs work, it does not output things which can be fed back into python.
111+
return "Property::repr: " + prop->name();
112+
}
113+
114+
59115
template<>
60-
std::string Property<LaTeXForm>::latex_() const
116+
std::string BoundProperty<LaTeXForm>::latex_() const
61117
{
62118
std::ostringstream str;
63119
str << "\\text{Attached property ";
@@ -69,6 +125,56 @@ namespace cadabra {
69125
return str.str();
70126
}
71127

128+
template <typename PropT, typename... ParentTs>
129+
BoundProperty<PropT, ParentTs...> BoundProperty<PropT, ParentTs...>::get_from_it(Ex::iterator it, bool ignore_parent_rel)
130+
{
131+
int tmp;
132+
auto res = get_kernel_from_scope()->properties.get_with_pattern<PropT>(
133+
it,
134+
tmp,
135+
false,
136+
ignore_parent_rel
137+
);
138+
139+
BoundProperty ret;
140+
ret.prop = res.first;
141+
if (res.second)
142+
ret.for_obj = std::make_shared<Ex>(res.second->obj);
143+
return ret;
144+
}
145+
146+
template <typename PropT, typename... ParentTs>
147+
BoundProperty<PropT, ParentTs...> BoundProperty<PropT, ParentTs...>::get_from_ex(Ex_ptr ex, bool ignore_parent_rel)
148+
{
149+
return get_from_it(ex->begin(), ignore_parent_rel);
150+
}
151+
152+
template <typename PropT, typename... ParentTs>
153+
BoundProperty<PropT, ParentTs...> BoundProperty<PropT, ParentTs...>::get_from_exnode(ExNode exnode, bool ignore_parent_rel)
154+
{
155+
return get_from_it(exnode.it, ignore_parent_rel);
156+
}
157+
158+
template <typename PropT, typename... ParentTs>
159+
PyProperty<PropT, ParentTs...> def_prop(pybind11::module& m, const char* docstring)
160+
{
161+
using namespace pybind11;
162+
163+
return PyProperty<PropT, ParentTs...>(m, std::make_shared<PropT>()->name().c_str())
164+
.def(
165+
init<std::shared_ptr<Ex>, std::shared_ptr<Ex>>(),
166+
arg("ex"),
167+
arg("param"),
168+
docstring
169+
)
170+
.def("__str__", &BoundProperty<PropT, ParentTs...>::str_)
171+
.def("__repr__", &BoundProperty<PropT, ParentTs...>::repr_)
172+
.def("_latex_", &BoundProperty<PropT, ParentTs...>::latex_)
173+
.def_static("get", &BoundProperty<PropT, ParentTs...>::get_from_ex, py::arg("ex"), py::arg("ignore_parent_rel") = false)
174+
.def_static("get", &BoundProperty<PropT, ParentTs...>::get_from_exnode, py::arg("exnode"), py::arg("ignore_parent_rel") = false);
175+
}
176+
177+
72178

73179
pybind11::list list_properties()
74180
{
@@ -123,7 +229,7 @@ namespace cadabra {
123229

124230
void init_properties(py::module& m)
125231
{
126-
py::class_<BaseProperty, std::shared_ptr<BaseProperty>>(m, "Property");
232+
py::class_<BoundPropertyBase, std::shared_ptr<BoundPropertyBase>>(m, "Property");
127233

128234
m.def("properties", &list_properties);
129235

core/pythoncdb/py_properties.hh

Lines changed: 21 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace cadabra {
1414
///
1515
/// Helper class to ensure that all Python property objects derive from the
1616
/// same base class.
17-
class BaseProperty {
17+
class BoundPropertyBase {
1818
};
1919

2020
/// \ingroup pythoncore
@@ -42,10 +42,14 @@ namespace cadabra {
4242
/// The question is now what we do when Python keeps a pointer to these
4343
/// objects, and let that pointer escape local scope (e.g. by returning
4444
/// the Python property object). How do we keep it in scope?
45-
template<class T>
46-
class Property : public std::enable_shared_from_this<Property<T>>, public BaseProperty {
45+
template <typename PropT, typename... ParentTs>
46+
class BoundProperty
47+
: public std::enable_shared_from_this<BoundProperty<PropT>>
48+
, public BoundPropertyBase
49+
, public BoundProperty<ParentTs>... {
4750
public:
48-
Property(std::shared_ptr<cadabra::Ex> obj, std::shared_ptr<cadabra::Ex> params = 0);
51+
BoundProperty();
52+
BoundProperty(Ex_ptr obj, Ex_ptr params = 0);
4953

5054
/// Human-readable form in text, i.e. no special formatting.
5155
std::string str_() const;
@@ -56,90 +60,37 @@ namespace cadabra {
5660
/// Python-parseable form. FIXME: not correct right now.
5761
std::string repr_() const;
5862

63+
static BoundProperty get_from_it(Ex::iterator ex, bool ignore_parent_rel);
64+
static BoundProperty get_from_ex(Ex_ptr ex, bool ignore_parent_rel);
65+
static BoundProperty get_from_exnode(ExNode exnode, bool ignore_parent_rel);
5966

60-
private:
6167
// We keep a pointer to the C++ property, so it is possible to
6268
// query properties using the Python interface. However, this C++
6369
// object is owned by the C++ kernel and does not get destroyed
6470
// when the Python object goes out of scope.
6571

6672
// When the Python object survives the local scope, results are
6773
// undefined.
68-
T *prop;
74+
const PropT *prop;
6975

7076
// We also keep a shared pointer to the expression for which we
7177
// have defined this property, so that we can print sensible
7278
// information.
73-
std::shared_ptr<cadabra::Ex> for_obj;
79+
Ex_ptr for_obj;
7480
};
7581

82+
template <typename PropT, typename... ParentTs>
83+
using PyProperty = pybind11::class_<
84+
BoundProperty<PropT>, // C++ type
85+
std::shared_ptr<BoundProperty<PropT>>, // Holder type
86+
BoundPropertyBase, BoundProperty<ParentTs>... // Parent classes
87+
>;
7688

77-
template<class Prop>
78-
Property<Prop>::Property(std::shared_ptr<Ex> ex, std::shared_ptr<Ex> param)
79-
{
80-
for_obj = ex;
81-
Kernel *kernel = get_kernel_from_scope();
82-
prop = new Prop(); // we keep a pointer, but the kernel owns it.
83-
// std::cerr << "Declaring property " << prop->name() << " in kernel " << kernel << std::endl;
84-
kernel->inject_property(prop, ex, param);
85-
}
86-
87-
template<class Prop>
88-
std::string Property<Prop>::str_() const
89-
{
90-
std::ostringstream str;
91-
str << "Attached property ";
92-
prop->latex(str); // FIXME: this should call 'str' on the property, which does not exist yet
93-
str << " to " + Ex_as_str(for_obj) + ".";
94-
return str.str();
95-
}
96-
97-
template<class Prop>
98-
std::string Property<Prop>::latex_() const
99-
{
100-
std::ostringstream str;
101-
102-
// HERE: this text should go away, property should just print itself in a python form,
103-
// the decorating text should be printed in a separate place.
104-
105-
str << "\\text{Attached property ";
106-
prop->latex(str);
107-
std::string bare = Ex_as_latex(for_obj);
108-
str << " to~}" + bare + ".";
109-
return str.str();
110-
}
111-
112-
template<>
113-
std::string Property<LaTeXForm>::latex_() const;
114-
115-
template<class Prop>
116-
std::string Property<Prop>::repr_() const
117-
{
118-
// FIXME: this needs work, it does not output things which can be fed back into python.
119-
return "Property::repr: " + prop->name();
120-
}
121-
122-
template<class P>
123-
void def_prop(pybind11::module& m, const char* docstring = "")
124-
{
125-
using namespace pybind11;
126-
127-
class_<Property<P>, std::shared_ptr<Property<P>>, BaseProperty>(m, std::make_shared<P>()->name().c_str())
128-
.def(
129-
init<std::shared_ptr<Ex>, std::shared_ptr<Ex>>(),
130-
arg("ex"),
131-
arg("param"),
132-
docstring
133-
)
134-
.def("__str__", &Property<P>::str_)
135-
.def("__repr__", &Property<P>::repr_)
136-
.def("_latex_", &Property<P>::latex_);
137-
}
138-
89+
template <typename PropT, typename... ParentTs>
90+
PyProperty<PropT, ParentTs...> def_prop(pybind11::module& m, const char* docstring = "");
13991

14092
pybind11::list list_properties();
14193

142-
14394
void init_properties(pybind11::module& m);
14495

14596
}

0 commit comments

Comments
 (0)