Author: Brian Kearns <bdkea...@gmail.com> Branch: Changeset: r70994:844b089193cd Date: 2014-04-25 22:32 -0400 http://bitbucket.org/pypy/pypy/changeset/844b089193cd/
Log: merge heads diff --git a/pypy/module/cppyy/__init__.py b/pypy/module/cppyy/__init__.py --- a/pypy/module/cppyy/__init__.py +++ b/pypy/module/cppyy/__init__.py @@ -25,6 +25,7 @@ '_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): diff --git a/pypy/module/cppyy/converter.py b/pypy/module/cppyy/converter.py --- a/pypy/module/cppyy/converter.py +++ b/pypy/module/cppyy/converter.py @@ -63,7 +63,7 @@ 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 @@ -163,7 +163,7 @@ 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) @@ -204,7 +204,7 @@ # 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: 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 @@ -450,8 +450,8 @@ 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 @@ -460,8 +460,18 @@ # 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) @@ -1141,10 +1151,14 @@ 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) @@ -1194,7 +1208,7 @@ 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 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 @@ -25,9 +25,12 @@ # 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: @@ -143,7 +146,12 @@ 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): @@ -206,7 +214,7 @@ return pycppclass def make_cpptemplatetype(scope, template_name): - return CppyyTemplateType(scope, template_name) + return CppyyTemplateType(template_name, scope) def get_pycppitem(scope, name): @@ -424,7 +432,9 @@ __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) 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 @@ -3,12 +3,23 @@ #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 { @@ -38,28 +49,16 @@ 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; @@ -115,14 +114,81 @@ // 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; @@ -150,36 +216,69 @@ 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; @@ -188,26 +287,50 @@ } 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"); @@ -217,11 +340,17 @@ } 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) { @@ -230,17 +359,27 @@ 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; } @@ -346,8 +485,13 @@ 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; } @@ -356,12 +500,16 @@ 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; } 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 @@ -8,11 +8,12 @@ # 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): @@ -39,10 +40,11 @@ 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, ) 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 @@ -1,4 +1,3 @@ -#include <iostream> #include <sstream> #include <string> #include <stdlib.h> 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 @@ -321,7 +321,7 @@ 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 @@ -332,6 +332,33 @@ 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']) 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 @@ -477,3 +477,29 @@ 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 diff --git a/pypy/module/cppyy/test/test_zjit.py b/pypy/module/cppyy/test/test_zjit.py --- a/pypy/module/cppyy/test/test_zjit.py +++ b/pypy/module/cppyy/test/test_zjit.py @@ -133,10 +133,7 @@ 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): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit