Skip to content

Commit ca8a2b8

Browse files
Add the facility to insert arbitrary "static" C++ objects into the
python interpreter.
1 parent 065cf52 commit ca8a2b8

3 files changed

Lines changed: 74 additions & 7 deletions

File tree

pythonExample/example.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import example
2-
r=example.static.root
2+
r=example.root
33
assert r.bar.ch=='M'
44
r.bar.ch='A'
55
assert r.bar.ch=='A'
@@ -127,3 +127,11 @@
127127
# assert False
128128
#except RuntimeError:
129129
# pass
130+
131+
# check that the various aliases set up do refer to the same object
132+
example.registerRoot1()
133+
root.bar.a+=0.1
134+
assert root.bar.a==root1.bar.a
135+
assert root.bar.a==example.root.bar.a
136+
assert root.bar.a==example.root1.bar.a
137+
assert root.bar.a==example.static.root.bar.a

pythonExample/pythonExample.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@ Root root;
55
const int Foo::csi;
66
int Foo::si=25;
77

8+
void registerRoot()
9+
{
10+
addPythonObject("root1",root);
11+
addPythonObject("example.root1",root);
12+
}
13+
14+
815
BOOST_PYTHON_MODULE(example)
916
{
1017
python_t p;
1118
python<Root>(p,"");
19+
p.addFunctional("registerRoot1",registerRoot);
1220
p.addObject("static.root",root);
21+
p.addObject("root",root);
22+
addPythonObject("root",root);
1323
}
1424

python_base.h

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,18 @@ namespace classdesc
363363
std::is_copy_assignable<typename pythonDetail::MemberType<X>::T>,
364364
Not<is_const<typename pythonDetail::MemberType<X>::T>>
365365
>,void>::T
366-
addProperty(C& c,const string& d, X x) {c.def_readwrite(d.c_str(),x);}
366+
addProperty(C& c,const string& d, X x) {
367+
c.def_readwrite(d.c_str(),x);
368+
}
367369
template <class C,class X>
368370
typename enable_if<
369371
Or<
370372
Not<std::is_copy_assignable<typename pythonDetail::MemberType<X>::T>>,
371373
is_const<typename pythonDetail::MemberType<X>::T>
372374
>,void>::T
373-
addProperty(C& c,const string& d, X x) {c.def_readonly(d.c_str(),x);}
375+
addProperty(C& c,const string& d, X x) {
376+
c.def_readonly(d.c_str(),x);
377+
}
374378
};
375379

376380
template <class T, bool copiable> struct Class;
@@ -449,24 +453,34 @@ namespace classdesc
449453
}
450454

451455
template <class T>
452-
void addObject(const string& d, T& o) {
456+
typename enable_if<std::is_copy_assignable<T>,void>::T
457+
addObject(const string& d, T& o) {
458+
using namespace boost::python;
453459
checkScope(d);
454460

455461
if (!scopeStack.empty())
456462
scopeStack.back().object.def_readwrite(tail(d).c_str(),o);
463+
else
464+
extract<dict>(scope().attr("__dict__"))()[tail(d).c_str()]=ptr(&o);
457465
}
458466
template <class T>
459-
void addObject(const string& d, const T& o) {
467+
typename enable_if<Not<std::is_copy_assignable<T>>,void>::T
468+
addObject(const string& d, const T& o) {
469+
using namespace boost::python;
460470
checkScope(d);
461-
scopeStack.back().object.def_readonly(tail(d).c_str(),o);
471+
if (!scopeStack.empty())
472+
scopeStack.back().object.def_readonly(tail(d).c_str(),o);
473+
else
474+
extract<dict>(scope().attr("__dict__"))()[tail(d).c_str()]=ptr(&o);
462475
}
463476

464477
template <class F>
465478
typename enable_if<Not<is_pointer<typename functional::Return<F>::T>>, void>::T
466479
addFunctional(const string& d, F f) {
467480
checkScope(d);
468481
boost::python::def(tail(d).c_str(),f);
469-
scopeStack.back().object.staticmethod(tail(d).c_str());
482+
if (!scopeStack.empty())
483+
scopeStack.back().object.staticmethod(tail(d).c_str());
470484
}
471485

472486
// ignore pointer returns, as we don't know anything about the object being pointed to.
@@ -736,6 +750,41 @@ namespace classdesc
736750
}
737751
}
738752
}
753+
754+
/// add a python reference to a C++ static object
755+
/// path is a '.' delimited name qualified by namespaces
756+
/// if no '.' present, object is inserted into global namespace
757+
758+
/// Lifetime of \a object should exceed that of any reference to it
759+
/// in Python, so best used for logn lived objects. You can
760+
/// programmatically delete the python reference by call "del
761+
/// objectname" using boost::python::exec
762+
763+
/// NB: this function has to be run after the requested module is
764+
/// created. That means in particular, it cannot be run in the
765+
/// initialiser of static variable, nor within the initialiser of
766+
/// the module in question. Please use python_t::addObject()
767+
/// instead. It can be used to insert an object into the global
768+
/// module, however within a module initialiser.
769+
template <class T>
770+
void addPythonObject(const std::string& path, T& object)
771+
{
772+
using namespace boost::python;
773+
auto lastDot=path.rfind('.');
774+
string module, name;
775+
if (lastDot==std::string::npos)
776+
{
777+
module="__main__";
778+
name=path;
779+
}
780+
else
781+
{
782+
module=path.substr(0,lastDot);
783+
name=path.substr(lastDot+1);
784+
}
785+
dict the_dict=extract<dict>(import(module.c_str()).attr("__dict__"));
786+
the_dict[name]=ptr(&object);
787+
}
739788
}
740789

741790
namespace classdesc_access

0 commit comments

Comments
 (0)