[pypy-commit] pypy reflex-support: - return by value of objects

wlav noreply at buildbot.pypy.org
Thu Jun 30 01:25:17 CEST 2011


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r45192:30fa4b9dc482
Date: 2011-06-29 16:20 -0700
http://bitbucket.org/pypy/pypy/changeset/30fa4b9dc482/

Log:	- return by value of objects
	 - lazy-lookup of functions in namespaces in case of multiple dicts
	 - fix to make global functions callable
	 - pythonization of std::vector (iterator protocol)

diff --git a/pypy/module/cppyy/capi.py b/pypy/module/cppyy/capi.py
--- a/pypy/module/cppyy/capi.py
+++ b/pypy/module/cppyy/capi.py
@@ -84,6 +84,10 @@
     "cppyy_call_v",
     [C_TYPEHANDLE, rffi.INT, C_OBJECT, rffi.INT, rffi.VOIDPP], lltype.Void,
     compilation_info=eci)
+c_call_o = rffi.llexternal(
+    "cppyy_call_o",
+    [C_TYPEHANDLE, rffi.INT, C_OBJECT, rffi.INT, rffi.VOIDPP, C_TYPEHANDLE], rffi.LONG,
+    compilation_info=eci)
 c_call_b = rffi.llexternal(
     "cppyy_call_b",
     [C_TYPEHANDLE, rffi.INT, C_OBJECT, rffi.INT, rffi.VOIDPP], rffi.INT,
diff --git a/pypy/module/cppyy/executor.py b/pypy/module/cppyy/executor.py
--- a/pypy/module/cppyy/executor.py
+++ b/pypy/module/cppyy/executor.py
@@ -151,6 +151,17 @@
         ptr_result = rffi.cast(rffi.VOIDP, long_result)
         return interp_cppyy.W_CPPInstance(space, self.cpptype, ptr_result)
 
+class InstanceExecutor(InstancePtrExecutor):
+    _immutable_ = True
+
+    def execute(self, space, func, cppthis, num_args, args):
+        from pypy.module.cppyy import interp_cppyy
+        long_result = capi.c_call_o(
+            func.cpptype.handle, func.method_index, cppthis, num_args, args, self.cpptype.handle)
+        ptr_result = rffi.cast(rffi.VOIDP, long_result)
+        # TODO: take ownership of result ...
+        return interp_cppyy.W_CPPInstance(space, self.cpptype, ptr_result)
+
 
 def get_executor(space, name):
     # Matching of 'name' to an executor factory goes through up to four levels:
@@ -188,11 +199,14 @@
 
     #   3) types/classes, either by ref/ptr or by value
     cpptype = interp_cppyy.type_byname(space, clean_name)
-    if cpptype and (compound == "*" or compound == "&"):
+    if cpptype:
         # type check for the benefit of the annotator
         from pypy.module.cppyy.interp_cppyy import W_CPPType
         cpptype = space.interp_w(W_CPPType, cpptype, can_be_None=False)
-        return InstancePtrExecutor(space, clean_name, cpptype)
+        if (compound == "*" or compound == "&"):
+            return InstancePtrExecutor(space, clean_name, cpptype)
+        elif compound == "":
+            return InstanceExecutor(space, clean_name, cpptype)
 
     # 4) additional special cases
     # ... none for now
diff --git a/pypy/module/cppyy/include/reflexcwrapper.h b/pypy/module/cppyy/include/reflexcwrapper.h
--- a/pypy/module/cppyy/include/reflexcwrapper.h
+++ b/pypy/module/cppyy/include/reflexcwrapper.h
@@ -19,6 +19,7 @@
 
     /* method/function dispatching */
     void   cppyy_call_v(cppyy_typehandle_t handle, int method_index, cppyy_object_t self, int numargs, void* args[]);
+    long   cppyy_call_o(cppyy_typehandle_t handle, int method_index, cppyy_object_t self, int numargs, void* args[], cppyy_typehandle_t rettype);
     int    cppyy_call_b(cppyy_typehandle_t handle, int method_index, cppyy_object_t self, int numargs, void* args[]);
     char   cppyy_call_c(cppyy_typehandle_t handle, int method_index, cppyy_object_t self, int numargs, void* args[]);
     short  cppyy_call_h(cppyy_typehandle_t handle, int method_index, cppyy_object_t self, int numargs, void* args[]);
diff --git a/pypy/module/cppyy/interp_cppyy.py b/pypy/module/cppyy/interp_cppyy.py
--- a/pypy/module/cppyy/interp_cppyy.py
+++ b/pypy/module/cppyy/interp_cppyy.py
@@ -339,11 +339,12 @@
         for i in range(num_methods):
             method_name = capi.charp2str_free(capi.c_method_name(self.handle, i))
             pymethod_name = helper.map_operator_name(
-                method_name, capi.c_method_num_args(self.handle, i),
-                capi.charp2str_free(capi.c_method_result_type(self.handle, i)))
-            cppfunction = self._make_cppfunction(i)
-            overload = args_temp.setdefault(pymethod_name, [])
-            overload.append(cppfunction)
+                    method_name, capi.c_method_num_args(self.handle, i),
+                    capi.charp2str_free(capi.c_method_result_type(self.handle, i)))
+            if not self.methods.has_key(pymethod_name):
+                cppfunction = self._make_cppfunction(i)
+                overload = args_temp.setdefault(pymethod_name, [])
+                overload.append(cppfunction)
         for name, functions in args_temp.iteritems():
             overload = W_CPPOverload(self.space, name, functions[:])
             self.methods[name] = overload
@@ -405,21 +406,28 @@
         num_data_members = capi.c_num_data_members(self.handle)
         for i in range(num_data_members):
             data_member_name = capi.charp2str_free(capi.c_data_member_name(self.handle, i))
-            type_name = capi.charp2str_free(capi.c_data_member_type(self.handle, i))
-            offset = capi.c_data_member_offset(self.handle, i)
-            data_member = W_CPPStaticDataMember(self.space, type_name, offset)
-            self.data_members[data_member_name] = data_member
+            if not self.data_members.has_key(data_member_name):
+                type_name = capi.charp2str_free(capi.c_data_member_type(self.handle, i))
+                offset = capi.c_data_member_offset(self.handle, i)
+                data_member = W_CPPStaticDataMember(self.space, type_name, offset)
+                self.data_members[data_member_name] = data_member
+
+    def update(self):
+        self._find_methods()
+        self._find_data_members()
 
     def is_namespace(self):
         return self.space.w_True
 
 W_CPPNamespace.typedef = TypeDef(
     'CPPNamespace',
+    update = interp2app(W_CPPNamespace.update, unwrap_spec=['self']),
     get_method_names = interp2app(W_CPPNamespace.get_method_names, unwrap_spec=['self']),
     get_overload = interp2app(W_CPPNamespace.get_overload, unwrap_spec=['self', str]),
     get_data_member_names = interp2app(W_CPPNamespace.get_data_member_names, unwrap_spec=['self']),
     get_data_member = interp2app(W_CPPNamespace.get_data_member, unwrap_spec=['self', str]),
     is_namespace = interp2app(W_CPPNamespace.is_namespace, unwrap_spec=['self']),
+    invoke = interp2app(W_CPPNamespace.invoke, unwrap_spec=['self', W_CPPOverload, 'args_w']),
 )
 
 
diff --git a/pypy/module/cppyy/pythonify.py b/pypy/module/cppyy/pythonify.py
--- a/pypy/module/cppyy/pythonify.py
+++ b/pypy/module/cppyy/pythonify.py
@@ -10,7 +10,6 @@
     def __getattr__(self, attr):
         try:
             cppitem = get_cppitem(attr, self)
-            self.__dict__[attr] = cppitem
             return cppitem
         except TypeError:
             raise AttributeError("%s object has no attribute '%s'" % (self, attr))
@@ -45,7 +44,7 @@
     __metaclass__ = CppyyClass
 
     def __init__(self, *args):
-        self._cppinstance = self._cppyyclass.construct(*args)
+        self._cppinstance = self._cpp_proxy.construct(*args)
         
     def destruct(self):
         self._cppinstance.destruct()
@@ -61,14 +60,14 @@
 def make_static_function(cpptype, func_name, cppol):
     rettype = cppol.get_returntype()
     if not rettype:                              # return builtin type
-        def method(*args):
+        def function(*args):
             return cpptype.invoke(cppol, *args)
     else:                                        # return instance
         cppclass = get_cppclass(rettype)
-        def method(*args):
+        def function(*args):
             return bind_object(cpptype.invoke(cppol, *args), cppclass)
-    method.__name__ = func_name
-    return staticmethod(method)
+    function.__name__ = func_name
+    return staticmethod(function)
 
 def make_method(meth_name, cppol):
     rettype = cppol.get_returntype()
@@ -84,11 +83,11 @@
 
 
 def make_cppnamespace(namespace_name, cppns):
-    d = {}
+    d = {"_cpp_proxy" : cppns}
 
     # insert static methods into the "namespace" dictionary
     for func_name in cppns.get_method_names():
-        cppol = cppns.get_overload(f)
+        cppol = cppns.get_overload(func_name)
         d[func_name] = make_static_function(cppns, func_name, cppol)
 
     # create a meta class to allow properties (for static data write access)
@@ -130,7 +129,7 @@
     metacpp = type(CppyyClass)(class_name+'_meta', _drop_cycles(metabases), {})
 
     # create the python-side C++ class representation
-    d = {"_cppyyclass" : cpptype}
+    d = {"_cpp_proxy" : cpptype}
     pycpptype = metacpp(class_name, _drop_cycles(bases), d)
  
     # cache result early so that the class methods can find the class itself
@@ -160,11 +159,12 @@
     return CppyyTemplateType(scope, template_name)
 
 
-_existing_cppitems = {}               # to merge with gbl.__dict__ (?)
+_existing_cppitems = {}               # TODO: to merge with gbl.__dict__ (?)
 def get_cppitem(name, scope=None):
     if scope and not scope is gbl:
         fullname = scope.__name__+"::"+name
     else:
+        scope = gbl
         fullname = name
 
     # lookup class ...
@@ -173,19 +173,30 @@
     except KeyError:
         pass
 
-    # ... if lookup failed, create
+    # ... if lookup failed, create (classes, templates, functions)
     pycppitem = None
+
     cppitem = cppyy._type_byname(fullname)
     if cppitem:
         if cppitem.is_namespace():
             pycppitem = make_cppnamespace(fullname, cppitem)
         else:
             pycppitem = make_cppclass(fullname, cppitem)
-    else:
+        scope.__dict__[name] = pycppitem
+
+    if not cppitem:
         cppitem = cppyy._template_byname(fullname)
         if cppitem:
             pycppitem = make_cpptemplatetype(name, scope)
             _existing_cppitems[fullname] = pycppitem
+            scope.__dict__[name] = pycppitem
+
+    if not cppitem and isinstance(scope, CppyyNamespaceMeta):
+        scope._cpp_proxy.update()  # TODO: this is currently quadratic
+        cppitem = scope._cpp_proxy.get_overload(name)
+        pycppitem = make_static_function(scope._cpp_proxy, name, cppitem)
+        setattr(scope.__class__, name, pycppitem)
+        pycppitem = getattr(scope, name)
 
     if pycppitem:
         _existing_cppitems[fullname] = pycppitem
@@ -203,6 +214,17 @@
             not hasattr(pyclass,'__len__') and callable(pyclass.size):
         pyclass.__len__ = pyclass.size
 
+    # map begin()/end() protocol to iter protocol
+    if hasattr(pyclass, 'begin') and hasattr(pyclass, 'end'):
+        def __iter__(self):
+            iter = self.begin()
+            while gbl.__gnu_cxx.__ne__(iter, self.end()):
+                yield iter.__deref__()
+                iter.__preinc__()
+            iter.destruct()
+            raise StopIteration
+        pyclass.__iter__ = __iter__
+
 
 _loaded_shared_libs = {}
 def load_lib(name):
diff --git a/pypy/module/cppyy/src/reflexcwrapper.cxx b/pypy/module/cppyy/src/reflexcwrapper.cxx
--- a/pypy/module/cppyy/src/reflexcwrapper.cxx
+++ b/pypy/module/cppyy/src/reflexcwrapper.cxx
@@ -65,6 +65,23 @@
     }
 }
 
+long cppyy_call_o(cppyy_typehandle_t handle, int method_index,
+                  cppyy_object_t self, int numargs, void* args[],
+                  cppyy_typehandle_t rettype) {
+    Reflex::Type rt = type_from_handle(rettype);
+    void* result = rt.Allocate();
+    std::vector<void*> arguments(args, args+numargs);
+    Reflex::Scope s = scope_from_handle(handle);
+    Reflex::Member m = s.FunctionMemberAt(method_index);
+    if (self) {
+        Reflex::Object o((Reflex::Type)s, self);
+        m.Invoke(o, *((long*)result), arguments);
+    } else {
+       m.Invoke(*((long*)result), arguments);
+    }
+    return (long)result;
+}
+
 template<typename T>
 static inline T cppyy_call_T(cppyy_typehandle_t handle, int method_index,
                              cppyy_object_t self, int numargs, void* args[]) {
@@ -82,12 +99,12 @@
 }
 
 int cppyy_call_b(cppyy_typehandle_t handle, int method_index,
-                  cppyy_object_t self, int numargs, void* args[]) {
+                 cppyy_object_t self, int numargs, void* args[]) {
     return (int)cppyy_call_T<bool>(handle, method_index, self, numargs, args);
 }
 
 char cppyy_call_c(cppyy_typehandle_t handle, int method_index,
-                 cppyy_object_t self, int numargs, void* args[]) {
+                  cppyy_object_t self, int numargs, void* args[]) {
     return cppyy_call_T<char>(handle, method_index, self, numargs, args);
 }
 
diff --git a/pypy/module/cppyy/test/Makefile b/pypy/module/cppyy/test/Makefile
--- a/pypy/module/cppyy/test/Makefile
+++ b/pypy/module/cppyy/test/Makefile
@@ -18,8 +18,8 @@
   cppflags2=-Wno-pmf-conversions -O3
 endif
 
-example01Dict.so: example01.cxx example01.h
-	$(genreflex) example01.h $(genreflexflags)
+example01Dict.so: example01.cxx example01.h example01.xml
+	$(genreflex) example01.h $(genreflexflags) --selection=example01.xml
 	g++ -o $@ example01_rflx.cpp example01.cxx -shared -lReflex $(cppflags) $(cppflags2)
 
 datatypesDict.so: datatypes.cxx datatypes.h
diff --git a/pypy/module/cppyy/test/example01.cxx b/pypy/module/cppyy/test/example01.cxx
--- a/pypy/module/cppyy/test/example01.cxx
+++ b/pypy/module/cppyy/test/example01.cxx
@@ -61,6 +61,11 @@
     return p;
 }
 
+payload example01::staticCopyCyclePayload(payload* p, double d) {
+    staticSetPayload(p, d);
+    return *p;
+}
+
 int example01::getCount() {
     std::cout << "getcount called" << std::endl;
     return count;
@@ -98,5 +103,20 @@
     return p;
 }
 
+payload example01::copyCyclePayload(payload* p) {
+    setPayload(p);
+    return *p;
+}
+
 // class-level data
 int example01::count = 0;
+
+
+// global
+int globalAddOneToInt(int a) {
+   return a + 1;
+}
+
+int ns_example01::globalAddOneToInt(int a) {
+   return ::globalAddOneToInt(a);
+}
diff --git a/pypy/module/cppyy/test/example01.h b/pypy/module/cppyy/test/example01.h
--- a/pypy/module/cppyy/test/example01.h
+++ b/pypy/module/cppyy/test/example01.h
@@ -27,6 +27,7 @@
     static char* staticStrcpy(const char* strin);
     static void staticSetPayload(payload* p, double d);
     static payload* staticCyclePayload(payload* p, double d);
+    static payload staticCopyCyclePayload(payload* p, double d);
     static int getCount();
 
 public:        // instance methods
@@ -37,6 +38,7 @@
 
     void setPayload(payload* p);
     payload* cyclePayload(payload* p);
+    payload copyCyclePayload(payload* p);
 
 public:        // class-level data
     static int count;
@@ -44,3 +46,10 @@
 public:        // instance data
     int m_somedata;
 };
+
+
+// global functions
+int globalAddOneToInt(int a);
+namespace ns_example01 {
+    int globalAddOneToInt(int a);
+}
diff --git a/pypy/module/cppyy/test/stltypes.h b/pypy/module/cppyy/test/stltypes.h
--- a/pypy/module/cppyy/test/stltypes.h
+++ b/pypy/module/cppyy/test/stltypes.h
@@ -6,7 +6,13 @@
 #define STLTYPES_EXPLICIT_INSTANTIATION(STLTYPE, TTYPE)                         \
 template class std::STLTYPE< TTYPE >;                                           \
 template class __gnu_cxx::__normal_iterator<TTYPE*, std::STLTYPE< TTYPE > >;    \
-template class __gnu_cxx::__normal_iterator<const TTYPE*, std::STLTYPE< TTYPE > >;
+template class __gnu_cxx::__normal_iterator<const TTYPE*, std::STLTYPE< TTYPE > >;\
+namespace __gnu_cxx {                                                           \
+template bool operator==(const std::STLTYPE< TTYPE >::iterator&,                \
+                         const std::STLTYPE< TTYPE >::iterator&);               \
+template bool operator!=(const std::STLTYPE< TTYPE >::iterator&,                \
+                         const std::STLTYPE< TTYPE >::iterator&);               \
+}
 
 
 //- basic example class
diff --git a/pypy/module/cppyy/test/stltypes.xml b/pypy/module/cppyy/test/stltypes.xml
--- a/pypy/module/cppyy/test/stltypes.xml
+++ b/pypy/module/cppyy/test/stltypes.xml
@@ -7,5 +7,9 @@
   <class pattern="std::_Vector_base<*>::_Vector_impl" />
   <class pattern="std::allocator<*>" />
 
+  <function name="__gnu_cxx::operator=="/>
+  <function name="__gnu_cxx::operator!="/>
+
   <class name="just_a_class" />
+
 </lcgdict>
diff --git a/pypy/module/cppyy/test/test_pythonify.py b/pypy/module/cppyy/test/test_pythonify.py
--- a/pypy/module/cppyy/test/test_pythonify.py
+++ b/pypy/module/cppyy/test/test_pythonify.py
@@ -24,13 +24,13 @@
             import cppyy
             return cppyy.load_lib(%r)""" % (shared_lib, ))
 
-    def test0_load_lib_cache(self):
+    def test01_load_lib_cache(self):
         """Test whether loading a library twice results in the same object."""
         import cppyy
         lib2 = cppyy.load_lib(self.shared_lib)
         assert self.example01 is lib2
 
-    def test1_finding_classes(self):
+    def test02_finding_classes(self):
         """Test the lookup of a class, and its caching."""
         import cppyy
         example01_class = cppyy.gbl.example01
@@ -39,7 +39,7 @@
 
         raises(AttributeError, "cppyy.gbl.nonexistingclass")
 
-    def test2_calling_static_functions(self):
+    def test03_calling_static_functions(self):
         """Test calling of static methods."""
         import cppyy, sys
         example01_class = cppyy.gbl.example01
@@ -74,7 +74,7 @@
 
         raises(TypeError, 'example01_class.staticStrcpy(1.)')
 
-    def test3_constructing_and_calling(self):
+    def test04_constructing_and_calling(self):
         """Test object and method calls."""
         import cppyy
         example01_class = cppyy.gbl.example01
@@ -125,7 +125,7 @@
         instance.destruct()
         assert example01_class.getCount() == 0
 
-    def test4_passing_object_by_pointer(self):
+    def test05_passing_object_by_pointer(self):
         import cppyy
         example01_class = cppyy.gbl.example01
         payload_class = cppyy.gbl.payload
@@ -148,14 +148,14 @@
         e.destruct()
         assert example01_class.getCount() == 0
 
-    def test5_returning_object_by_pointer(self):
+    def test06_returning_object_by_pointer(self):
         import cppyy
         example01_class = cppyy.gbl.example01
         payload_class = cppyy.gbl.payload
 
         pl = payload_class(3.14)
         assert round(pl.getData()-3.14, 8) == 0
-        
+
         pl2 = example01_class.staticCyclePayload(pl, 38.)
         assert pl2.getData() == 38.
 
@@ -163,7 +163,38 @@
 
         pl2 = e.cyclePayload(pl)
         assert round(pl2.getData()-14., 8) == 0
-        
+
         pl.destruct()
         e.destruct()
         assert example01_class.getCount() == 0
+
+    def test07_returning_object_by_value(self):
+        import cppyy
+        example01_class = cppyy.gbl.example01
+        payload_class = cppyy.gbl.payload
+
+        pl = payload_class(3.14)
+        assert round(pl.getData()-3.14, 8) == 0
+
+        pl2 = example01_class.staticCopyCyclePayload(pl, 38.)
+        assert pl2.getData() == 38.
+        pl2.destruct()
+
+        e = example01_class(14)
+
+        pl2 = e.copyCyclePayload(pl)
+        assert round(pl2.getData()-14., 8) == 0
+        pl2.destruct()
+
+        pl.destruct()
+        e.destruct()
+        assert example01_class.getCount() == 0
+
+    def test08_global_functions(self):
+        import cppyy
+
+        assert cppyy.gbl.globalAddOneToInt(3) == 4     # creation lookup
+        assert cppyy.gbl.globalAddOneToInt(3) == 4     # cached lookup
+
+        assert cppyy.gbl.ns_example01.globalAddOneToInt(4) == 5
+        assert cppyy.gbl.ns_example01.globalAddOneToInt(4) == 5
diff --git a/pypy/module/cppyy/test/test_stltypes.py b/pypy/module/cppyy/test/test_stltypes.py
--- a/pypy/module/cppyy/test/test_stltypes.py
+++ b/pypy/module/cppyy/test/test_stltypes.py
@@ -101,6 +101,31 @@
         import cppyy
 
         v = cppyy.gbl.std.vector(int)()
-     #   for arg in v:
-     #       pass
+        for arg in v:
+            pass
         v.destruct()
+
+    def test04_vector_iteration(self):
+        """Test iteration over an std::vector<int>"""
+
+        import cppyy
+
+        v = cppyy.gbl.std.vector(int)()
+
+        for i in range(self.N):
+            v.push_back(i)
+            assert v.size() == i+1
+            assert v.at(i) == i
+            assert v[i] == i
+
+        assert v.size() == self.N
+        assert len(v) == self.N
+
+        i = 0
+        for arg in v:
+            assert arg == i
+            i += 1
+
+        assert list(v) == [i for i in range(self.N)]
+
+        v.destruct()


More information about the pypy-commit mailing list