[pypy-commit] pypy reflex-support: first steps towards STL support
wlav
noreply at buildbot.pypy.org
Thu Jun 2 07:01:26 CEST 2011
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r44632:c241b111c812
Date: 2011-06-01 22:14 -0700
http://bitbucket.org/pypy/pypy/changeset/c241b111c812/
Log: first steps towards STL support
diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -16,6 +16,11 @@
^pypy/module/cpyext/test/.+\.obj$
^pypy/module/cpyext/test/.+\.manifest$
^pypy/module/test_lib_pypy/ctypes_tests/.+\.o$
+^pypy/module/cppyy/src/.+\.o$
+^pypy/module/cppyy/src/.+\.errors$
+^pypy/module/cppyy/test/.+_rflx\.cpp$
+^pypy/module/cppyy/test/.+\.so$
+^pypy/module/cppyy/test/.+\.exe$
^pypy/doc/.+\.html$
^pypy/doc/config/.+\.rst$
^pypy/doc/basicblock\.asc$
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
@@ -393,6 +393,13 @@
except KeyError, k:
pass
+ # 3) accept const ref as by value
+ if compound and compound[len(compound)-1] == "&":
+ try:
+ return _converters[clean_name](space, -1)
+ except KeyError:
+ pass
+
# 5) generalized cases (covers basically all user classes)
cpptype = interp_cppyy.type_byname(space, clean_name)
if compound == "*":
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
@@ -27,7 +27,7 @@
class PtrTypeExecutor(FunctionExecutor):
_immutable_ = True
- typecode = ''
+ typecode = 'P'
def execute(self, space, func, cppthis, num_args, args):
lresult = capi.c_call_l(func.cpptype.handle, func.method_index, cppthis, num_args, args)
@@ -140,8 +140,17 @@
def get_executor(space, name):
+ # Matching of 'name' to an executor factory goes through up to four levels:
+ # 1) full, qualified match
+ # 2) drop '&': by-ref is pretty much the same as by-value, python-wise
+ # 3) types/classes, either by ref/ptr or by value
+ # 4) additional special cases
+ #
+ # If all fails, a default is used, which can be ignored at least until use.
+
from pypy.module.cppyy import interp_cppyy
+ # 1) full, qualified match
try:
return _executors[name](space, "", None)
except KeyError:
@@ -149,9 +158,32 @@
compound = helper.compound(name)
clean_name = helper.clean_type(name)
- cpptype = interp_cppyy.type_byname(space, clean_name)
- if compound == "*":
- return InstancePtrExecutor(space, cpptype.name, cpptype)
+
+ # 1a) clean lookup
+ try:
+ return _executors[clean_name+compound](space, "", None)
+ except KeyError:
+ pass
+
+ # 2) drop '&': by-ref is pretty much the same as by-value, python-wise
+ if compound and compound[len(compound)-1] == "&":
+ try:
+ return _executors[clean_name](space, "", None)
+ except KeyError:
+ pass
+
+ # 3) types/classes, either by ref/ptr or by value
+ try:
+ cpptype = interp_cppyy.type_byname(space, clean_name)
+ if compound == "*" or compound == "&":
+ return InstancePtrExecutor(space, clean_name, cpptype)
+ except OperationError, e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ pass
+
+ # 4) additional special cases
+ # ... none for now
# currently used until proper lazy instantiation available in interp_cppyy
return FunctionExecutor(space, "", None)
@@ -159,8 +191,10 @@
# raise TypeError("no clue what %s is" % name)
_executors["void"] = VoidExecutor
+_executors["void*"] = PtrTypeExecutor
_executors["bool"] = BoolExecutor
_executors["char"] = CharExecutor
+_executors["char*"] = CStringExecutor
_executors["unsigned char"] = CharExecutor
_executors["short int"] = ShortExecutor
_executors["short int*"] = ShortPtrExecutor
@@ -178,4 +212,3 @@
_executors["float*"] = FloatPtrExecutor
_executors["double"] = DoubleExecutor
_executors["double*"] = DoublePtrExecutor
-_executors["char*"] = CStringExecutor
diff --git a/pypy/module/cppyy/helper.py b/pypy/module/cppyy/helper.py
--- a/pypy/module/cppyy/helper.py
+++ b/pypy/module/cppyy/helper.py
@@ -1,5 +1,7 @@
from pypy.rlib import rstring
+
+#- type name manipulations --------------------------------------------------
def compound(name):
name = "".join(rstring.split(name, "const")) # poor man's replace
if name.endswith("]"): # array type?
@@ -27,11 +29,120 @@
return i + 1
def clean_type(name):
- assert name.find("const") == -1
+ # can't strip const early b/c name could be a template ...
i = _find_qualifier_index(name)
name = name[:i].strip(' ')
+
+ idx = -1
if name.endswith("]"): # array type?
idx = name.rfind("[")
if 0 < idx:
- return name[:idx]
- return name
+ name = name[:idx]
+ elif name.endswith(">"): # template type?
+ idx = name.find("<")
+ n1 = "".join(rstring.split(name[:idx], "const")) # poor man's replace
+ name = "".join((n1, name[idx:]))
+ else:
+ name = "".join(rstring.split(name, "const")) # poor man's replace
+ name = name[:_find_qualifier_index(name)]
+ return name.strip(' ')
+
+
+#- operator mappings --------------------------------------------------------
+_operator_mappings = {}
+
+def map_operator_name(cppname, nargs):
+ from pypy.module.cppyy import capi
+
+ if cppname[0:8] == "operator":
+ op = cppname[8:].strip(' ')
+
+ # operator could be a conversion using a typedef
+ handle = capi.c_get_typehandle(op)
+ if handle:
+ op = capi.charp2str_free(capi.c_final_name(handle))
+
+ # look for known mapping
+ try:
+ return _operator_mappings[op]
+ except KeyError:
+ pass
+
+ # a couple more cases that depend on whether args were given
+
+ if op == "*": # dereference (not python) vs. multiplication
+ return nargs and "__mul__" or "__deref__"
+
+ if op == "+": # unary positive vs. binary addition
+ return nargs and "__add__" or "__pos__"
+
+ if op == "-": # unary negative vs. binary subtraction
+ return nargs and "__sub__" or "__neg__"
+
+ if op == "++": # prefix v.s. postfix increment (not python)
+ return nargs and "__postinc__" or "__preinc__";
+
+ if op == "--": # prefix v.s. postfix decrement (not python)
+ return nargs and "__postdec__" or "__predec__";
+
+ # might get here, as not all operator methods handled (new, delete,etc.)
+ # TODO: perhaps absorb or "pythonify" these operators?
+ return cppname
+
+# _operator_mappings["[]"] = "__setitem__" # depends on return type
+# _operator_mappings["+"] = "__add__" # depends on # of args (see __pos__)
+# _operator_mappings["-"] = "__sub__" # id. (eq. __neg__)
+# _operator_mappings["*"] = "__mul__" # double meaning in C++
+
+_operator_mappings["[]"] = "__getitem__"
+_operator_mappings["()"] = "__call__"
+_operator_mappings["/"] = "__div__" # __truediv__ in p3
+_operator_mappings["%"] = "__mod__"
+_operator_mappings["**"] = "__pow__" # not C++
+_operator_mappings["<<"] = "__lshift__"
+_operator_mappings[">>"] = "__rshift__"
+_operator_mappings["&"] = "__and__"
+_operator_mappings["|"] = "__or__"
+_operator_mappings["^"] = "__xor__"
+_operator_mappings["~"] = "__inv__"
+_operator_mappings["+="] = "__iadd__"
+_operator_mappings["-="] = "__isub__"
+_operator_mappings["*="] = "__imul__"
+_operator_mappings["/="] = "__idiv__" # __itruediv__ in p3
+_operator_mappings["%="] = "__imod__"
+_operator_mappings["**="] = "__ipow__"
+_operator_mappings["<<="] = "__ilshift__"
+_operator_mappings[">>="] = "__irshift__"
+_operator_mappings["&="] = "__iand__"
+_operator_mappings["|="] = "__ior__"
+_operator_mappings["^="] = "__ixor__"
+_operator_mappings["=="] = "__eq__"
+_operator_mappings["!="] = "__ne__"
+_operator_mappings[">"] = "__gt__"
+_operator_mappings["<"] = "__lt__"
+_operator_mappings[">="] = "__ge__"
+_operator_mappings["<="] = "__le__"
+
+# the following type mappings are "exact"
+_operator_mappings["const char*"] = "__str__"
+_operator_mappings["int"] = "__int__"
+_operator_mappings["long"] = "__long__" # __int__ in p3
+_operator_mappings["double"] = "__float__"
+
+# the following type mappings are "okay"; the assumption is that they
+# are not mixed up with the ones above or between themselves (and if
+# they are, that it is done consistently)
+_operator_mappings["char*"] = "__str__"
+_operator_mappings["short"] = "__int__"
+_operator_mappings["unsigned short"] = "__int__"
+_operator_mappings["unsigned int"] = "__long__" # __int__ in p3
+_operator_mappings["unsigned long"] = "__long__" # id.
+_operator_mappings["long long"] = "__long__" # id.
+_operator_mappings["unsigned long long"] = "__long__" # id.
+_operator_mappings["float"] = "__float__"
+
+_operator_mappings["bool"] = "__nonzero__" # __bool__ in p3
+
+# the following are not python, but useful to expose
+_operator_mappings["->"] = "__follow__"
+_operator_mappings["="] = "__assign__"
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
@@ -10,7 +10,7 @@
from pypy.rlib import libffi
from pypy.rlib import jit, debug
-from pypy.module.cppyy import converter, executor
+from pypy.module.cppyy import converter, executor, helper
class FastCallNotPossible(Exception):
pass
@@ -320,8 +320,10 @@
args_temp = {}
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))
cppfunction = self._make_cppfunction(i)
- overload = args_temp.setdefault(method_name, [])
+ overload = args_temp.setdefault(pymethod_name, [])
overload.append(cppfunction)
for name, functions in args_temp.iteritems():
overload = W_CPPOverload(self.space, name, functions[:])
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
@@ -59,9 +59,7 @@
cppclass = get_cppitem(attr, self.__name__)
self.__dict__[attr] = cppclass
return cppclass
- except TypeError, e:
- import traceback
- traceback.print_exc()
+ except TypeError:
raise AttributeError("%s object has no attribute '%s'" % (self,attr))
@@ -92,15 +90,6 @@
return pycppns
def make_cppclass(class_name, cpptype):
- d = {"_cppyyclass" : cpptype}
-
- # insert (static) methods into the class dictionary
- for meth_name in cpptype.get_method_names():
- cppol = cpptype.get_overload(meth_name)
- if cppol.is_static():
- d[meth_name] = make_static_function(cpptype, meth_name, cppol)
- else:
- d[meth_name] = make_method(meth_name, cppol)
# get a list of base classes for class creation
bases = tuple([get_cppclass(base) for base in cpptype.get_base_names()])
@@ -112,20 +101,30 @@
metacpp = type(CppyyClass)(class_name+'_meta', metabases,
{"__getattr__" : __innercpp_getattr__})
+ # create the python-side C++ class representation
+ d = {"_cppyyclass" : cpptype}
+ pycpptype = metacpp(class_name, bases, d)
+
+ # cache result early so that the class methods can find the class itself
+ _existing_cppitems[class_name] = pycpptype
+
+ # insert (static) methods into the class dictionary
+ for meth_name in cpptype.get_method_names():
+ cppol = cpptype.get_overload(meth_name)
+ if cppol.is_static():
+ setattr(pycpptype, meth_name, make_static_function(cpptype, meth_name, cppol))
+ else:
+ setattr(pycpptype, meth_name, make_method(meth_name, cppol))
+
# add all data members to the dictionary of the class to be created, and
# static ones also to the meta class (needed for property setters)
for dm_name in cpptype.get_data_member_names():
cppdm = cpptype.get_data_member(dm_name)
- d[dm_name] = cppdm
+ setattr(pycpptype, dm_name, cppdm)
if cppdm.is_static():
setattr(metacpp, dm_name, cppdm)
- # create the python-side C++ class representation
- pycpptype = metacpp(class_name, bases, d)
-
- # cache result and return
- _existing_cppitems[class_name] = pycpptype
return pycpptype
@@ -136,14 +135,13 @@
else:
fullname = name
- # lookup class
+ # lookup class ...
try:
return _existing_cppitems[fullname]
except KeyError:
pass
- # if failed, create
-
+ # ... if lookup failed, create
cppitem = cppyy._type_byname(fullname)
if cppitem.is_namespace():
return make_cppnamespace(fullname, cppitem)
@@ -160,9 +158,7 @@
cppitem = get_cppitem(attr)
self.__dict__[attr] = cppitem
return cppitem
- except TypeError, e:
- import traceback
- traceback.print_exc()
+ except TypeError:
raise AttributeError("'gbl' object has no attribute '%s'" % attr)
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
@@ -167,7 +167,11 @@
char* cppyy_method_name(cppyy_typehandle_t handle, int method_index) {
Reflex::Scope s = scope_from_handle(handle);
Reflex::Member m = s.FunctionMemberAt(method_index);
- std::string name = m.Name();
+ std::string name;
+ if (m.IsConstructor())
+ name = s.Name(Reflex::FINAL); // to get proper name for templates
+ else
+ name = m.Name();
return cppstring_to_cstring(name);
}
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
@@ -1,4 +1,4 @@
-all: example01Dict.so datatypesDict.so
+all: example01Dict.so datatypesDict.so advancedcppDict.so stltypesDict.so
ROOTSYS := ${ROOTSYS}
@@ -12,10 +12,10 @@
ifeq ($(shell $(genreflex) --help | grep -- --with-methptrgetter),)
genreflexflags=
- cppflags2=
+ cppflags2=-O3
else
genreflexflags=--with-methptrgetter
- cppflags2=-Wno-pmf-conversions
+ cppflags2=-Wno-pmf-conversions -O3
endif
example01Dict.so: example01.cxx example01.h
@@ -29,3 +29,7 @@
advancedcppDict.so: advancedcpp.cxx advancedcpp.h
$(genreflex) advancedcpp.h $(genreflexflags)
g++ -o $@ advancedcpp_rflx.cpp advancedcpp.cxx -shared -lReflex $(cppflags) $(cppflags2)
+
+stltypesDict.so: stltypes.cxx stltypes.h stltypes.xml
+ $(genreflex) stltypes.h --selection=stltypes.xml
+ g++ -o $@ stltypes_rflx.cpp stltypes.cxx -shared -lReflex $(cppflags) $(cppflags2)
diff --git a/pypy/module/cppyy/test/stltypes.cxx b/pypy/module/cppyy/test/stltypes.cxx
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/stltypes.cxx
@@ -0,0 +1,1 @@
+#include "stltypes.h"
diff --git a/pypy/module/cppyy/test/stltypes.h b/pypy/module/cppyy/test/stltypes.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/stltypes.h
@@ -0,0 +1,11 @@
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#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 > >;
+
+STLTYPES_EXPLICIT_INSTANTIATION(vector, int)
diff --git a/pypy/module/cppyy/test/stltypes.xml b/pypy/module/cppyy/test/stltypes.xml
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/stltypes.xml
@@ -0,0 +1,10 @@
+<lcgdict>
+
+ <class pattern="std::vector<*>" />
+ <class pattern="__gnu_cxx::__normal_iterator<*>" />
+ <class pattern="__gnu_cxx::new_allocator<*>" />
+ <class pattern="std::_Vector_base<*>" />
+ <class pattern="std::_Vector_base<*>::_Vector_impl" />
+ <class pattern="std::allocator<*>" />
+
+</lcgdict>
diff --git a/pypy/module/cppyy/test/test_helper.py b/pypy/module/cppyy/test/test_helper.py
--- a/pypy/module/cppyy/test/test_helper.py
+++ b/pypy/module/cppyy/test/test_helper.py
@@ -8,7 +8,32 @@
assert helper.array_size("unsigned long int[5]") == 5
+def test_array_size():
+ assert helper.array_size("int[5]") == 5
+
+
def test_clean_type():
assert helper.clean_type(" int***") == "int"
+ assert helper.clean_type("int* const *&") == "int"
assert helper.clean_type("std::vector<int>&") == "std::vector<int>"
+ assert helper.clean_type("const std::vector<int>&") == "std::vector<int>"
+ assert helper.clean_type("std::vector<std::vector<int> >" ) == "std::vector<std::vector<int> >"
assert helper.clean_type("unsigned short int[3]") == "unsigned short int"
+
+
+def test_operator_mapping():
+ assert helper.map_operator_name("operator[]", 1) == "__getitem__"
+ assert helper.map_operator_name("operator()", 1) == "__call__"
+ assert helper.map_operator_name("operator%", 1) == "__mod__"
+ assert helper.map_operator_name("operator**", 1) == "__pow__"
+ assert helper.map_operator_name("operator<<", 1) == "__lshift__"
+ assert helper.map_operator_name("operator|", 1) == "__or__"
+
+ assert helper.map_operator_name("operator*", 1) == "__mul__"
+ assert helper.map_operator_name("operator*", 0) == "__deref__"
+
+ assert helper.map_operator_name("operator+", 1) == "__add__"
+ assert helper.map_operator_name("operator+", 0) == "__pos__"
+
+ assert helper.map_operator_name("func", 0) == "func"
+ assert helper.map_operator_name("some_method", 0) == "some_method"
diff --git a/pypy/module/cppyy/test/test_stltypes.py b/pypy/module/cppyy/test/test_stltypes.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/test_stltypes.py
@@ -0,0 +1,44 @@
+import py, os, sys
+from pypy.conftest import gettestobjspace
+
+
+currpath = py.path.local(__file__).dirpath()
+shared_lib = str(currpath.join("stltypesDict.so"))
+
+space = gettestobjspace(usemodules=['cppyy'])
+
+def setup_module(mod):
+ if sys.platform == 'win32':
+ py.test.skip("win32 not supported so far")
+ err = os.system("cd '%s' && make stltypesDict.so" % currpath)
+ if err:
+ raise OSError("'make' failed (see stderr)")
+
+class AppTestSTL:
+ def setup_class(cls):
+ cls.space = space
+ env = os.environ
+ cls.w_N = space.wrap(13)
+ cls.w_shared_lib = space.wrap(shared_lib)
+ cls.w_datatypes = cls.space.appexec([], """():
+ import cppyy
+ return cppyy.load_lib(%r)""" % (shared_lib, ))
+
+ def test1BuiltinTypeVectorType( self ):
+ """Test access to a vector<int>"""
+
+ import cppyy
+
+ assert cppyy.gbl.std is cppyy.gbl.std
+# assert cppyy.gbl.std.vector is cppyy.gbl.std.vector
+
+ tv = getattr(cppyy.gbl.std,'vector<int>')
+
+ v = tv()
+ for i in range(self.N):
+ v.push_back(i)
+ assert v.size() == i+1
+# assert v[i] == i
+
+# assert len(v) == self.N
+ v.destruct()
More information about the pypy-commit
mailing list