From 03dbf0387a7f2a2041f5d76d688da35bf7dbed7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20K=C3=B6the?= Date: Fri, 10 Nov 2000 11:44:42 +0000 Subject: [PATCH] added new feature: def_raw() [SVN r8162] --- class_wrapper.h | 7 +++++++ extclass.h | 33 ++++++++++++++++++--------------- extclass_demo.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- functions.h | 27 +++++++++++++++++++++++++++ gen_extclass.py | 9 +++++++++ module.cpp | 5 +++++ module.h | 4 ++++ test_extclass.py | 18 ++++++++++++++++++ 8 files changed, 130 insertions(+), 16 deletions(-) diff --git a/class_wrapper.h b/class_wrapper.h index 1bd66a05..ac2e208e 100644 --- a/class_wrapper.h +++ b/class_wrapper.h @@ -26,6 +26,13 @@ class ClassWrapper void def(const Signature& signature) { m_class->def(signature); } + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'Tuple const &' and 'Dict const &' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + void def_raw(RawArgumentsFunction::PtrFun fn, const char* name) + { m_class->def_raw(fn, name); } + // define member functions. In fact this works for free functions, too - // they act like static member functions, or if they start with the // appropriate self argument (as a pointer or reference), they can be used diff --git a/extclass.h b/extclass.h index bf91c198..66e8bbe0 100644 --- a/extclass.h +++ b/extclass.h @@ -74,9 +74,7 @@ class ExtensionClassBase : public Class { public: ExtensionClassBase(const char* name); - void set_attribute(const char* name, PyObject* x); - void set_attribute(const char* name, Ptr x); - + public: // the purpose of try_class_conversions() and its related functions // is explained in extclass.cpp @@ -84,6 +82,9 @@ class ExtensionClassBase : public Class void* try_base_class_conversions(InstanceHolderBase*) const; void* try_derived_class_conversions(InstanceHolderBase*) const; + void set_attribute(const char* name, PyObject* x_); + void set_attribute(const char* name, Ptr x); + private: virtual void* extract_object_from_holder(InstanceHolderBase* v) const = 0; virtual std::vector const& base_classes() const = 0; @@ -137,7 +138,7 @@ class PyExtensionClassConverters // Get an object which can be used to convert T to/from python. This is used // as a kind of concept check by the global template // - // from_python(PyObject*, py::Type) + // PyObject* to_python(const T& x) // // below this class, to prevent the confusing messages that would otherwise // pop up. Now, if T hasn't been wrapped as an extension class, the user @@ -149,12 +150,12 @@ class PyExtensionClassConverters } // This is a member function because in a conforming implementation, friend - // functions defined inline in the class body are all instantiated as soon + // funcitons defined inline in the class body are all instantiated as soon // as the enclosing class is instantiated. If T is not copyable, that causes // a compiler error. Instead, we access this function through the global // template // - // from_python(PyObject*, py::Type) + // PyObject* to_python(const T& x) // // defined below this class. Since template functions are instantiated only // on demand, errors will be avoided unless T is noncopyable and the user @@ -233,15 +234,7 @@ class PyExtensionClassConverters friend const T* from_python(PyObject* p, py::Type) { return from_python(p, py::Type()); } - // Convert to const T* const& - friend const T* from_python(PyObject* p, py::Type) - { return from_python(p, py::Type()); } - - // Convert to T* const& - friend T* from_python(PyObject* p, py::Type) - { return from_python(p, py::Type()); } - - // Convert to T& + // Convert to T& friend T& from_python(PyObject* p, py::Type) { return *py::check_non_null(from_python(p, py::Type())); } @@ -369,6 +362,15 @@ class ExtensionClass Signature0())))))); } + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'Tuple const &' and 'Dict const &' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + void def_raw(RawArgumentsFunction::PtrFun fn, const char* name) + { + this->add_method(new RawArgumentsFunction(fn), name); + } + // define member functions. In fact this works for free functions, too - // they act like static member functions, or if they start with the // appropriate self argument (as a pointer), they can be used just like @@ -683,3 +685,4 @@ std::vector ClassRegistry::static_derived_class } // namespace py #endif // EXTENSION_CLASS_DWA052000_H_ + diff --git a/extclass_demo.cpp b/extclass_demo.cpp index 94062b54..a67688d3 100644 --- a/extclass_demo.cpp +++ b/extclass_demo.cpp @@ -549,6 +549,39 @@ struct A_callback : A1 { PyObject* m_self; }; +/************************************************************/ +/* */ +/* RawTest */ +/* (test passing of raw arguments to C++) */ +/* */ +/************************************************************/ + +struct RawTest +{ + RawTest(int i) : i_(i) {} + + int i_; +}; + +template class py::ExtensionClass; + +PyObject * raw(py::Tuple const & args, py::Dict const & keywords) +{ + if(args.size() != 2 || keywords.size() != 2) + { + PyErr_SetString(PyExc_TypeError, "wrong number of arguments"); + throw py::ArgumentError(); + } + + RawTest * first = from_python(args[0].get(), py::Type()); + int second = from_python(args[1].get(), py::Type()); + + int third = from_python(keywords[py::String("third")].get(), py::Type()); + int fourth = from_python(keywords[py::String("fourth")].get(), py::Type()); + + return to_python(first->i_ + second + third + fourth); +} + /************************************************************/ /* */ /* Ratio */ @@ -636,6 +669,8 @@ const Record* get_record() return &v; } +template class py::ExtensionClass; // explicitly instantiate + } // namespace extclass_demo #ifndef PY_NO_INLINE_FRIENDS_IN_NAMESPACE @@ -799,7 +834,7 @@ void init_module(py::Module& m) b2_class.def(py::Constructor<>()); b2_class.def(&B2::overrideA1, "overrideA1"); b2_class.def(&B2::inheritB2, "inheritB2"); - + m.def(call_overrideA1, "call_overrideA1"); m.def(call_overrideB1, "call_overrideB1"); m.def(call_inheritA1, "call_inheritA1"); @@ -812,6 +847,12 @@ void init_module(py::Module& m) m.def(factoryB1asA2, "factoryB1asA2"); m.def(factoryB1asB1, "factoryB1asB1"); m.def(factoryCasB1, "factoryCasB1"); + + py::ClassWrapper rawtest_class(m, "RawTest"); + rawtest_class.def(py::Constructor()); + rawtest_class.def_raw(&raw, "raw"); + + m.def_raw(&raw, "raw"); } void init_module() diff --git a/functions.h b/functions.h index da25e145..eba23f89 100644 --- a/functions.h +++ b/functions.h @@ -75,6 +75,33 @@ struct WrappedFunctionPointer : Function const PtrFun m_pf; }; +// RawArgumentsFunction +// A function that passes the Python argument tuple and keyword dictionary +// verbatim to C++ (useful for customized argument parsing and variable +// argument lists) +struct RawArgumentsFunction : Function +{ + typedef PyObject * (*PtrFun)(Tuple const &, Dict const &); + + RawArgumentsFunction(PtrFun pf) + : m_pf(pf) {} + + private: + PyObject* do_call(PyObject* args, PyObject* keywords) const + { + return (*m_pf)(Tuple(Ptr(args, Ptr::new_ref)), + keywords ? + Dict(Ptr(keywords, Ptr::new_ref)) : + Dict()); + } + + const char* description() const + { return typeid(PtrFun).name(); } + + private: + const PtrFun m_pf; +}; + // virtual_function<> -- // A virtual function with a default implementation wrapped and presented // to Python as a callable object. diff --git a/gen_extclass.py b/gen_extclass.py index d0891fc4..3569dc68 100644 --- a/gen_extclass.py +++ b/gen_extclass.py @@ -367,6 +367,15 @@ class ExtensionClass """, args) + """ + // define a function that passes Python arguments and keywords + // to C++ verbatim (as a 'Tuple const &' and 'Dict const &' + // respectively). This is useful for manual argument passing. + // It's also the only possibility to pass keyword arguments to C++. + void def_raw(RawArgumentsFunction::PtrFun fn, const char* name) + { + this->add_method(new RawArgumentsFunction(fn), name); + } + // define member functions. In fact this works for free functions, too - // they act like static member functions, or if they start with the // appropriate self argument (as a pointer), they can be used just like diff --git a/module.cpp b/module.cpp index f4e9f91f..6bd4d07e 100644 --- a/module.cpp +++ b/module.cpp @@ -34,6 +34,11 @@ void Module::add(PyTypeObject* x, const char* name /*= 0*/) name ? name : x->tp_name); } +void Module::def_raw(RawFunctionPtr fn, const char* name) +{ + add(new RawArgumentsFunction(fn), name); +} + PyMethodDef Module::initial_methods[] = { { 0, 0, 0, 0 } }; } diff --git a/module.h b/module.h index 29b98fdf..0215622a 100644 --- a/module.h +++ b/module.h @@ -19,6 +19,8 @@ class ExtensionType; class Module { + typedef PyObject * (*RawFunctionPtr)(py::Tuple const &, py::Dict const &); + public: Module(const char* name); @@ -28,6 +30,8 @@ public: void add(Ptr x, const char*name); + void def_raw(RawFunctionPtr fn, const char* name); + template void def(Fn fn, const char* name) { diff --git a/test_extclass.py b/test_extclass.py index f20e9553..4a274acf 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -842,6 +842,24 @@ test inheritB2 >>> db2.inheritB2() 'B2::inheritB2' +========= test the new def_raw() feature ========== + + >>> r = RawTest(1) + >>> raw(r,1,third=1,fourth=1) + 4 + >>> r.raw(1,third=1,fourth=1) + 4 + >>> raw(r,1,third=1,f=1) + Traceback (innermost last): + KeyError: fourth + >>> raw(r,1,third=1) + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw(r,1) + Traceback (innermost last): + TypeError: wrong number of arguments + + ========= Prove that the "phantom base class" issue is resolved ========== >>> assert pa1_a1.__class__ == A1