[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