Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r62162 - in sandbox/python_extensions: boost/python libs/python/test site_scons
From: talljimbo_at_[hidden]
Date: 2010-05-23 04:10:17


Author: jbosch
Date: 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
New Revision: 62162
URL: http://svn.boost.org/trac/boost/changeset/62162

Log:
boost.python extensions - basic functionality and tests for const_aware_class done; still need to add result converter for shared_ptr<const T>, more rigorous tests, and move the registry into a separate library.
Added:
   sandbox/python_extensions/boost/python/const_aware_class.hpp (contents, props changed)
   sandbox/python_extensions/libs/python/test/const_aware.cpp (contents, props changed)
   sandbox/python_extensions/libs/python/test/const_aware.py (contents, props changed)
Text files modified:
   sandbox/python_extensions/libs/python/test/SConscript | 6 +++++-
   sandbox/python_extensions/site_scons/scons_tools.py | 25 +++++++++++++++----------
   2 files changed, 20 insertions(+), 11 deletions(-)

Added: sandbox/python_extensions/boost/python/const_aware_class.hpp
==============================================================================
--- (empty file)
+++ sandbox/python_extensions/boost/python/const_aware_class.hpp 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
@@ -0,0 +1,403 @@
+#ifndef BOOST_PYTHON_CONST_AWARE_CLASS_HPP
+#define BOOST_PYTHON_CONST_AWARE_CLASS_HPP
+
+#include <boost/python.hpp>
+#include <boost/function_types/parameter_types.hpp>
+#include <boost/mpl/front.hpp>
+#include <boost/type_traits/is_const.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <boost/static_assert.hpp>
+
+namespace boost {
+namespace python {
+
+namespace const_aware_registry {
+
+BOOST_PYTHON_DECL PyTypeObject * lookup(PyTypeObject * non_const);
+
+BOOST_PYTHON_DECL void insert(object const & non_const, object const & const_);
+
+} // namespace boost::python::const_aware_registry
+
+namespace objects {
+
+template <typename T, typename Holder>
+struct make_const_ptr_instance
+ : make_instance_impl<T, Holder, make_const_ptr_instance<T,Holder> >
+{
+
+ template <typename Arg>
+ static inline Holder* construct(void* storage, PyObject*, Arg& x) {
+ return new (storage) Holder(x);
+ }
+
+ template <typename Ptr>
+ static inline PyTypeObject* get_class_object(Ptr const& x) {
+ PyTypeObject * non_const = make_ptr_instance<T,Holder>::get_class_object(x);
+ return const_aware_registry::lookup(non_const);
+ }
+
+#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
+ static inline PyTypeObject const* get_pytype()
+ {
+ return converter::registered<T>::converters.get_class_object();
+ }
+#endif
+};
+
+} // namespace boost::python::objects
+
+namespace detail {
+
+template <typename F>
+struct is_const_method : public
+boost::is_const<
+ typename boost::remove_reference<
+ typename boost::mpl::front<
+ boost::function_types::parameter_types<F>
+ >::type
+ >::type
+ >
+{};
+
+struct make_const_reference_holder {
+
+ template <typename T>
+ static PyObject * execute(T* p) {
+ typedef objects::pointer_holder<T*,T> holder_t;
+ T* q = const_cast<T*>(p);
+ return objects::make_const_ptr_instance<T, holder_t>::execute(q);
+ }
+
+};
+
+} // namespace boost::python::detail
+
+struct const_reference_existing_object {
+
+ template <class T>
+ struct apply
+ {
+ BOOST_STATIC_CONSTANT(
+ bool, ok = is_pointer<T>::value || is_reference<T>::value);
+
+ typedef typename mpl::if_c<
+ ok
+ , to_python_indirect<T, detail::make_const_reference_holder>
+ , detail::reference_existing_object_requires_a_pointer_or_reference_return_type<T>
+ >::type type;
+ };
+
+};
+
+template <std::size_t owner_arg = 1, class BasePolicy_ = default_call_policies>
+struct return_internal_const_reference
+ : with_custodian_and_ward_postcall<0, owner_arg, BasePolicy_>
+{
+ private:
+ BOOST_STATIC_CONSTANT(bool, legal = owner_arg > 0);
+ public:
+ typedef typename mpl::if_c<
+ legal
+ , const_reference_existing_object
+ , detail::return_internal_reference_owner_arg_must_be_greater_than_zero<owner_arg>
+ >::type result_converter;
+};
+
+template <
+ typename W,
+ typename X1 = detail::not_specified,
+ typename X2 = detail::not_specified,
+ typename X3 = detail::not_specified
+ >
+class const_aware_class {
+public:
+
+ typedef typename objects::class_metadata<W,X1,X2,X3> metadata;
+ typedef const_aware_class<W,X1,X2,X3> self;
+
+ typedef class_<W,X1,X2,X3> const_class_t;
+ typedef class_< W, typename metadata::held_type_arg, typename metadata::bases, boost::noncopyable
+ > non_const_class_t;
+
+ const_class_t const_class;
+ non_const_class_t non_const_class;
+
+public: // constructors
+
+ const_aware_class(char const * non_const_name, char const * const_name, char const* doc = 0) :
+ const_class(const_name, doc), non_const_class(non_const_name, doc) { _register(); }
+
+ const_aware_class(char const * non_const_name, char const * const_name, no_init_t n) :
+ const_class(const_name, n), non_const_class(non_const_name, n) { _register(); }
+
+ const_aware_class(char const * non_const_name, char const * const_name, char const * doc, no_init_t n) :
+ const_class(const_name, doc, n), non_const_class(non_const_name, doc, n) { _register(); }
+
+ template <typename DerivedT>
+ const_aware_class(char const * non_const_name, char const * const_name, init_base<DerivedT> const & i) :
+ const_class(const_name, i), non_const_class(non_const_name, i) { _register(); }
+
+ template <typename DerivedT>
+ const_aware_class(char const * non_const_name, char const * const_name,
+ char const * doc, init_base<DerivedT> const & i) :
+ const_class(const_name, doc, i), non_const_class(non_const_name, doc, i) { _register(); }
+
+public: // member functions
+
+ // Generic visitation
+ template <class Derived>
+ self& def(def_visitor<Derived> const& visitor)
+ {
+ visitor.visit(non_const_class);
+ visitor.visit(const_class);
+ return *this;
+ }
+
+ // Wrap a member function or a non-member function which can take
+ // a T, T cv&, or T cv* as its first parameter, a callable
+ // python object, or a generic visitor.
+ template <class F>
+ self& def(char const* name, F f)
+ {
+ this->def_impl(
+ detail::unwrap_wrapper((W*)0)
+ , name, f, detail::def_helper<char const*>(0), &f);
+ return *this;
+ }
+
+ template <class A1, class A2>
+ self& def(char const* name, A1 a1, A2 const& a2)
+ {
+ this->def_maybe_overloads(name, a1, a2, &a2);
+ return *this;
+ }
+
+ template <class Fn, class A1, class A2>
+ self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2)
+ {
+ // The arguments are definitely:
+ // def(name, function, policy, doc_string)
+ // def(name, function, doc_string, policy)
+
+ this->def_impl(
+ detail::unwrap_wrapper((W*)0)
+ , name, fn
+ , detail::def_helper<A1,A2>(a1,a2)
+ , &fn);
+
+ return *this;
+ }
+
+ template <class Fn, class A1, class A2, class A3>
+ self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ this->def_impl(
+ detail::unwrap_wrapper((W*)0)
+ , name, fn
+ , detail::def_helper<A1,A2,A3>(a1,a2,a3)
+ , &fn);
+
+ return *this;
+ }
+
+public:
+
+ template <typename D>
+ self & def_readonly(char const* name, D const & d, char const* doc=0) {
+ non_const_class.def_readonly(name, d, doc);
+ const_class.def_readonly(name, d, doc);
+ return *this;
+ }
+
+ template <typename D>
+ self & def_readwrite(char const* name, D const & d, char const* doc=0) {
+ non_const_class.def_readwrite(name, d, doc);
+ const_class.def_readonly(name, d, doc);
+ return *this;
+ }
+
+ template <typename D>
+ self & def_readonly(char const* name, D & d, char const* doc=0) {
+ non_const_class.def_readonly(name, d, doc);
+ const_class.def_readonly(name, d, doc);
+ return *this;
+ }
+
+ template <typename D>
+ self & def_readwrite(char const* name, D & d, char const* doc=0) {
+ non_const_class.def_readwrite(name, d, doc);
+ const_class.def_readonly(name, d, doc);
+ return *this;
+ }
+
+ template <class Get>
+ self & add_property(char const* name, Get fget, char const* docstr = 0) {
+ non_const_class.add_property(name, fget, docstr);
+ const_class.add_property(name, fget, docstr);
+ return *this;
+ }
+
+ template <class Get, class Set>
+ self & add_property(char const* name, Get fget, Set fset, char const* docstr = 0) {
+ non_const_class.add_property(name, fget, fset, docstr);
+ const_class.add_property(name, fget, docstr);
+ return *this;
+ }
+
+ template <class Get>
+ self & add_static_property(char const* name, Get fget) {
+ non_const_class.add_static_property(name, fget);
+ const_class.add_static_property(name, fget);
+ return *this;
+ }
+
+ template <class Get, class Set>
+ self & add_static_property(char const* name, Get fget, Set fset) {
+ non_const_class.add_static_property(name, fget, fset);
+ const_class.add_static_property(name, fget, fset);
+ return *this;
+ }
+
+ template <class U>
+ self & setattr(char const* name, U const& x) {
+ non_const_class.setattr(name, x);
+ const_class.setattr(name, x);
+ return *this;
+ }
+
+ self & enable_pickling() {
+ non_const_class.enable_pickling();
+ const_class.enable_pickling();
+ return *this;
+ }
+
+ self & staticmethod(char const* name) {
+ non_const_class.staticmethod(name);
+ const_class.static_method(name);
+ return *this;
+ }
+
+private:
+
+ void _register() {
+ const_aware_registry::insert(non_const_class, const_class);
+ }
+
+ //
+ // These two overloads discriminate between def() as applied to a
+ // generic visitor and everything else.
+ //
+ // @group def_impl {
+ template <class T, class Helper, class LeafVisitor, class Visitor>
+ inline void def_impl(
+ T*
+ , char const* name
+ , LeafVisitor
+ , Helper const& helper
+ , def_visitor<Visitor> const* v
+ )
+ {
+ v->visit(non_const_class, name, helper);
+ v->visit(const_class, name, helper);
+ }
+
+ template <class T, class Fn, class Helper>
+ inline void def_impl(
+ T*
+ , char const* name
+ , Fn fn
+ , Helper const& helper
+ , ...
+ )
+ {
+ object method =
+ make_function(
+ fn
+ , helper.policies()
+ , helper.keywords()
+ , detail::get_signature(fn, (T*)0)
+ );
+ objects::add_to_namespace(non_const_class, name, method, helper.doc());
+
+ if (detail::is_const_method<Fn>::value) {
+ objects::add_to_namespace(const_class, name, method, helper.doc());
+ }
+
+ }
+
+ //
+ // These two overloads handle the definition of default
+ // implementation overloads for virtual functions. The second one
+ // handles the case where no default implementation was specified.
+ //
+ // @group def_default {
+ template <class Fn, class Helper>
+ inline void def_default(
+ char const* name
+ , Fn
+ , Helper const& helper
+ , mpl::bool_<true>)
+ {
+ detail::error::virtual_function_default<W,Fn>::must_be_derived_class_member(
+ helper.default_implementation());
+
+ object default_method = make_function(
+ helper.default_implementation(), helper.policies(), helper.keywords()
+ );
+
+ objects::add_to_namespace(non_const_class, name, default_method);
+
+ if (detail::is_const_method<Fn>::value) {
+ objects::add_to_namespace(const_class, name, default_method);
+ }
+
+ }
+
+ template <class Fn, class Helper>
+ inline void def_default(char const*, Fn, Helper const&, mpl::bool_<false>)
+ { }
+ // }
+
+ //
+ // These two overloads discriminate between def() as applied to
+ // regular functions and def() as applied to the result of
+ // BOOST_PYTHON_FUNCTION_OVERLOADS(). The final argument is used to
+ // discriminate.
+ //
+ // @group def_maybe_overloads {
+ template <class OverloadsT, class SigT>
+ void def_maybe_overloads(
+ char const* name
+ , SigT sig
+ , OverloadsT const& overloads
+ , detail::overloads_base const*)
+
+ {
+ BOOST_STATIC_ASSERT(sizeof(SigT) < 0); // No support for overload macros (yet).
+ }
+
+ template <class Fn, class A1>
+ void def_maybe_overloads(
+ char const* name
+ , Fn fn
+ , A1 const& a1
+ , ...)
+ {
+ this->def_impl(
+ detail::unwrap_wrapper((W*)0)
+ , name
+ , fn
+ , detail::def_helper<A1>(a1)
+ , &fn
+ );
+
+ }
+ // }
+};
+
+} // namespace boost::python
+} // namespace boost
+
+
+#endif // !BOOST_PYTHON_CONST_AWARE_CLASS_HPP

Modified: sandbox/python_extensions/libs/python/test/SConscript
==============================================================================
--- sandbox/python_extensions/libs/python/test/SConscript (original)
+++ sandbox/python_extensions/libs/python/test/SConscript 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
@@ -1,6 +1,10 @@
 Import("bp_extensions_env")
 
 test_mod = bp_extensions_env.SharedLibrary("test_mod", "test_mod.cpp", SHLIBPREFIX="")
-test = bp_extensions_env.PythonUnitTest("test_script.py", test_mod)
+const_aware_mod = bp_extensions_env.SharedLibrary("const_aware", "const_aware.cpp", SHLIBPREFIX="")
+test = (
+ bp_extensions_env.PythonUnitTest("test_script.py", test_mod)
+ + bp_extensions_env.PythonUnitTest("const_aware.py", const_aware_mod)
+ )
 
 Return("test")

Added: sandbox/python_extensions/libs/python/test/const_aware.cpp
==============================================================================
--- (empty file)
+++ sandbox/python_extensions/libs/python/test/const_aware.cpp 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
@@ -0,0 +1,71 @@
+#include <boost/python.hpp>
+#include <boost/python/const_aware_class.hpp>
+
+#include <iostream>
+#include <map>
+
+namespace bp = boost::python;
+
+namespace boost { namespace python { namespace const_aware_registry {
+
+namespace {
+typedef std::map<PyTypeObject*,PyTypeObject*> registry_t;
+registry_t registry;
+}
+
+PyTypeObject * lookup(PyTypeObject* non_const) {
+ registry_t::const_iterator iter = registry.find(non_const);
+ if (iter != registry.end()) return iter->second;
+ return NULL;
+}
+
+void insert(object const & non_const, object const & const_) {
+ registry.insert(
+ registry_t::value_type(
+ reinterpret_cast<PyTypeObject*>(non_const.ptr()),
+ reinterpret_cast<PyTypeObject*>(const_.ptr())
+ )
+ );
+}
+
+}}}
+
+class Example {
+public:
+ std::size_t get_address() const { return std::size_t(this); }
+
+ bool non_const_method() { return true; }
+
+ bool const_method() const { return true; }
+};
+
+class Owner {
+public:
+ Example by_value() const { return _example; }
+ Example const by_const_value() const { return _example; }
+ Example & by_reference() { return _example; }
+ Example const & by_const_reference() const { return _example; }
+private:
+ Example _example;
+};
+
+static void export_module() {
+
+ bp::const_aware_class<Example>("Example", "FrozenExample")
+ .add_property("address", &Example::get_address)
+ .def("non_const_method", &Example::non_const_method)
+ .def("const_method", &Example::const_method)
+ ;
+
+ bp::class_<Owner>("Owner")
+ .def("by_value", &Owner::by_value)
+ .def("by_const_value", &Owner::by_const_value)
+ .def("by_reference", &Owner::by_reference, bp::return_internal_reference<>())
+ .def("by_const_reference", &Owner::by_const_reference, bp::return_internal_const_reference<>())
+ ;
+
+}
+
+BOOST_PYTHON_MODULE(const_aware) {
+ export_module();
+}

Added: sandbox/python_extensions/libs/python/test/const_aware.py
==============================================================================
--- (empty file)
+++ sandbox/python_extensions/libs/python/test/const_aware.py 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
@@ -0,0 +1,31 @@
+import const_aware
+import unittest
+
+class TestConstAware(unittest.TestCase):
+
+ def setUp(self):
+ self.owner = const_aware.Owner()
+
+ def testByValue(self):
+ by_value = self.owner.by_value();
+ by_const_value = self.owner.by_const_value();
+ self.assertEqual(type(by_value), const_aware.Example)
+ self.assertNotEqual(by_value.address, by_const_value.address)
+ self.assert_(by_value.const_method())
+ self.assert_(by_const_value.const_method())
+ self.assert_(by_value.non_const_method())
+ self.assert_(by_const_value.non_const_method())
+
+ def testByReference(self):
+ by_reference = self.owner.by_reference();
+ by_const_reference = self.owner.by_const_reference();
+ self.assertEqual(type(by_reference), const_aware.Example)
+ self.assertEqual(type(by_const_reference), const_aware.FrozenExample)
+ self.assertEqual(by_reference.address, by_const_reference.address)
+ self.assert_(by_reference.const_method())
+ self.assert_(by_const_reference.const_method())
+ self.assert_(by_reference.non_const_method())
+ self.assertFalse(hasattr(by_const_reference,"non_const_method"))
+
+if __name__=="__main__":
+ unittest.main()

Modified: sandbox/python_extensions/site_scons/scons_tools.py
==============================================================================
--- sandbox/python_extensions/site_scons/scons_tools.py (original)
+++ sandbox/python_extensions/site_scons/scons_tools.py 2010-05-23 04:10:14 EDT (Sun, 23 May 2010)
@@ -82,7 +82,6 @@
 
     def _check(self):
         env = MakeEnvironment()
- context = scons.Configure(env)
         try:
             from distutils.sysconfig import get_config_vars, get_python_inc
         except ImportError:
@@ -98,7 +97,9 @@
         self._flags = [f for f in self._flags if not f.startswith("-O")]
         self._flags.append("-I%s" % get_python_inc())
         self._apply(env)
+ context = scons.Configure(env)
         if not context.CheckHeader(["Python.h"]):
+ context.Finish()
             return False
         context.Finish()
         return True
@@ -112,15 +113,15 @@
 
     def _check(self):
         env = MakeEnvironment()
- context = scons.Configure(env)
- self._apply_dependencies(context.env)
+ self._apply_dependencies(env)
         try:
             import numpy.distutils.misc_util
             self._variables = {"CPPPATH": numpy.distutils.misc_util.get_numpy_include_dirs()}
         except ImportError:
- context.Result(False)
+ context.Finish()
             return False
- self._apply(context.env)
+ self._apply(env)
+ context = scons.Configure(env)
         if not context.CheckHeader(["Python.h", "numpy/arrayobject.h"]):
             return False
         context.Finish()
@@ -139,14 +140,16 @@
 
     def _check(self):
         env = MakeEnvironment()
+ self._apply_dependencies(env)
+ self._apply(env)
         context = scons.Configure(env)
- self._apply_dependencies(context.env)
- self._apply(context.env)
         if self._headers:
             if not context.CheckHeader(self._headers, language="C++"):
+ context.Finish()
                 return False
         if self._libraries:
             if not context.CheckLib(self._libraries, language="C++"):
+ context.Finish()
                 return False
             self._variables = {"LIBS": self._libraries}
         context.Finish()
@@ -205,12 +208,12 @@
     except KeyError:
         libs = "boost_unit_test_framework"
     bin = env.Program(name, source, LIBS=libs)
- run = env.Command(".%s.succeeded" % name, name, RunProgramUnitTest)
+ run = env.Command(".%s.succeeded" % str(name), name, RunProgramUnitTest)
     env.Depends(run, bin)
     return run
 
 def PythonUnitTest(env, script, dependencies):
- run = env.Command(".%s.succeeded" % script, script, RunPythonUnitTest)
+ run = env.Command(".%s.succeeded" % str(script), script, RunPythonUnitTest)
     env.Depends(run, dependencies)
     return run
 
@@ -219,7 +222,9 @@
         database[package].apply(env)
 
 def MakeEnvironment():
- env = scons.Environment()
+ env = scons.Environment(tools = ["default", "doxygen"])
+ env.Append(CPPPATH="#include")
+ env.Append(LIBPATH="#lib")
     env.AddMethod(RecursiveInstall, "RecursiveInstall")
     env.AddMethod(SetupPackages, "SetupPackages")
     env.AddMethod(BoostUnitTest, "BoostUnitTest")


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk