Commits

wlav committed a3036ea

rework how constructors are used to allow for overloader operator new/delete (albeit that Reflex does not support that, but cling/llvm does)

Comments (0)

Files changed (11)

pypy/module/cppyy/capi/__init__.py

 
 c_constructor = rffi.llexternal(
     "cppyy_constructor",
-    [C_METHOD, C_OBJECT, rffi.INT, rffi.VOIDP], lltype.Void,
+    [C_METHOD, C_TYPE, rffi.INT, rffi.VOIDP], C_OBJECT,
     threadsafe=ts_call,
     compilation_info=backend.eci)
 _c_call_o = rffi.llexternal(

pypy/module/cppyy/executor.py

         return space.wrap(result)
 
 
-class ConstructorExecutor(VoidExecutor):
+class ConstructorExecutor(FunctionExecutor):
 
-    def execute(self, space, cppmethod, cppthis, num_args, args):
-        capi.c_constructor(cppmethod, cppthis, num_args, args)
-        return space.w_None
+    def execute(self, space, cppmethod, cpptype, num_args, args):
+        from pypy.module.cppyy import interp_cppyy
+        newthis = capi.c_constructor(cppmethod, cpptype, num_args, args)
+        assert lltype.typeOf(newthis) == capi.C_OBJECT
+        return space.wrap(newthis)
 
 
 class InstancePtrExecutor(FunctionExecutor):

pypy/module/cppyy/include/capi.h

     void*  cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
     char*  cppyy_call_s(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
 
-    void cppyy_constructor(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
+    cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t klass, int nargs, void* args);
     cppyy_object_t cppyy_call_o(cppyy_method_t method, cppyy_object_t self, int nargs, void* args, cppyy_type_t result_type);
 
     cppyy_methptrgetter_t cppyy_get_methptr_getter(cppyy_scope_t scope, cppyy_index_t idx);

pypy/module/cppyy/interp_cppyy.py

 
 
 class CPPConstructor(CPPMethod):
-    """Method dispatcher that constructs new objects. In addition to the call,
-    it allocates memory for the newly constructed object and sets ownership
-    to Python."""
+    """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
+    new, disallowing malloc here."""
 
     _immutable_ = True
 
     def call(self, cppthis, args_w):
-        newthis = capi.c_allocate(self.scope)
-        assert lltype.typeOf(newthis) == capi.C_OBJECT
-        try:
-            CPPMethod.call(self, newthis, args_w)
-        except:
-            capi.c_deallocate(self.scope, newthis)
-            raise
+        # 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)
+        newthis = rffi.cast(capi.C_OBJECT, self.space.int_w(w_result))
         return wrap_new_cppobject_nocast(
             self.space, self.space.w_None, self.scope, newthis, isref=False, python_owns=True)
 

pypy/module/cppyy/src/cintcwrapper.cxx

     return cppstring_to_cstring("");
 }
 
-void cppyy_constructor(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
-    G__setgvp((long)self);
-    cppyy_call_T(method, self, nargs, args);
+cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t handle, int nargs, void* args) {
+    cppyy_object_t self = (cppyy_object_t)NULL;
+    if ((InterpretedFuncs_t::size_type)method >= g_interpreted.size()) {
+        G__setgvp((long)G__PVOID);
+        self = (cppyy_object_t)cppyy_call_l(method, (cppyy_object_t)NULL, nargs, args);
+    } else {
+    // for macro's/interpreted classes
+        self = cppyy_allocate(handle);
+        G__setgvp((long)self);
+        cppyy_call_T(method, self, nargs, args);
+    }
     G__setgvp((long)G__PVOID);
+    return self;
 }
 
 cppyy_object_t cppyy_call_o(cppyy_type_t method, cppyy_object_t self, int nargs, void* args,

pypy/module/cppyy/src/reflexcwrapper.cxx

     return cppstring_to_cstring(result);
 }
 
-void cppyy_constructor(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) {
+cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t handle, int nargs, void* args) {
+    cppyy_object_t self = cppyy_allocate(handle);
     cppyy_call_v(method, self, nargs, args);
+    return self;
 }
 
 cppyy_object_t cppyy_call_o(cppyy_method_t method, cppyy_object_t self, int nargs, void* args,

pypy/module/cppyy/test/advancedcpp.cxx

 multi1::~multi1() {}
 multi2::~multi2() {}
 multi::~multi() {}
+
+
+// for testing calls to overloaded new
+int new_overloader::s_instances = 0;
+
+void* new_overloader::operator new(std::size_t size) {
+    ++s_instances;
+    return ::operator new(size);
+}
+
+void* new_overloader::operator new(std::size_t, void* p) throw() {
+    // no ++s_instances, as no memory is allocated
+    return p;
+}
+
+void new_overloader::operator delete(void* p, std::size_t) {
+    if (p == 0) return;
+    --s_instances;
+    ::operator delete(p);
+}

pypy/module/cppyy/test/advancedcpp.h

+#include <new>
 #include <vector>
 
 
 private:
     int m_int;
 };
+
+
+//===========================================================================
+class new_overloader {             // for testing calls to overloaded new
+public:
+    static int s_instances;
+
+public:
+    void* operator new(std::size_t size);
+    void* operator new(std::size_t, void* p) throw();
+    void operator delete(void* p, std::size_t size);
+};

pypy/module/cppyy/test/advancedcpp.xml

 
   <class pattern="multi*" />
 
+  <class name="new_overloader" />
+
 </lcgdict>

pypy/module/cppyy/test/advancedcpp_LinkDef.h

 #pragma link C++ class multi2;
 #pragma link C++ class multi;
 
+#pragma link C++ class new_overloader;
+
 #endif

pypy/module/cppyy/test/test_advancedcpp.py

         assert type(c2) == cppyy.gbl.c_class_2
         assert c2.m_c == 3
         c2.destruct()
+
+    def test14_new_overloader(self):
+        """Verify that class-level overloaded new/delete are called"""
+
+        # TODO: operator new appears to be respected by CINT, but operator
+        # delete is not called through root/meta. Anyway, Reflex gets it all
+        # wrong (clear from the generated code). Keep this test as it should
+        # be all better in the cling/llvm world ...
+
+        if self.capi_identity == 'Reflex':   # don't test anything
+            return
+
+        import cppyy
+
+        assert cppyy.gbl.new_overloader.s_instances == 0
+        nl = cppyy.gbl.new_overloader()
+        assert cppyy.gbl.new_overloader.s_instances == 1
+        nl.destruct()
+
+        if self.capi_identity == 'CINT':     # do not test delete
+            return
+
+        import gc
+        gc.collect()
+        assert cppyy.gbl.new_overloader.s_instances == 0