diff --git a/build/filemgr.py b/build/filemgr.py new file mode 100644 index 00000000..51c87403 --- /dev/null +++ b/build/filemgr.py @@ -0,0 +1,122 @@ +bpl_src = "/libs/python/src" +bpl_tst = "/libs/python/test" +bpl_exa = "/libs/python/example" +files = ( +bpl_src + "/classes.cpp", +bpl_src + "/conversions.cpp", +bpl_src + "/extension_class.cpp", +bpl_src + "/functions.cpp", +bpl_src + "/init_function.cpp", +bpl_src + "/module_builder.cpp", +bpl_src + "/objects.cpp", +bpl_src + "/types.cpp", +bpl_src + "/cross_module.cpp", +bpl_tst + "/comprehensive.cpp", +bpl_tst + "/comprehensive.hpp", +bpl_tst + "/comprehensive.py", +bpl_tst + "/doctest.py", +bpl_exa + "/abstract.cpp", +bpl_exa + "/getting_started1.cpp", +bpl_exa + "/getting_started2.cpp", +bpl_exa + "/getting_started3.cpp", +bpl_exa + "/getting_started4.cpp", +bpl_exa + "/getting_started5.cpp", +bpl_exa + "/pickle1.cpp", +bpl_exa + "/pickle2.cpp", +bpl_exa + "/pickle3.cpp", +bpl_exa + "/test_abstract.py", +bpl_exa + "/test_getting_started1.py", +bpl_exa + "/test_getting_started2.py", +bpl_exa + "/test_getting_started3.py", +bpl_exa + "/test_getting_started4.py", +bpl_exa + "/test_getting_started5.py", +bpl_exa + "/test_pickle1.py", +bpl_exa + "/test_pickle2.py", +bpl_exa + "/test_pickle3.py", +bpl_exa + "/noncopyable.h", +bpl_exa + "/noncopyable_export.cpp", +bpl_exa + "/noncopyable_import.cpp", +bpl_exa + "/tst_noncopyable.py", +bpl_exa + "/ivect.h", +bpl_exa + "/ivect.cpp", +bpl_exa + "/dvect.h", +bpl_exa + "/dvect.cpp", +bpl_exa + "/tst_ivect.py", +bpl_exa + "/tst_dvect.py", +) + +defs = ( +"boost_python_test", +"abstract", +"getting_started1", +"getting_started2", +"getting_started3", +"getting_started4", +"getting_started5", +"pickle1", +"pickle2", +"pickle3", +"noncopyable_export", +"noncopyable_import", +"ivect", +"dvect", +) + +if (__name__ == "__main__"): + + import sys, os, string + + path = sys.argv[1] + mode = sys.argv[2] + if (not mode in ("softlinks", "unlink", "cp", "rm", "copy", "del")): + raise RuntimeError, \ + "usage: python filemgr.py path " + + translation_table = string.maketrans("/", "\\") + + if (mode == "copy"): + for fn in files: + fn = string.translate(fn, translation_table) + os.system("copy " + path + fn + " .") + + elif (mode == "cp"): + for fn in files: + os.system("cp " + path + fn + " .") + + elif (mode == "softlinks"): + for fn in files: + f = string.split(fn, "/")[-1] + if (os.access(f, os.F_OK)): + print "File exists: " + f + else: + os.system("ln -s " + path + os.sep + fn + " .") + + elif (mode in ("rm", "del")): + for fn in files: + flds = string.split(fn, "/") + try: os.unlink(flds[-1]) + except: pass + + elif (mode == "unlink"): + for fn in files: + f = string.split(fn, "/")[-1] + if (os.system("test -e " + f) == 0): + if (os.system("test -L " + f) == 0): + try: os.unlink(f) + except: pass + else: + print "Not a softlink: " + f + + if (mode in ("softlinks", "cp", "copy")): + for d in defs: + fn = d + ".def" + f = open(fn, "w") + f.write("EXPORTS\n") + f.write("\tinit" + d + "\n") + f.close() + + if (mode in ("unlink", "rm", "del")): + for d in defs: + fn = d + ".def" + try: os.unlink(fn) + except: pass diff --git a/build/vc60.mak b/build/vc60.mak new file mode 100644 index 00000000..a05d04f4 --- /dev/null +++ b/build/vc60.mak @@ -0,0 +1,129 @@ +# Usage: +# +# make copy Copy the sources and tests +# make Compile all sources +# make test Run doctest tests +# make clean Remove all object files +# make del Remove the sources and tests + +BOOST_WIN= "L:\boost" +BOOST_UNIX= /net/cci/rwgk/boost + +PYEXE= "C:\Program files\Python\python.exe" +PYINC= /I"C:\Program files\Python\include" +PYLIB= "C:\Program files\Python\libs\python15.lib" + +STDOPTS= /nologo /MD /GR /GX /FD /Zm200 +WARNOPTS= + +CPP= cl.exe +CPPOPTS= $(STLPORTINC) $(STLPORTOPTS) /I$(BOOST_WIN) $(PYINC) \ + $(STDOPTS) $(WARNOPTS) + +LD= link.exe +LDOPTS= /nologo /dll /incremental:no + +OBJ = classes.obj conversions.obj extension_class.obj functions.obj \ + init_function.obj module_builder.obj \ + objects.obj types.obj cross_module.obj + +.SUFFIXES: .obj .cpp + +all: libboost_python.a \ + boost_python_test.pyd \ + abstract.pyd \ + getting_started1.pyd getting_started2.pyd getting_started3.pyd \ + getting_started4.pyd getting_started5.pyd \ + pickle1.pyd pickle2.pyd pickle3.pyd \ + noncopyable_export.pyd noncopyable_import.pyd \ + ivect.pyd dvect.pyd + +libboost_python.a: $(OBJ) + +boost_python_test.pyd: $(OBJ) comprehensive.obj + $(LD) $(LDOPTS) $(OBJ) comprehensive.obj $(PYLIB) /export:initboost_python_test /out:"boost_python_test.pyd" + +abstract.pyd: $(OBJ) abstract.obj + $(LD) $(LDOPTS) $(OBJ) abstract.obj $(PYLIB) /export:initabstract /out:"abstract.pyd" + +getting_started1.pyd: $(OBJ) getting_started1.obj + $(LD) $(LDOPTS) $(OBJ) getting_started1.obj $(PYLIB) /export:initgetting_started1 /out:"getting_started1.pyd" + +getting_started2.pyd: $(OBJ) getting_started2.obj + $(LD) $(LDOPTS) $(OBJ) getting_started2.obj $(PYLIB) /export:initgetting_started2 /out:"getting_started2.pyd" + +getting_started3.pyd: $(OBJ) getting_started3.obj + $(LD) $(LDOPTS) $(OBJ) getting_started3.obj $(PYLIB) /export:initgetting_started3 /out:"getting_started3.pyd" + +getting_started4.pyd: $(OBJ) getting_started4.obj + $(LD) $(LDOPTS) $(OBJ) getting_started4.obj $(PYLIB) /export:initgetting_started4 /out:"getting_started4.pyd" + +getting_started5.pyd: $(OBJ) getting_started5.obj + $(LD) $(LDOPTS) $(OBJ) getting_started5.obj $(PYLIB) /export:initgetting_started5 /out:"getting_started5.pyd" + +pickle1.pyd: $(OBJ) pickle1.obj + $(LD) $(LDOPTS) $(OBJ) pickle1.obj $(PYLIB) /export:initpickle1 /out:"pickle1.pyd" + +pickle2.pyd: $(OBJ) pickle2.obj + $(LD) $(LDOPTS) $(OBJ) pickle2.obj $(PYLIB) /export:initpickle2 /out:"pickle2.pyd" + +pickle3.pyd: $(OBJ) pickle3.obj + $(LD) $(LDOPTS) $(OBJ) pickle3.obj $(PYLIB) /export:initpickle3 /out:"pickle3.pyd" + +noncopyable_export.pyd: $(OBJ) noncopyable_export.obj + $(LD) $(LDOPTS) $(OBJ) noncopyable_export.obj $(PYLIB) /export:initnoncopyable_export /out:"noncopyable_export.pyd" + +noncopyable_import.pyd: $(OBJ) noncopyable_import.obj + $(LD) $(LDOPTS) $(OBJ) noncopyable_import.obj $(PYLIB) /export:initnoncopyable_import /out:"noncopyable_import.pyd" + +ivect.pyd: $(OBJ) ivect.obj + $(LD) $(LDOPTS) $(OBJ) ivect.obj $(PYLIB) /export:initivect /out:"ivect.pyd" + +dvect.pyd: $(OBJ) dvect.obj + $(LD) $(LDOPTS) $(OBJ) dvect.obj $(PYLIB) /export:initdvect /out:"dvect.pyd" + +.cpp.obj: + $(CPP) $(CPPOPTS) /c $*.cpp + +test: + $(PYEXE) comprehensive.py --broken-auto-ptr + $(PYEXE) test_abstract.py + $(PYEXE) test_getting_started1.py + $(PYEXE) test_getting_started2.py + $(PYEXE) test_getting_started3.py + $(PYEXE) test_getting_started4.py + $(PYEXE) test_getting_started5.py + $(PYEXE) test_pickle1.py + $(PYEXE) test_pickle2.py + $(PYEXE) test_pickle3.py + +tst: + $(PYEXE) tst_noncopyable.py + $(PYEXE) tst_ivect.py + $(PYEXE) tst_dvect.py + +clean: + del *.obj + del *.lib + del *.exp + del *.idb + del *.pyd + del *.pyc + +softlinks: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) softlinks + +unlink: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) unlink + +cp: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) cp + +rm: + python $(BOOST_UNIX)/libs/python/build/filemgr.py $(BOOST_UNIX) rm + +copy: + python $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) copy + +del: + python $(BOOST_WIN)\libs\python\build\filemgr.py $(BOOST_WIN) del diff --git a/example/getting_started4.cpp b/example/getting_started4.cpp new file mode 100644 index 00000000..fe6a3421 --- /dev/null +++ b/example/getting_started4.cpp @@ -0,0 +1,101 @@ +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // A wrapper is used to define additional constructors. + // + struct vector_double_wrapper: std::vector + { + // Tell the compiler how to convert a base class object to + // this wrapper object. + vector_double_wrapper(PyObject*, const std::vector& vd) + : std::vector(vd) {} + + vector_double_wrapper(PyObject* self) + : std::vector() {} + + vector_double_wrapper(PyObject* self, int n) + : std::vector(n) {} + + vector_double_wrapper(PyObject* self, python::tuple tuple) + : std::vector(tuple.size()) + { + std::vector::iterator vd = begin(); + for (int i = 0; i < tuple.size(); i++) + vd[i] = BOOST_PYTHON_CONVERSION::from_python(tuple[i].get(), + python::type()); + } + }; + + double getitem(const std::vector& vd, std::size_t key) { + return vd[key]; + } + + void setitem(std::vector& vd, std::size_t key, double d) { + std::vector::iterator vditer = vd.begin(); + vditer[key] = d; + } + + void delitem(std::vector& vd, std::size_t key) { + std::vector::iterator vditer = vd.begin(); + vd.erase(&vditer[key]); + } + + // Convert vector_double to a regular Python tuple. + // + python::tuple as_tuple(const std::vector& vd) + { + python::tuple t(vd.size()); + for (int i = 0; i < vd.size(); i++) t.set_item(i, + python::ref(BOOST_PYTHON_CONVERSION::to_python(vd[i]))); + return t; + } + + // Function returning a vector_double object to Python. + // + std::vector foo(int n) + { + std::vector vd(n); + std::vector::iterator vditer = vd.begin(); + for (int i = 0; i < n; i++) vditer[i] = double(i); + return vd; + } + + // Same as foo(), but avoid copying on return. + // + std::auto_ptr > bar(int n) + { + std::auto_ptr > vdptr(new std::vector(n)); + std::vector::iterator vditer = vdptr->begin(); + for (int i = 0; i < n; i++) vditer[i] = double(10 * i); + return vdptr; + } +} + +BOOST_PYTHON_MODULE_INIT(getting_started4) +{ + try + { + python::module_builder this_module("getting_started4"); + + python::class_builder, vector_double_wrapper> + vector_double(this_module, "vector_double"); + + vector_double.def(python::constructor<>()); + vector_double.def(python::constructor()); + vector_double.def(python::constructor()); + vector_double.def(&std::vector::size, "__len__"); + vector_double.def(getitem, "__getitem__"); + vector_double.def(setitem, "__setitem__"); + vector_double.def(delitem, "__delitem__"); + vector_double.def(as_tuple, "as_tuple"); + + this_module.def(foo, "foo"); + this_module.def(bar, "bar"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/getting_started5.cpp b/example/getting_started5.cpp new file mode 100644 index 00000000..9e9e7c75 --- /dev/null +++ b/example/getting_started5.cpp @@ -0,0 +1,126 @@ +/* + This example shows how to convert a class from and to native + Python objects, such as tuples. + + We do not want to expose the helper class MillerIndex as an + Extension Class. However, in order to simplify the wrapper code, + we want to define from_python() and to_python() functions for + class MillerIndex. + + Consider the alternatives: + + - Expose MillerIndex as an Extension Class. + We need a constructor MillerIndex(python::tuple). + Python function calls become more complex: + foo(MillerIndex((1,2,3)) instead of foo((1,2,3)) + We need a method such as MillerIndex().as_tuple(). + + - Define a wrapper function for each function that we + want to expose, e.g.: + void add(const IndexingSet& ixset, const python::tuple PyMIx) + + The first alternative introduces a new type that the user has to + deal with. Other modules using Miller indices might organize them in + different ways, for example to increase runtime efficiency for + important procedures. This means, the user has to know how to + convert between the different kinds of Miller index representations. + This can quickly become a nuisance. Relying on native Python data + structures minimizes the number of special types the user has to + learn and convert. Of course, this argument is only valid for + small and relatively simply classes. + + If there are many member functions with MillerIndex arguments, the + second alternative is impractical, and concentrating the conversion + mechanism in one central place is essential for code + maintainability. An added benefit is that more convenient (smarter) + conversion functions can be provided without cluttering the rest of + the wrapper code. + + */ + +#include +#include +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // The helper class. + // + class MillerIndex { + public: + int v[3]; + }; + + // The main class. Imagine that there are MANY member functions + // like add() and get(). + // + class IndexingSet { + private: + std::vector VMIx; + public: + void add(const MillerIndex& MIx) { VMIx.push_back(MIx); } + MillerIndex get(std::size_t i) const { return VMIx[i]; } + }; +} + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + + // Convert a Python tuple to a MillerIndex object. + // + MillerIndex from_python(PyObject* p, python::type) + { + python::tuple tup + = python::tuple(python::ref(p, python::ref::increment_count)); + if (tup.size() != 3) { + PyErr_SetString(PyExc_ValueError, + "expecting exactly 3 values in tuple."); + throw python::error_already_set(); + } + MillerIndex result; + for (int i = 0; i < 3; i++) + result.v[i] = from_python(tup[i].get(), python::type()); + return result; + } + + // Similar conversion for MillerIndex objects passed by value. + // Not actually used, but included to show the principle. + // + MillerIndex from_python(PyObject* p, python::type) + { + return from_python(p, python::type()); + } + + // Convert a MillerIndex object to a Python tuple. + // + PyObject* to_python(const MillerIndex& hkl) + { + python::tuple result(3); + for (int i = 0; i < 3; i++) + result.set_item(i, python::ref(to_python(hkl.v[i]))); + return result.reference().release(); + } + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +BOOST_PYTHON_MODULE_INIT(getting_started5) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("getting_started5"); + + // Create the Python type object for our extension class. + python::class_builder ixset_class(this_module, "IndexingSet"); + + // Add the __init__ function. + ixset_class.def(python::constructor<>()); + // Add the member functions. + ixset_class.def(&IndexingSet::add, "add"); + ixset_class.def(&IndexingSet::get, "get"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/include/boost/python/cross_module.hpp b/include/boost/python/cross_module.hpp new file mode 100644 index 00000000..a6c32889 --- /dev/null +++ b/include/boost/python/cross_module.hpp @@ -0,0 +1,274 @@ +#ifndef CROSS_MODULE_HPP +# define CROSS_MODULE_HPP + +# include + +namespace boost { namespace python { + struct import_error : error_already_set {}; + struct export_error : error_already_set {}; +}} + +namespace boost { namespace python { namespace detail { + +// Concept: throw exception if api_major is changed +// show warning on stderr if api_minor is changed +const int export_converters_api_major = 2; +const int export_converters_api_minor = 1; +extern const char* converters_attribute_name; +void* import_converter_object(const std::string& module_name, + const std::string& py_class_name, + const std::string& attribute_name); +void check_export_converters_api(const int importing_major, + const int importing_minor, + const int imported_major, + const int imported_minor); + +}}} + +// forward declaration +namespace boost { namespace python { namespace detail { +template class import_extension_class; +}}} + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +//QUESTIONMARK +// This class is a look-alike of class python_extension_class_converters. +// Is there a formal way to ensure that the siblings stay in sync? +template +class python_import_extension_class_converters +{ + public: + + friend python_import_extension_class_converters py_extension_class_converters(boost::python::type) + { + return python_import_extension_class_converters(); + } + + PyObject* to_python(const T& x) const + { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } + + friend T* from_python(PyObject* p, boost::python::type) + { + return boost::python::detail::import_extension_class::get_converters()->T_pointer_from_python(p); + } + + // Convert to const T* + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to const T* const& + friend const T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T* const& + friend T* from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T& + friend T& from_python(PyObject* p, boost::python::type) { + return boost::python::detail::import_extension_class::get_converters()->T_reference_from_python(p); + } + + // Convert to const T& + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + // Convert to T + friend const T& from_python(PyObject* p, boost::python::type) + { return from_python(p, boost::python::type()); } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type&>) { + return boost::python::detail::import_extension_class::get_converters()->auto_ptr_reference_from_python(p); + } + + friend std::auto_ptr& from_python(PyObject* p, boost::python::type >) { + return boost::python::detail::import_extension_class::get_converters()->auto_ptr_value_from_python(p); + } + + friend const std::auto_ptr& from_python(PyObject* p, boost::python::type&>) { + return boost::python::detail::import_extension_class::get_converters()->auto_ptr_value_from_python(p); + } + + friend PyObject* to_python(std::auto_ptr x) { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) { + return boost::python::detail::import_extension_class::get_converters()->shared_ptr_reference_from_python(p); + } + + friend boost::shared_ptr& from_python(PyObject* p, boost::python::type >) { + return boost::python::detail::import_extension_class::get_converters()->shared_ptr_value_from_python(p); + } + + friend const boost::shared_ptr& from_python(PyObject* p, boost::python::type&>) { + return boost::python::detail::import_extension_class::get_converters()->shared_ptr_value_from_python(p); + } + + friend PyObject* to_python(boost::shared_ptr x) { + return boost::python::detail::import_extension_class::get_converters()->to_python(x); + } +}; + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(python_import_extension_class_converters); + +// A pointer to this class is exported/imported via the Python API. +// All functions are virtual. This is, what we really export/import +// is essentially just a pointer to a vtbl. +template +struct export_converter_object_base +{ + virtual const int get_api_major() const { + return detail::export_converters_api_major; } + virtual const int get_api_minor() const { + return detail::export_converters_api_minor; } + virtual PyObject* to_python(const T& x) = 0; + virtual PyObject* to_python(std::auto_ptr x) = 0; + virtual PyObject* to_python(boost::shared_ptr x) = 0; + virtual T* T_pointer_from_python(PyObject* obj) = 0; + virtual T& T_reference_from_python(PyObject* obj) = 0; + virtual std::auto_ptr& auto_ptr_reference_from_python(PyObject* obj) = 0; + virtual std::auto_ptr auto_ptr_value_from_python(PyObject* obj) = 0; + virtual boost::shared_ptr& shared_ptr_reference_from_python(PyObject* obj) = 0; + virtual boost::shared_ptr shared_ptr_value_from_python(PyObject* obj) = 0; +}; + +// Converters to be used if T is not copyable. +template +struct export_converter_object_noncopyable : export_converter_object_base +{ + virtual PyObject* to_python(const T& x) { + PyErr_SetString(PyExc_RuntimeError, + "to_python(const T&) converter not exported"); + throw import_error(); + } + virtual PyObject* to_python(std::auto_ptr x) { + return BOOST_PYTHON_CONVERSION::to_python(x); + } + virtual PyObject* to_python(boost::shared_ptr x) { + return BOOST_PYTHON_CONVERSION::to_python(x); + } + virtual T* T_pointer_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::from_python(obj, boost::python::type()); + } + virtual T& T_reference_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::from_python(obj, boost::python::type()); + } + virtual std::auto_ptr& auto_ptr_reference_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::python_extension_class_converters::smart_ptr_reference(obj, boost::python::type >()); + } + virtual std::auto_ptr auto_ptr_value_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::python_extension_class_converters::smart_ptr_value(obj, boost::python::type >()); + } + virtual boost::shared_ptr& shared_ptr_reference_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::python_extension_class_converters::smart_ptr_reference(obj, boost::python::type >()); + } + virtual boost::shared_ptr shared_ptr_value_from_python(PyObject* obj) { + return BOOST_PYTHON_CONVERSION::python_extension_class_converters::smart_ptr_value(obj, boost::python::type >()); + } +}; + +// The addditional to_python() converter that can be used if T is copyable. +template +struct export_converter_object : export_converter_object_noncopyable +{ + virtual PyObject* to_python(const T& x) { + BOOST_PYTHON_CONVERSION::python_extension_class_converters cv; + return cv.to_python(x); + } +}; + +namespace detail { + +//QUESTIONMARK +// A stripped-down, modified version of class extension_class. +// Would it make sense to establish a formal relationship +// between the two classes? +template +class import_extension_class + : public python_import_extension_class_converters +{ + public: + inline import_extension_class(const char* module, const char* py_class) { + m_module = module; + m_py_class = py_class; + } + + static boost::python::export_converter_object_base* get_converters(); + + private: + static std::string m_module; + static std::string m_py_class; + static boost::python::export_converter_object_base* imported_converters; +}; + +template std::string import_extension_class::m_module; +template std::string import_extension_class::m_py_class; +template +boost::python::export_converter_object_base* +import_extension_class::imported_converters = 0; + +template +boost::python::export_converter_object_base* +import_extension_class::get_converters() { + if (imported_converters == 0) { + void* cobject + = import_converter_object(m_module, m_py_class, + converters_attribute_name); + imported_converters + = static_cast*>(cobject); + check_export_converters_api( + export_converters_api_major, + export_converters_api_minor, + imported_converters->get_api_major(), + imported_converters->get_api_minor()); + } + return imported_converters; +} + +}}} // namespace boost::python::detail + +namespace boost { namespace python { + +template +void export_converters(class_builder& cb) +{ + static export_converter_object export_cvts; + cb.add( + ref(PyCObject_FromVoidPtr(reinterpret_cast(&export_cvts), NULL)), + detail::converters_attribute_name); +} + +template +void export_converters_noncopyable(class_builder& cb) +{ + static export_converter_object_noncopyable export_cvts; + cb.add( + ref(PyCObject_FromVoidPtr(reinterpret_cast(&export_cvts), NULL)), + detail::converters_attribute_name); +} + +template +class import_converters + : python_import_extension_class_converters +{ + public: + import_converters(const char* module, const char* py_class) + : m_class(new detail::import_extension_class(module, py_class)) + { } + private: + //QUESTIONMARK + //reference > m_class; + boost::shared_ptr > m_class; +}; + +}} // namespace boost::python + +#endif // CROSS_MODULE_HPP diff --git a/src/cross_module.cpp b/src/cross_module.cpp new file mode 100644 index 00000000..c14acfda --- /dev/null +++ b/src/cross_module.cpp @@ -0,0 +1,77 @@ +# include +namespace python = boost::python; +# include // MSVC6.0SP4 does not know std::fprintf +# include // MSVC6.0SP4 does not know std::strcmp + +namespace { + + PyObject* get_module_dict(const char* module_name) + { + python::ref module_obj(PyImport_ImportModule((char*) module_name)); + PyObject* module_dict = PyModule_GetDict(module_obj.get()); + if (module_dict == 0) throw python::import_error(); + return module_dict; + } +} + +namespace boost { namespace python { namespace detail { + +const char* converters_attribute_name = "__converters__"; + +void* import_converter_object(const std::string& module_name, + const std::string& py_class_name, + const std::string& attribute_name) +{ + static std::string err; + PyObject* module_dict = get_module_dict(const_cast(module_name.c_str())); + PyObject* py_class = PyDict_GetItemString(module_dict, const_cast(py_class_name.c_str())); + if (py_class == 0) { + err = std::string("module ") + module_name + " has no attribute " + py_class_name; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + python::ref c_obj(PyObject_GetAttrString(py_class, const_cast(attribute_name.c_str())), ref::null_ok); + if (c_obj.get() == 0) { + err = std::string("object ") + module_name + "." + py_class_name + + " has no attribute " + attribute_name; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + if (! PyCObject_Check(c_obj.get())) { + err = std::string("object ") + module_name + "." + py_class_name + "." + + attribute_name + " is not a PyCObject"; + PyErr_SetString(PyExc_RuntimeError, const_cast(err.c_str())); + throw python::import_error(); + } + return PyCObject_AsVoidPtr(c_obj.get()); +} + +void check_export_converters_api(const int importing_major, + const int importing_minor, + const int imported_major, + const int imported_minor) +{ + if (importing_major != imported_major) { + // Python uses fprintf(stderr, ...) for API warnings. + fprintf(stderr, + "Fatal: export_converters_api mismatch:" + " Importing module = %d.%d" + " Imported module = %d.%d\n", + importing_major, importing_minor, + imported_major, imported_minor); + PyErr_SetString(PyExc_RuntimeError, + "Fatal: export_converters_api mismatch"); + throw import_error(); + } + if (importing_minor != imported_minor) { + // Python uses fprintf(stderr, ...) for API warnings. + fprintf(stderr, + "Warning: export_converters_api mismatch:" + " Importing module = %d.%d" + " Imported module = %d.%d\n", + importing_major, importing_minor, + imported_major, imported_minor); + } +} + +}}} // namespace boost::python::detail