[pypy-commit] pypy refactor-buffer-api: merge default

bdkearns noreply at buildbot.pypy.org
Thu Apr 24 22:52:19 CEST 2014


Author: Brian Kearns <bdkearns at gmail.com>
Branch: refactor-buffer-api
Changeset: r70943:513cd84a914e
Date: 2014-04-24 16:51 -0400
http://bitbucket.org/pypy/pypy/changeset/513cd84a914e/

Log:	merge default

diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst
--- a/pypy/doc/cppyy.rst
+++ b/pypy/doc/cppyy.rst
@@ -560,6 +560,12 @@
   Fixing these bootstrap problems is on the TODO list.
   The global namespace is ``cppyy.gbl``.
 
+* **NULL**: Is represented as ``cppyy.gbl.nullptr``.
+  In C++11, the keyword ``nullptr`` is used to represent ``NULL``.
+  For clarity of intent, it is recommended to use this instead of ``None``
+  (or the integer ``0``, which can serve in some cases), as ``None`` is better
+  understood as ``void`` in C++.
+
 * **operator conversions**: If defined in the C++ class and a python
   equivalent exists (i.e. all builtin integer and floating point types, as well
   as ``bool``), it will map onto that python conversion.
diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst
--- a/pypy/doc/extending.rst
+++ b/pypy/doc/extending.rst
@@ -66,58 +66,26 @@
 Reflex
 ======
 
-This method is still experimental.  It adds the `cppyy`_ module.
-The method works by using the `Reflex package`_ to provide reflection
-information of the C++ code, which is then used to automatically generate
-bindings at runtime.
-From a python standpoint, there is no difference between generating bindings
-at runtime, or having them "statically" generated and available in scripts
-or compiled into extension modules: python classes and functions are always
-runtime structures, created when a script or module loads.
+The builtin `cppyy`_ module uses reflection information, provided by
+`Reflex`_ (which needs to be `installed separately`_), of C/C++ code to
+automatically generate bindings at runtime.
+In Python, classes and functions are always runtime structures, so when they
+are generated matters not for performance.
 However, if the backend itself is capable of dynamic behavior, it is a much
-better functional match to python, allowing tighter integration and more
-natural language mappings.
-Full details are `available here`_.
+better functional match, allowing tighter integration and more natural
+language mappings.
+
+The `cppyy`_ module is written in RPython, thus PyPy's JIT is able to remove
+most cross-language call overhead.
+
+`Full details`_ are `available here`_.
 
 .. _`cppyy`: cppyy.html
-.. _`reflex-support`: cppyy.html
-.. _`Reflex package`: http://root.cern.ch/drupal/content/reflex
+.. _`installed separately`: http://cern.ch/wlav/reflex-2013-08-14.tar.bz2
+.. _`Reflex`: http://root.cern.ch/drupal/content/reflex
+.. _`Full details`: cppyy.html
 .. _`available here`: cppyy.html
 
-Pros
-----
-
-The cppyy module is written in RPython, which makes it possible to keep the
-code execution visible to the JIT all the way to the actual point of call into
-C++, thus allowing for a very fast interface.
-Reflex is currently in use in large software environments in High Energy
-Physics (HEP), across many different projects and packages, and its use can be
-virtually completely automated in a production environment.
-One of its uses in HEP is in providing language bindings for CPython.
-Thus, it is possible to use Reflex to have bound code work on both CPython and
-on PyPy.
-In the medium-term, Reflex will be replaced by `cling`_, which is based on
-`llvm`_.
-This will affect the backend only; the python-side interface is expected to
-remain the same, except that cling adds a lot of dynamic behavior to C++,
-enabling further language integration.
-
-.. _`cling`: http://root.cern.ch/drupal/content/cling
-.. _`llvm`: http://llvm.org/
-
-Cons
-----
-
-C++ is a large language, and cppyy is not yet feature-complete.
-Still, the experience gained in developing the equivalent bindings for CPython
-means that adding missing features is a simple matter of engineering, not a
-question of research.
-The module is written so that currently missing features should do no harm if
-you don't use them, if you do need a particular feature, it may be necessary
-to work around it in python or with a C++ helper function.
-Although Reflex works on various platforms, the bindings with PyPy have only
-been tested on Linux.
-
 
 RPython Mixed Modules
 =====================
diff --git a/pypy/module/cppyy/src/dummy_backend.cxx b/pypy/module/cppyy/src/dummy_backend.cxx
--- a/pypy/module/cppyy/src/dummy_backend.cxx
+++ b/pypy/module/cppyy/src/dummy_backend.cxx
@@ -38,6 +38,24 @@
 typedef std::map<cppyy_scope_t, Cppyy_PseudoClassInfo> Scopes_t;
 static Scopes_t s_scopes;
 
+class PseudoExample01 {
+public:
+    PseudoExample01() : m_somedata(-99) {}
+    PseudoExample01(int a) : m_somedata(a) {}
+    PseudoExample01(const PseudoExample01& e) : m_somedata(e.m_somedata) {}
+    PseudoExample01& operator=(const PseudoExample01& e) {
+        if (this != &e) m_somedata = e.m_somedata;
+        return *this;
+    }
+   virtual ~PseudoExample01() {}
+
+public:
+    int m_somedata;
+};
+
+static int example01_last_static_method = 0;
+static int example01_last_constructor = 0;
+
 struct Cppyy_InitPseudoReflectionInfo {
     Cppyy_InitPseudoReflectionInfo() {
         // class example01 --
@@ -46,27 +64,62 @@
 
         std::vector<Cppyy_PseudoMethodInfo> methods;
 
-        // static double staticAddToDouble(double a);
+        // ( 0) static double staticAddToDouble(double a)
         std::vector<std::string> argtypes;
         argtypes.push_back("double");
         methods.push_back(Cppyy_PseudoMethodInfo("staticAddToDouble", argtypes, "double"));
 
-        // static int staticAddOneToInt(int a);
-        // static int staticAddOneToInt(int a, int b);
+        // ( 1) static int staticAddOneToInt(int a)
+        // ( 2) static int staticAddOneToInt(int a, int b)
         argtypes.clear();
         argtypes.push_back("int");
         methods.push_back(Cppyy_PseudoMethodInfo("staticAddOneToInt", argtypes, "int"));
         argtypes.push_back("int");
         methods.push_back(Cppyy_PseudoMethodInfo("staticAddOneToInt", argtypes, "int"));
 
-        // static int staticAtoi(const char* str);
+        // ( 3) static int staticAtoi(const char* str)
         argtypes.clear();
         argtypes.push_back("const char*");
         methods.push_back(Cppyy_PseudoMethodInfo("staticAtoi", argtypes, "int"));
 
-        // static char* staticStrcpy(const char* strin);
+        // ( 4) static char* staticStrcpy(const char* strin)
         methods.push_back(Cppyy_PseudoMethodInfo("staticStrcpy", argtypes, "char*"));
 
+        // ( 5) static void staticSetPayload(payload* p, double d)
+        // ( 6) static payload* staticCyclePayload(payload* p, double d)
+        // ( 7) static payload staticCopyCyclePayload(payload* p, double d)
+        argtypes.clear();
+        argtypes.push_back("payload*");
+        argtypes.push_back("double");
+        methods.push_back(Cppyy_PseudoMethodInfo("staticSetPayload", argtypes, "void"));
+        methods.push_back(Cppyy_PseudoMethodInfo("staticCyclePayload", argtypes, "payload*"));
+        methods.push_back(Cppyy_PseudoMethodInfo("staticCopyCyclePayload", argtypes, "payload"));
+
+        // ( 8) static int getCount()
+        // ( 9) static void setCount(int)
+        argtypes.clear();
+        methods.push_back(Cppyy_PseudoMethodInfo("getCount", argtypes, "int"));
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("setCount", argtypes, "void"));
+
+        // cut-off is used in cppyy_is_static
+        example01_last_static_method = methods.size();
+
+        // (10) example01()
+        // (11) example01(int a)
+        argtypes.clear();
+        methods.push_back(Cppyy_PseudoMethodInfo("example01", argtypes, "constructor"));
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("example01", argtypes, "constructor"));
+
+        // cut-off is used in cppyy_is_constructor
+        example01_last_constructor = methods.size();
+
+        // (12) double addDataToDouble(double a)
+        argtypes.clear();
+        argtypes.push_back("double");
+        methods.push_back(Cppyy_PseudoMethodInfo("addDataToDouble", argtypes, "double"));
+
         Cppyy_PseudoClassInfo info(methods);
         s_scopes[(cppyy_scope_t)s_scope_id] = info;
         // -- class example01
@@ -98,47 +151,69 @@
 }
 
 
+/* memory management ------------------------------------------------------ */
+void cppyy_destruct(cppyy_type_t handle, cppyy_object_t self) {
+    if (handle == s_handles["example01"])
+       delete (PseudoExample01*)self;
+}
+
+
 /* method/function dispatching -------------------------------------------- */
-template<typename T>
-static inline T cppyy_call_T(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    T result = T();
+int cppyy_call_i(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
+    int result = 0;
     switch ((long)method) {
-    case 0:             // double staticAddToDouble(double)
-        assert(!self && nargs == 1);
-        result = ((CPPYY_G__value*)args)[0].obj.d + 0.01;
-        break;
-    case 1:             // int staticAddOneToInt(int)
+    case 1:             // static int staticAddOneToInt(int)
         assert(!self && nargs == 1);
         result = ((CPPYY_G__value*)args)[0].obj.in + 1;
         break;
-    case 2:             // int staticAddOneToInt(int, int)
+    case 2:             // static int staticAddOneToInt(int, int)
         assert(!self && nargs == 2);
         result = ((CPPYY_G__value*)args)[0].obj.in + ((CPPYY_G__value*)args)[1].obj.in + 1;
         break;
-    case 3:             // int staticAtoi(const char* str)
+    case 3:             // static int staticAtoi(const char* str)
         assert(!self && nargs == 1);
         result = ::atoi((const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
         break;
+    case 8:             // static int getCount()
+       assert(!self && nargs == 0);
+       // can't actually call this method (would need to resolve example01::count), but
+       // other than the memory tests, most tests just check for 0 at the end
+       result = 0;
+       break;
     default:
+        assert(!"method unknown in cppyy_call_i");
         break;
     }
     return result;
 }
 
-int cppyy_call_i(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    return cppyy_call_T<int>(method, self, nargs, args);
-}
-
 long cppyy_call_l(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    // char* staticStrcpy(const char* strin)
-    const char* strin = (const char*)(*(long*)&((CPPYY_G__value*)args)[0]);
-    char* strout = (char*)malloc(::strlen(strin)+1);
-    ::strcpy(strout, strin);
-    return (long)strout;
+    if ((long)method == 4) {  // static char* staticStrcpy(const char* strin)
+       const char* strin = (const char*)(*(long*)&((CPPYY_G__value*)args)[0]);
+       char* strout = (char*)malloc(::strlen(strin)+1);
+       ::strcpy(strout, strin);
+       return (long)strout;
+    }
+    assert(!"method unknown in cppyy_call_l");
+    return 0;
 }
 
 double cppyy_call_d(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    return cppyy_call_T<double>(method, self, nargs, args);
+    double result = 0.;
+    switch ((long)method) {
+    case 0:             // static double staticAddToDouble(double)
+        assert(!self && nargs == 1);
+        result = ((CPPYY_G__value*)args)[0].obj.d + 0.01;
+        break;
+    case 12:            // double addDataToDouble(double a)
+        assert(self && nargs == 1);
+        result = ((PseudoExample01*)self)->m_somedata + ((CPPYY_G__value*)args)[0].obj.d;
+        break;
+    default:
+        assert(!"method unknown in cppyy_call_d");
+        break;
+    }
+    return result;
 }
 
 char* cppyy_call_s(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
@@ -149,10 +224,31 @@
     return strout;
 }
 
+cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t handle, int nargs, void* args) {
+    void* result = 0;
+    if (handle == s_handles["example01"]) {
+        switch ((long)method) {
+        case 10:
+            assert(nargs == 0);
+            result = new PseudoExample01;
+            break;
+        case 11:
+            assert(nargs == 1);
+            result = new PseudoExample01(((CPPYY_G__value*)args)[0].obj.in);
+            break;
+        default:
+            assert(!"method unknown in cppyy_constructor");
+            break;
+        }
+    }
+    return (cppyy_object_t)result;
+}
+
 cppyy_methptrgetter_t cppyy_get_methptr_getter(cppyy_type_t /* handle */, cppyy_index_t /* method_index */) {
     return (cppyy_methptrgetter_t)0;
 }
 
+
 /* handling of function argument buffer ----------------------------------- */
 void* cppyy_allocate_function_args(size_t nargs) {
     CPPYY_G__value* args = (CPPYY_G__value*)malloc(nargs*sizeof(CPPYY_G__value));
@@ -200,7 +296,11 @@
 }   
 
 int cppyy_has_complex_hierarchy(cppyy_type_t /* handle */) {
-    return 1;
+    return 0;
+}
+
+int cppyy_num_bases(cppyy_type_t /*handle*/) {
+   return 0;
 }
 
 
@@ -252,11 +352,16 @@
 
 
 /* method properties -----------------------------------------------------  */
-int cppyy_is_constructor(cppyy_type_t /* handle */, cppyy_index_t /* method_index */) {
+int cppyy_is_constructor(cppyy_type_t handle, cppyy_index_t method_index) {
+    if (handle == s_handles["example01"])
+       return example01_last_static_method <= method_index
+           && method_index < example01_last_constructor;
     return 0;
 }
 
-int cppyy_is_staticmethod(cppyy_type_t /* handle */, cppyy_index_t /* method_index */) {
+int cppyy_is_staticmethod(cppyy_type_t handle, cppyy_index_t method_index) {
+    if (handle == s_handles["example01"])
+        return method_index < example01_last_static_method ? 1 : 0;
     return 1;
 }
 
diff --git a/pypy/module/cppyy/test/conftest.py b/pypy/module/cppyy/test/conftest.py
--- a/pypy/module/cppyy/test/conftest.py
+++ b/pypy/module/cppyy/test/conftest.py
@@ -7,11 +7,12 @@
         if 'dummy' in lcapi.reflection_library:
             # run only tests that are covered by the dummy backend and tests
             # that do not rely on reflex
-            if not item.location[0] in ['test_helper.py', 'test_cppyy.py']:
+            if not ('test_helper.py' in item.location[0] or \
+                    'test_cppyy.py' in item.location[0]):
                 py.test.skip("genreflex is not installed")
             import re
-            if item.location[0] == 'test_cppyy.py' and \
-                not re.search("test0[1-3]", item.location[2]):
+            if 'test_cppyy.py' in item.location[0] and \
+                not re.search("test0[1-36]", item.location[2]):
                 py.test.skip("genreflex is not installed")
 
 def pytest_configure(config):


More information about the pypy-commit mailing list