[pypy-commit] pypy reflex-support: make sure that typedefs point to the same python class (this includes a full overhaul of the caching of cpp classes)
wlav
noreply at buildbot.pypy.org
Thu Mar 8 01:44:41 CET 2012
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r53265:0055136f2633
Date: 2012-03-07 15:10 -0800
http://bitbucket.org/pypy/pypy/changeset/0055136f2633/
Log: make sure that typedefs point to the same python class (this
includes a full overhaul of the caching of cpp classes)
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
@@ -5,6 +5,7 @@
interpleveldefs = {
'_load_dictionary' : 'interp_cppyy.load_dictionary',
+ '_resolve_name' : 'interp_cppyy.resolve_name',
'_type_byname' : 'interp_cppyy.type_byname',
'_template_byname' : 'interp_cppyy.template_byname',
'CPPInstance' : 'interp_cppyy.W_CPPInstance',
diff --git a/pypy/module/cppyy/capi/__init__.py b/pypy/module/cppyy/capi/__init__.py
--- a/pypy/module/cppyy/capi/__init__.py
+++ b/pypy/module/cppyy/capi/__init__.py
@@ -33,6 +33,10 @@
c_load_dictionary = backend.c_load_dictionary
# name to opaque C++ scope representation ------------------------------------
+c_resolve_name = rffi.llexternal(
+ "cppyy_resolve_name",
+ [rffi.CCHARP], rffi.CCHARP,
+ compilation_info=backend.eci)
c_get_scope = rffi.llexternal(
"cppyy_get_scope",
[rffi.CCHARP], C_SCOPE,
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
@@ -592,7 +592,7 @@
# 5) generalized cases (covers basically all user classes)
# 6) void converter, which fails on use
- from pypy.module.cppyy import interp_cppyy
+ name = capi.charp2str_free(capi.c_resolve_name(name))
# 1) full, exact match
try:
@@ -624,6 +624,7 @@
pass
# 5) generalized cases (covers basically all user classes)
+ from pypy.module.cppyy import interp_cppyy
cpptype = interp_cppyy.type_byname(space, clean_name)
if cpptype:
# type check for the benefit of the annotator
@@ -665,16 +666,10 @@
_converters["void*&"] = VoidPtrRefConverter
# special cases
-_converters["std::string"] = StdStringConverter
-_converters["string"] = _converters["std::string"]
_converters["std::basic_string<char>"] = StdStringConverter
_converters["basic_string<char>"] = _converters["std::basic_string<char>"]
-_converters["const std::string&"] = StdStringConverter # TODO: shouldn't copy
-_converters["const string&"] = _converters["const std::string&"]
-_converters["const std::basic_string<char>&"] = StdStringConverter
+_converters["const std::basic_string<char>&"] = StdStringConverter # TODO: shouldn't copy
_converters["const basic_string<char>&"] = _converters["const std::basic_string<char>&"]
-_converters["std::string&"] = StdStringRefConverter
-_converters["string&"] = _converters["std::string&"]
_converters["std::basic_string<char>&"] = StdStringRefConverter
_converters["basic_string<char>&"] = _converters["std::basic_string<char>&"]
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
@@ -292,7 +292,7 @@
#
# If all fails, a default is used, which can be ignored at least until use.
- from pypy.module.cppyy import interp_cppyy
+ name = capi.charp2str_free(capi.c_resolve_name(name))
# 1) full, qualified match
try:
@@ -318,6 +318,7 @@
pass
# 3) types/classes, either by ref/ptr or by value
+ from pypy.module.cppyy import interp_cppyy
cpptype = interp_cppyy.type_byname(space, clean_name)
if cpptype:
# type check for the benefit of the annotator
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
@@ -99,14 +99,12 @@
# is put at the end only as it is unlikely and may trigger unwanted
# errors in class loaders in the backend, because a typical operator
# name is illegal as a class name)
- handle = capi.c_get_scope(op)
- if handle:
- op = capi.charp2str_free(capi.c_final_name(handle))
+ true_op = capi.charp2str_free(capi.c_resolve_name(op))
- try:
- return _operator_mappings[op]
- except KeyError:
- pass
+ try:
+ return _operator_mappings[true_op]
+ except KeyError:
+ pass
# might get here, as not all operator methods handled (although some with
# no python equivalent, such as new, delete, etc., are simply retained)
diff --git a/pypy/module/cppyy/include/capi.h b/pypy/module/cppyy/include/capi.h
--- a/pypy/module/cppyy/include/capi.h
+++ b/pypy/module/cppyy/include/capi.h
@@ -14,6 +14,7 @@
typedef void* (*cppyy_methptrgetter_t)(cppyy_object_t);
/* name to opaque C++ scope representation -------------------------------- */
+ char* cppyy_resolve_name(const char* cppitem_name);
cppyy_scope_t cppyy_get_scope(const char* scope_name);
cppyy_type_t cppyy_get_template(const char* template_name);
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
@@ -32,14 +32,20 @@
self.r_cpptemplate_cache = {}
@unwrap_spec(name=str)
+def resolve_name(space, name=str):
+ return space.wrap(capi.charp2str_free(capi.c_resolve_name(name)))
+
+ at unwrap_spec(name=str)
def type_byname(space, name):
+ true_name = capi.charp2str_free(capi.c_resolve_name(name))
+
state = space.fromcache(State)
try:
- return state.r_cppscope_cache[name]
+ return state.r_cppscope_cache[true_name]
except KeyError:
pass
- cppscope = capi.c_get_scope(name)
+ cppscope = capi.c_get_scope(true_name)
assert lltype.typeOf(cppscope) == capi.C_SCOPE
if cppscope:
final_name = capi.charp2str_free(capi.c_final_name(cppscope))
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
@@ -7,12 +7,11 @@
# classes for inheritance. Both are python classes, though, and refactoring
# may be in order at some point.
class CppyyScopeMeta(type):
- def __getattr__(self, attr):
+ def __getattr__(self, name):
try:
- cppitem = get_cppitem(attr, self)
- return cppitem
+ return get_cppitem(self, name) # will cache on self
except TypeError:
- raise AttributeError("%s object has no attribute '%s'" % (self, attr))
+ raise AttributeError("%s object has no attribute '%s'" % (self, name))
class CppyyNamespaceMeta(CppyyScopeMeta):
pass
@@ -98,7 +97,7 @@
metans = type(CppyyNamespaceMeta)(namespace_name+'_meta', (CppyyNamespaceMeta,), {})
if cppns:
- nsdct = {"_cpp_proxy" : cppns }
+ nsdct = {"_cpp_proxy" : cppns}
else:
nsdct = dict()
def cpp_proxy_loader(cls):
@@ -123,13 +122,7 @@
setattr(metans, dm, pydm)
# create the python-side C++ namespace representation
- pycppns = metans(namespace_name, (object,), nsdct)
-
- # cache result and return
- _existing_cppitems[namespace_name] = pycppns
-
- return pycppns
-
+ return metans(namespace_name, (object,), nsdct)
def _drop_cycles(bases):
# TODO: figure this out, as it seems to be a PyPy bug?!
@@ -152,12 +145,19 @@
return constructor_overload.call(None, cls, *args)
return __new__
-def make_cppclass(class_name, cpptype):
+def make_cppclass(scope, class_name, final_class_name, cpptype):
# get a list of base classes for class creation
bases = [get_cppclass(base) for base in cpptype.get_base_names()]
if not bases:
bases = [CPPObject,]
+ else:
+ # it's technically possible that the required class now has been built
+ # if one of the base classes uses it in e.g. a function interface
+ try:
+ return scope.__dict__[final_class_name]
+ except KeyError:
+ pass
# create a meta class to allow properties (for static data write access)
metabases = [type(base) for base in bases]
@@ -170,7 +170,7 @@
pycpptype = metacpp(class_name, _drop_cycles(bases), d)
# cache result early so that the class methods can find the class itself
- _existing_cppitems[class_name] = pycpptype
+ setattr(scope, final_class_name, pycpptype)
# insert (static) methods into the class dictionary
for meth_name in cpptype.get_method_names():
@@ -193,25 +193,17 @@
_pythonize(pycpptype)
return pycpptype
-def make_cpptemplatetype(template_name, scope):
+def make_cpptemplatetype(scope, template_name):
return CppyyTemplateType(scope, template_name)
-_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
+def get_cppitem(scope, name):
+ # resolve typedefs/aliases
+ full_name = (scope == gbl) and name or (scope.__name__+'::'+name)
+ true_name = cppyy._resolve_name(full_name)
+ if true_name != full_name:
+ return get_cppclass(true_name)
- # lookup if already created (e.g. as a function return type)
- try:
- return _existing_cppitems[fullname]
- except KeyError:
- pass
-
- # ... if lookup failed, create as appropriate
pycppitem = None
# namespaces are "open"; TODO: classes are too (template methods, inner classes ...)
@@ -222,22 +214,20 @@
_loaded_dictionaries_isdirty = False
# classes
- cppitem = cppyy._type_byname(fullname)
+ cppitem = cppyy._type_byname(true_name)
if cppitem:
if cppitem.is_namespace():
- pycppitem = make_cppnamespace(fullname, cppitem)
+ pycppitem = make_cppnamespace(true_name, cppitem)
+ setattr(scope, name, pycppitem)
else:
- pycppitem = make_cppclass(fullname, cppitem)
- _existing_cppitems[fullname] = pycppitem
- scope.__dict__[name] = pycppitem
+ pycppitem = make_cppclass(scope, true_name, name, cppitem)
# templates
if not cppitem:
- cppitem = cppyy._template_byname(fullname)
+ cppitem = cppyy._template_byname(true_name)
if cppitem:
- pycppitem = make_cpptemplatetype(name, scope)
- _existing_cppitems[fullname] = pycppitem
- scope.__dict__[name] = pycppitem
+ pycppitem = make_cpptemplatetype(scope, name)
+ setattr(scope, name, pycppitem)
# functions
if not cppitem:
@@ -266,7 +256,28 @@
raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name))
-get_cppclass = get_cppitem # TODO: restrict to classes only (?)
+
+def scope_splitter(name):
+ is_open_template, scope = 0, ""
+ for c in name:
+ if c == ':' and not is_open_template:
+ if scope:
+ yield scope
+ scope = ""
+ continue
+ elif c == '<':
+ is_open_template += 1
+ elif c == '>':
+ is_open_template -= 1
+ scope += c
+ yield scope
+
+def get_cppclass(name):
+ # break up the name, to walk the scopes and get the class recursively
+ scope = gbl
+ for part in scope_splitter(name):
+ scope = getattr(scope, part)
+ return scope
def _pythonize(pyclass):
@@ -289,7 +300,7 @@
pyclass.__iter__ = __iter__
# string comparisons
- if pyclass.__name__ == 'std::string' or pyclass.__name__ == 'string':
+ if pyclass.__name__ == 'std::basic_string<char>':
def eq(self, other):
if type(other) == pyclass:
return self.c_str() == other.c_str()
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
@@ -48,6 +48,16 @@
/* name to opaque C++ scope representation -------------------------------- */
+char* cppyy_resolve_name(const char* cppitem_name) {
+ Reflex::Scope s = Reflex::Scope::ByName(cppitem_name);
+ if (s.IsEnum())
+ return cppstring_to_cstring("unsigned int");
+ const std::string& name = s.Name(Reflex::SCOPED|Reflex::QUALIFIED|Reflex::FINAL);
+ if (name.empty())
+ return cppstring_to_cstring(cppitem_name);
+ return cppstring_to_cstring(name);
+}
+
cppyy_scope_t cppyy_get_scope(const char* scope_name) {
Reflex::Scope s = Reflex::Scope::ByName(scope_name);
if (s.IsEnum()) // pretend to be builtin by returning 0
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
@@ -91,6 +91,10 @@
};
+// typedefs
+typedef example01 example01_t;
+
+
// special case naming
class z_ {
public:
diff --git a/pypy/module/cppyy/test/example01.xml b/pypy/module/cppyy/test/example01.xml
--- a/pypy/module/cppyy/test/example01.xml
+++ b/pypy/module/cppyy/test/example01.xml
@@ -2,6 +2,7 @@
<class name="payload" />
<class name="example01" />
+ <class name="example01_t" />
<class name="std::string" />
<class name="z_" />
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
@@ -290,7 +290,14 @@
assert e.overloadedAddDataToInt(4, 5) == 10
assert e.overloadedAddDataToInt(6, 7, 8) == 22
- def test12_underscore_in_class_name(self):
+ def test12_typedefs(self):
+ """Test access and use of typedefs"""
+
+ import cppyy
+
+ assert cppyy.gbl.example01 == cppyy.gbl.example01_t
+
+ def test13_underscore_in_class_name(self):
"""Test recognition of '_' as part of a valid class name"""
import cppyy
More information about the pypy-commit
mailing list