Commits

wlav  committed 73f0d41 Merge

merge reflex-support into branch: more tests opened, issue 1676 fixed

  • Participants
  • Parent commits 28a7290, 812b952

Comments (0)

Files changed (10)

File pypy/module/cppyy/__init__.py

         '_init_pythonify'        : 'pythonify._init_pythonify',
         'load_reflection_info'   : 'pythonify.load_reflection_info',
         'add_pythonization'      : 'pythonify.add_pythonization',
+        'Template'               : 'pythonify.CppyyTemplateType',
     }
 
     def __init__(self, space, *args):

File pypy/module/cppyy/converter.py

 def get_rawbuffer(space, w_obj):
     # raw buffer
     try:
-        buf = space.readbuf_w(w_obj)
+        buf = space.getarg_w('s*', w_obj)
         return rffi.cast(rffi.VOIDP, buf.get_raw_address())
     except Exception:
         pass
     def to_memory(self, space, w_obj, w_value, offset):
         # copy the full array (uses byte copy for now)
         address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset))
-        buf = space.readbuf_w(w_value)
+        buf = space.getarg_w('s*', w_value)
         # TODO: report if too many items given?
         for i in range(min(self.size*self.typesize, buf.getlength())):
             address[i] = buf.getitem(i)
         # copy only the pointer value
         rawobject = get_rawobject_nonnull(space, w_obj)
         byteptr = rffi.cast(rffi.CCHARPP, capi.direct_ptradd(rawobject, offset))
-        buf = space.readbuf_w(w_value)
+        buf = space.getarg_w('s*', w_value)
         try:
             byteptr[0] = buf.get_raw_address()
         except ValueError:

File pypy/module/cppyy/interp_cppyy.py

 
 class CPPConstructor(CPPMethod):
     """Method dispatcher that constructs new objects. This method can not have
-    a fast path, a the allocation of the object is currently left to the
-    reflection layer only, b/c the C++ class may have an overloaded operator
+    a fast path, as the allocation of the object is currently left to the
+    reflection layer only, since the C++ class may have an overloaded operator
     new, disallowing malloc here."""
 
     _immutable_ = True
         # TODO: these casts are very, very un-pretty; need to find a way of
         # re-using CPPMethod's features w/o these roundabouts
         vscope = rffi.cast(capi.C_OBJECT, self.scope.handle)
-        w_result = CPPMethod.call(self, vscope, args_w)
+        cppinstance = None
+        try:
+            cppinstance = self.space.interp_w(W_CPPInstance, args_w[0], can_be_None=False)
+            use_args_w = args_w[1:]
+        except (OperationError, TypeError), e:
+            use_args_w = args_w
+        w_result = CPPMethod.call(self, vscope, use_args_w)
         newthis = rffi.cast(capi.C_OBJECT, self.space.int_w(w_result))
+        if cppinstance:
+            cppinstance._rawobject = newthis
+            memory_regulator.register(cppinstance)
+            return args_w[0]
         return wrap_cppobject(self.space, newthis, self.scope,
                               do_cast=False, python_owns=True, fresh=True)
 
         self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
 
     def register(self, obj):
+        if not obj._rawobject:
+            return
         int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
         self.objects.set(int_address, obj)
 
     def unregister(self, obj):
+        if not obj._rawobject:
+            return
         int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
         self.objects.set(int_address, None)
 
         w_pycppclass = get_pythonized_cppclass(space, cppclass.handle)
 
     # try to recycle existing object if this one is not newly created
-    if not fresh:
+    if not fresh and rawobject:
         obj = memory_regulator.retrieve(rawobject)
         if obj is not None and obj.cppclass is cppclass:
             return obj

File pypy/module/cppyy/pythonify.py

 # class CppyyClass defined in _init_pythonify()
 
 class CppyyTemplateType(object):
-    def __init__(self, scope, name):
-        self._scope = scope
+    def __init__(self, name, scope=None):
         self._name = name
+        if scope is None:
+            self._scope = gbl
+        else:
+            self._scope = scope
 
     def _arg_to_str(self, arg):
         if arg == str:
             raise TypeError(msg)
     else:
         def __new__(cls, *args):
-            return constructor_overload.call(None, *args)
+            # create a place-holder only as there may be a derived class defined
+            import cppyy
+            instance = cppyy.bind_object(0, class_name, True)
+            if not instance.__class__ is cls:
+                instance.__class__ = cls     # happens for derived class
+            return instance
     return __new__
 
 def make_pycppclass(scope, class_name, final_class_name, cppclass):
     return pycppclass
 
 def make_cpptemplatetype(scope, template_name):
-    return CppyyTemplateType(scope, template_name)
+    return CppyyTemplateType(template_name, scope)
 
 
 def get_pycppitem(scope, name):
         __metaclass__ = CppyyClassMeta
 
         def __init__(self, *args, **kwds):
-            pass   # ignored, for the C++ backend, ctor == __new__ + __init__
+            # self is only a placeholder; now create the actual C++ object
+            args = (self,) + args
+            self._cpp_proxy.get_overload(self._cpp_proxy.type_name).call(None, *args)
 
     # class generator callback
     cppyy._set_class_generator(clgen_callback)

File pypy/module/cppyy/src/dummy_backend.cxx

 
 #include <map>
 #include <string>
+#include <sstream>
 #include <vector>
 
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+// add example01.cxx code
+int globalAddOneToInt(int a);
+
+namespace dummy {
+#include "example01.cxx"
+}
+
+int globalAddOneToInt(int a) {
+   return dummy::globalAddOneToInt(a);
+}
 
 /* pseudo-reflection data ------------------------------------------------- */
 namespace {
 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;
+static int payload_methods_offset = 0;
 
 struct Cppyy_InitPseudoReflectionInfo {
     Cppyy_InitPseudoReflectionInfo() {
         // class example01 --
         static long s_scope_id = 0;
+
+        { // class example01 --
         s_handles["example01"] = (cppyy_scope_t)++s_scope_id;
 
         std::vector<Cppyy_PseudoMethodInfo> methods;
         // cut-off is used in cppyy_is_constructor
         example01_last_constructor = methods.size();
 
-        // (12) double addDataToDouble(double a)
+        // (12) int addDataToInt(int a)
+        argtypes.clear();
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("addDataToInt", argtypes, "int"));
+
+        // (13) int addDataToIntConstRef(const int& a)
+        argtypes.clear();
+        argtypes.push_back("const int&");
+        methods.push_back(Cppyy_PseudoMethodInfo("addDataToIntConstRef", argtypes, "int"));
+
+        // (14) int overloadedAddDataToInt(int a, int b)
+        argtypes.clear();
+        argtypes.push_back("int");
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("overloadedAddDataToInt", argtypes, "int"));
+
+        // (15) int overloadedAddDataToInt(int a)
+        // (16) int overloadedAddDataToInt(int a, int b, int c)
+        argtypes.clear();
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("overloadedAddDataToInt", argtypes, "int"));
+
+        argtypes.push_back("int");
+        argtypes.push_back("int");
+        methods.push_back(Cppyy_PseudoMethodInfo("overloadedAddDataToInt", argtypes, "int"));
+
+        // (17) double addDataToDouble(double a)
         argtypes.clear();
         argtypes.push_back("double");
         methods.push_back(Cppyy_PseudoMethodInfo("addDataToDouble", argtypes, "double"));
 
+        // (18) int addDataToAtoi(const char* str)
+        // (19) char* addToStringValue(const char* str)
+        argtypes.clear();
+        argtypes.push_back("const char*");
+        methods.push_back(Cppyy_PseudoMethodInfo("addDataToAtoi", argtypes, "int"));
+        methods.push_back(Cppyy_PseudoMethodInfo("addToStringValue", argtypes, "char*"));
+
+        // (20) void setPayload(payload* p)
+        // (21) payload* cyclePayload(payload* p)
+        // (22) payload copyCyclePayload(payload* p)
+        argtypes.clear();
+        argtypes.push_back("payload*");
+        methods.push_back(Cppyy_PseudoMethodInfo("setPayload", argtypes, "void"));
+        methods.push_back(Cppyy_PseudoMethodInfo("cyclePayload", argtypes, "payload*"));
+        methods.push_back(Cppyy_PseudoMethodInfo("copyCyclePayload", argtypes, "payload"));
+
+        payload_methods_offset = methods.size();
+
         Cppyy_PseudoClassInfo info(methods);
         s_scopes[(cppyy_scope_t)s_scope_id] = info;
-        // -- class example01
+        } // -- class example01
+
+        { // class payload --
+        s_handles["payload"] = (cppyy_scope_t)++s_scope_id;
+
+        std::vector<Cppyy_PseudoMethodInfo> methods;
+
+        // (23) payload(double d = 0.)
+        std::vector<std::string> argtypes;
+        argtypes.push_back("double");
+        methods.push_back(Cppyy_PseudoMethodInfo("payload", argtypes, "constructor"));
+
+        // (24) double getData()
+        argtypes.clear();
+        methods.push_back(Cppyy_PseudoMethodInfo("getData", argtypes, "double"));
+
+        // (25) void setData(double d)
+        argtypes.clear();
+        argtypes.push_back("double");
+        methods.push_back(Cppyy_PseudoMethodInfo("setData", argtypes, "void"));
+
+        Cppyy_PseudoClassInfo info(methods);
+        s_scopes[(cppyy_scope_t)s_scope_id] = info;
+        } // -- class payload
     }
 } _init;
 
     return s_handles[scope_name];  // lookup failure will return 0 (== error)
 }
 
+cppyy_type_t cppyy_actual_class(cppyy_type_t klass, cppyy_object_t /* obj */) {
+    return klass;
+}
+
 
 /* memory management ------------------------------------------------------ */
 void cppyy_destruct(cppyy_type_t handle, cppyy_object_t self) {
     if (handle == s_handles["example01"])
-       delete (PseudoExample01*)self;
+       delete (dummy::example01*)self;
 }
 
 
 /* method/function dispatching -------------------------------------------- */
+void cppyy_call_v(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
+    switch ((long)method) {
+    case 5:             //  static void example01:;staticSetPayload(payload* p, double d)
+        assert(!self && nargs == 2);
+        dummy::example01::staticSetPayload((dummy::payload*)(*(long*)&((CPPYY_G__value*)args)[0]),
+           ((CPPYY_G__value*)args)[1].obj.d);
+        break;
+    case 9:             // static void example01::setCount(int)
+        assert(!self && nargs == 1);
+        dummy::example01::setCount(((CPPYY_G__value*)args)[0].obj.in);
+        break;
+    case 20:            // void example01::setPayload(payload* p);
+        assert(self && nargs == 1);
+        ((dummy::example01*)self)->setPayload((dummy::payload*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
+    default:
+        assert(!"method unknown in cppyy_call_v");
+        break;
+    }
+}
+
 int cppyy_call_i(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
     int result = 0;
     switch ((long)method) {
-    case 1:             // static int staticAddOneToInt(int)
+    case 1:             // static int example01::staticAddOneToInt(int)
         assert(!self && nargs == 1);
-        result = ((CPPYY_G__value*)args)[0].obj.in + 1;
+        result = dummy::example01::staticAddOneToInt(((CPPYY_G__value*)args)[0].obj.in);
         break;
-    case 2:             // static int staticAddOneToInt(int, int)
+    case 2:             // static int example01::staticAddOneToInt(int, int)
         assert(!self && nargs == 2);
-        result = ((CPPYY_G__value*)args)[0].obj.in + ((CPPYY_G__value*)args)[1].obj.in + 1;
+        result =  dummy::example01::staticAddOneToInt(
+           ((CPPYY_G__value*)args)[0].obj.in, ((CPPYY_G__value*)args)[1].obj.in);
         break;
-    case 3:             // static int staticAtoi(const char* str)
+    case 3:             // static int example01::staticAtoi(const char* str)
         assert(!self && nargs == 1);
-        result = ::atoi((const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        result = dummy::example01::staticAtoi((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;
+    case 8:             // static int example01::getCount()
+        assert(!self && nargs == 0);
+        result = dummy::example01::getCount();
+        break;
+    case 12:            // int example01::addDataToInt(int a)
+        assert(self && nargs == 1);
+        result = ((dummy::example01*)self)->addDataToInt(((CPPYY_G__value*)args)[0].obj.in);
+        break;
+    case 18:            // int example01::addDataToAtoi(const char* str)
+        assert(self && nargs == 1);
+        result = ((dummy::example01*)self)->addDataToAtoi(
+           (const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
     default:
         assert(!"method unknown in cppyy_call_i");
         break;
 }
 
 long cppyy_call_l(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    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;
+    long result = 0;
+    switch ((long)method) {
+    case 4:             // static char* example01::staticStrcpy(const char* strin)
+        assert(!self && nargs == 1);
+        result = (long)dummy::example01::staticStrcpy(
+           (const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
+    case 6:             // static payload* example01::staticCyclePayload(payload* p, double d)
+        assert(!self && nargs == 2);
+        result = (long)dummy::example01::staticCyclePayload(
+           (dummy::payload*)(*(long*)&((CPPYY_G__value*)args)[0]),
+           ((CPPYY_G__value*)args)[1].obj.d);
+        break;
+    case 19:            // char* example01::addToStringValue(const char* str)
+        assert(self && nargs == 1);
+        result = (long)((dummy::example01*)self)->addToStringValue(
+           (const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
+    case 21:            // payload* example01::cyclePayload(payload* p)
+        assert(self && nargs == 1);
+        result = (long)((dummy::example01*)self)->cyclePayload(
+           (dummy::payload*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
+    default:
+        assert(!"method unknown in cppyy_call_l");
+        break;
     }
-    assert(!"method unknown in cppyy_call_l");
-    return 0;
+    return result;
 }
 
 double cppyy_call_d(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
     double result = 0.;
     switch ((long)method) {
-    case 0:             // static double staticAddToDouble(double)
+    case 0:             // static double example01::staticAddToDouble(double)
         assert(!self && nargs == 1);
-        result = ((CPPYY_G__value*)args)[0].obj.d + 0.01;
+        result = dummy::example01::staticAddToDouble(((CPPYY_G__value*)args)[0].obj.d);
         break;
-    case 12:            // double addDataToDouble(double a)
+    case 17:            // double example01::addDataToDouble(double a)
         assert(self && nargs == 1);
-        result = ((PseudoExample01*)self)->m_somedata + ((CPPYY_G__value*)args)[0].obj.d;
+        result = ((dummy::example01*)self)->addDataToDouble(((CPPYY_G__value*)args)[0].obj.d);
+        break;
+    case 24:            // double payload::getData()
+        assert(self && nargs == 0);
+        result = ((dummy::payload*)self)->getData();
         break;
     default:
         assert(!"method unknown in cppyy_call_d");
 }
 
 char* cppyy_call_s(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 strout;
+    char* result = 0;
+    switch ((long)method) {
+    case 4:             // static char* example01::staticStrcpy(const char* strin)
+        assert(!self && nargs == 1);
+        result = dummy::example01::staticStrcpy((const char*)(*(long*)&((CPPYY_G__value*)args)[0]));
+        break;
+    default:
+        assert(!"method unknown in cppyy_call_s");
+        break;
+    }
+    return result;
 }
 
 cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t handle, int nargs, void* args) {
         switch ((long)method) {
         case 10:
             assert(nargs == 0);
-            result = new PseudoExample01;
+            result = new dummy::example01;
             break;
         case 11:
             assert(nargs == 1);
-            result = new PseudoExample01(((CPPYY_G__value*)args)[0].obj.in);
+            result = new dummy::example01(((CPPYY_G__value*)args)[0].obj.in);
             break;
         default:
-            assert(!"method unknown in cppyy_constructor");
+            assert(!"method of example01 unknown in cppyy_constructor");
             break;
         }
-    }
+    } else if (handle == s_handles["payload"]) {
+        switch ((long)method) {
+        case 23:
+            if (nargs == 0) result = new dummy::payload;
+            else if (nargs == 1) result = new dummy::payload(((CPPYY_G__value*)args)[0].obj.d);
+            break;
+        default:
+            assert(!"method payload unknown in cppyy_constructor");
+            break;
+        }
+    }       
     return (cppyy_object_t)result;
 }
 
     return 0;
 }
     
-cppyy_method_t cppyy_get_method(cppyy_scope_t /* handle */, cppyy_index_t method_index) {
-    return (cppyy_method_t)method_index;
+cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t method_index) {
+    if (handle == s_handles["example01"])
+        return (cppyy_method_t)method_index;
+    else if (handle == s_handles["payload"])
+        return (cppyy_method_t)((long)method_index + payload_methods_offset);
+    assert(!"unknown class in cppyy_get_method");
+    return (cppyy_method_t)0;
 }
 
 
     if (handle == s_handles["example01"])
        return example01_last_static_method <= method_index
            && method_index < example01_last_constructor;
+    else if (handle == s_handles["payload"])
+       return (long)method_index == 0;
     return 0;
 }
 
 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;
+    if (handle == s_handles["payload"])
+        return 0;
     return 1;
 }
 

File pypy/module/cppyy/test/conftest.py

             # run only tests that are covered by the dummy backend and tests
             # that do not rely on reflex
             if not ('test_helper.py' in item.location[0] or \
-                    'test_cppyy.py' in item.location[0]):
+                    'test_cppyy.py' in item.location[0] or \
+                    'test_pythonify.py' in item.location[0]):
                 py.test.skip("genreflex is not installed")
             import re
-            if 'test_cppyy.py' in item.location[0] and \
-                not re.search("test0[1-36]", item.location[2]):
+            if 'test_pythonify.py' in item.location[0] and \
+                not re.search("AppTestPYTHONIFY.test0[1-6]", item.location[2]):
                 py.test.skip("genreflex is not installed")
 
 def pytest_ignore_collect(path, config):
             pkgpath = py.path.local(__file__).dirpath().join(os.pardir)
             srcpath = pkgpath.join('src')
             incpath = pkgpath.join('include')
+            tstpath = pkgpath.join('test')
 
             eci = ExternalCompilationInfo(
                 separate_module_files=[srcpath.join('dummy_backend.cxx')],
-                include_dirs=[incpath],
+                include_dirs=[incpath, tstpath],
                 use_cpp_linker=True,
             )
 

File pypy/module/cppyy/test/example01.cxx

-#include <iostream>
 #include <sstream>
 #include <string>
 #include <stdlib.h>

File pypy/module/cppyy/test/test_pythonify.py

         e = cppyy.gbl.example01(2)
         assert 5 == meth(e, 3)
 
-    def test01_installable_function(self):
+    def test15_installable_function(self):
        """Test installing and calling global C++ function as python method"""
 
        import cppyy
        assert 2 == e.fresh(1)
        assert 3 == e.fresh(2)
 
+    def test16_subclassing(self):
+        """A sub-class on the python side should have that class as type"""
+
+        import cppyy
+        example01 = cppyy.gbl.example01
+
+        o = example01()
+        assert type(o) == example01
+
+        class MyClass1(example01):
+            def myfunc(self):
+                return 1
+
+        o = MyClass1()
+        assert type(o) == MyClass1
+        assert isinstance(o, example01)
+        assert o.myfunc() == 1
+
+        class MyClass2(example01):
+            def __init__(self, what):
+                example01.__init__(self)
+                self.what = what
+
+        o = MyClass2('hi')
+        assert type(o) == MyClass2
+        assert o.what == 'hi'
+
 
 class AppTestPYTHONIFY_UI:
     spaceconfig = dict(usemodules=['cppyy', '_rawffi', 'itertools'])

File pypy/module/cppyy/test/test_stltypes.py

         assert b1 == e2
         assert b1 != b2
         assert b1 == e2
+
+
+class AppTestTEMPLATE_UI:
+    spaceconfig = dict(usemodules=['cppyy', '_rawffi', 'itertools'])
+
+    def setup_class(cls):
+        cls.w_test_dct  = cls.space.wrap(test_dct)
+        cls.w_stlstring = cls.space.appexec([], """():
+            import cppyy, sys
+            return cppyy.load_reflection_info(%r)""" % (test_dct, ))
+
+    def test01_explicit_templates(self):
+        """Explicit use of Template class"""
+
+        import cppyy
+
+        vector = cppyy.Template('vector', cppyy.gbl.std)
+        assert vector[int] == vector(int)
+
+        v = vector[int]()
+
+        N = 10
+        v += range(N)
+        assert len(v) == N
+        for i in range(N):
+            assert v[i] == i

File pypy/module/cppyy/test/test_zjit.py

         return w_obj
     interp_w._annspecialcase_ = 'specialize:arg(1)'
 
-    def buffer_w(self, w_obj, flags):
-        return FakeBuffer(w_obj)
-
-    def readbuf_w(self, w_obj):
+    def getarg_w(self, code, w_obj):    # for retrieving buffers
         return FakeBuffer(w_obj)
 
     def exception_match(self, typ, sub):