Author: Wim Lavrijsen <[email protected]>
Branch: reflex-support
Changeset: r62672:fd24e89f5e2c
Date: 2013-03-21 17:44 -0700
http://bitbucket.org/pypy/pypy/changeset/fd24e89f5e2c/
Log: support for templated member functions
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
@@ -367,6 +367,30 @@
def c_method_signature(cppscope, index):
return charp2str_free(_c_method_signature(cppscope.handle, index))
+_c_method_is_template = rffi.llexternal(
+ "cppyy_method_is_template",
+ [C_SCOPE, C_INDEX], rffi.INT,
+ threadsafe=ts_reflect,
+ compilation_info=backend.eci)
+def c_method_is_template(cppscope, index):
+ return _c_method_is_template(cppscope.handle, index)
+_c_method_num_template_args = rffi.llexternal(
+ "cppyy_method_num_template_args",
+ [C_SCOPE, C_INDEX], rffi.INT,
+ threadsafe=ts_reflect,
+ compilation_info=backend.eci)
+_c_method_template_arg_name = rffi.llexternal(
+ "cppyy_method_template_arg_name",
+ [C_SCOPE, C_INDEX, C_INDEX], rffi.CCHARP,
+ threadsafe=ts_reflect,
+ compilation_info=backend.eci)
+def c_template_args(cppscope, index):
+ nargs = _c_method_num_template_args(cppscope.handle, index)
+ args = [c_resolve_name(
+ charp2str_free(_c_method_template_arg_name(cppscope.handle, index,
iarg)))
+ for iarg in range(nargs)]
+ return args
+
_c_get_method = rffi.llexternal(
"cppyy_get_method",
[C_SCOPE, C_INDEX], C_METHOD,
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
@@ -81,6 +81,10 @@
char* cppyy_method_arg_default(cppyy_scope_t scope, cppyy_index_t idx, int
arg_index);
char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx);
+ int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx);
+ int cppyy_method_num_template_args(cppyy_scope_t scope, cppyy_index_t idx);
+ char* cppyy_method_template_arg_name(cppyy_scope_t scope, cppyy_index_t
idx, cppyy_index_t iarg);
+
cppyy_method_t cppyy_get_method(cppyy_scope_t scope, cppyy_index_t idx);
cppyy_index_t cppyy_get_global_operator(
cppyy_scope_t scope, cppyy_scope_t lc, cppyy_scope_t rc, const char*
op);
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
@@ -373,6 +373,38 @@
return "CPPFunction: %s" % self.signature()
+class CPPTemplatedMember(object):
+ """Method dispatcher that first needs to resolve the template instance.
+ Note that the derivation is from object: the CPPMethod is a data member."""
+
+ _attrs_ = ['space', 'templ_args', 'method']
+ _immutable_ = True
+
+ def __init__(self, space, templ_args, containing_scope, method_index,
arg_defs, args_required):
+ self.space = space
+ self.templ_args = templ_args
+ self.method = CPPMethod(space, containing_scope, method_index,
arg_defs, args_required)
+
+ def call(self, cppthis, args_w):
+ assert lltype.typeOf(cppthis) == capi.C_OBJECT
+ for i in range(len(args_w)):
+ try:
+ s = self.space.str_w(args_w[i])
+ except OperationError:
+ s = self.space.str_w(self.space.getattr(args_w[i],
self.space.wrap('__name__')))
+ s = capi.c_resolve_name(s)
+ if s != self.templ_args[i]:
+ raise OperationError(self.space.w_TypeError, self.space.wrap(
+ "non-matching template (got %s where %s expected" % (s,
self.templ_args[i])))
+ return W_CPPBoundMethod(cppthis, self.method)
+
+ def signature(self):
+ return self.method.signature()
+
+ def __repr__(self):
+ return "CPPTemplatedMember: %s" % self.signature()
+
+
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
@@ -500,6 +532,22 @@
)
+class W_CPPBoundMethod(Wrappable):
+ _attrs_ = ['cppthis', 'method']
+
+ def __init__(self, cppthis, method):
+ self.cppthis = cppthis
+ self.method = method
+
+ def __call__(self, args_w):
+ return self.method.call(self.cppthis, args_w)
+
+W_CPPBoundMethod.typedef = TypeDef(
+ 'CPPBoundMethod',
+ __call__ = interp2app(W_CPPBoundMethod.__call__),
+)
+
+
class W_CPPDataMember(Wrappable):
_attrs_ = ['space', 'scope', 'converter', 'offset']
_immutable_fields = ['scope', 'converter', 'offset']
@@ -766,7 +814,6 @@
self.default_constructor = None
def _make_cppfunction(self, pyname, index):
- default_constructor = False
num_args = capi.c_method_num_args(self, index)
args_required = capi.c_method_req_args(self, index)
arg_defs = []
@@ -775,18 +822,18 @@
arg_dflt = capi.c_method_arg_default(self, index, i)
arg_defs.append((arg_type, arg_dflt))
if capi.c_is_constructor(self, index):
- cls = CPPConstructor
+ cppfunction = CPPConstructor(self.space, self, index, arg_defs,
args_required)
if args_required == 0:
- default_constructor = True
+ self.default_constructor = cppfunction
elif capi.c_is_staticmethod(self, index):
- cls = CPPFunction
+ cppfunction = CPPFunction(self.space, self, index, arg_defs,
args_required)
elif pyname == "__setitem__":
- cls = CPPSetItem
+ cppfunction = CPPSetItem(self.space, self, index, arg_defs,
args_required)
+ elif capi.c_method_is_template(self, index):
+ templ_args = capi.c_template_args(self, index)
+ cppfunction = CPPTemplatedMember(self.space, templ_args, self,
index, arg_defs, args_required)
else:
- cls = CPPMethod
- cppfunction = cls(self.space, self, index, arg_defs, args_required)
- if default_constructor:
- self.default_constructor = cppfunction
+ cppfunction = CPPMethod(self.space, self, index, arg_defs,
args_required)
return cppfunction
def _find_datamembers(self):
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
@@ -384,7 +384,11 @@
std::string name;
if (m.IsConstructor())
name = s.Name(Reflex::FINAL); // to get proper name for templates
- else
+ else if (m.IsTemplateInstance()) {
+ name = m.Name();
+ std::string::size_type pos = name.find("<");
+ name = name.substr(0, pos); // strip template argument portion for
overload
+ } else
name = m.Name();
return cppstring_to_cstring(name);
}
@@ -444,10 +448,31 @@
return cppstring_to_cstring(sig.str());
}
+int cppyy_method_is_template(cppyy_scope_t handle, cppyy_index_t method_index)
{
+ Reflex::Scope s = scope_from_handle(handle);
+ Reflex::Member m = s.FunctionMemberAt(method_index);
+ return m.IsTemplateInstance();
+}
+
+int cppyy_method_num_template_args(cppyy_scope_t handle, cppyy_index_t
method_index) {
+ Reflex::Scope s = scope_from_handle(handle);
+ Reflex::Member m = s.FunctionMemberAt(method_index);
+ assert(m.IsTemplateInstance());
+ return m.TemplateArgumentSize();
+}
+
+char* cppyy_method_template_arg_name(
+ cppyy_scope_t handle, cppyy_index_t method_index, cppyy_index_t iarg) {
+ Reflex::Scope s = scope_from_handle(handle);
+ Reflex::Member m = s.FunctionMemberAt(method_index);
+ assert(m.IsTemplateInstance());
+ return cppstring_to_cstring(
+ m.TemplateArgumentAt(iarg).Name(Reflex::SCOPED|Reflex::QUALIFIED));
+}
+
cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t
method_index) {
Reflex::Scope s = scope_from_handle(handle);
Reflex::Member m = s.FunctionMemberAt(method_index);
- assert(m.IsFunctionMember());
return (cppyy_method_t)m.Stubfunction();
}
diff --git a/pypy/module/cppyy/test/advancedcpp.cxx
b/pypy/module/cppyy/test/advancedcpp.cxx
--- a/pypy/module/cppyy/test/advancedcpp.cxx
+++ b/pypy/module/cppyy/test/advancedcpp.cxx
@@ -103,3 +103,14 @@
--s_instances;
::operator delete(p);
}
+
+
+// more template testing
+long my_templated_method_class::get_size() { return -1; }
+
+long my_templated_method_class::get_char_size() { return (long)sizeof(char);
}
+long my_templated_method_class::get_int_size() { return (long)sizeof(int); }
+long my_templated_method_class::get_long_size() { return (long)sizeof(long);
}
+long my_templated_method_class::get_float_size() { return
(long)sizeof(float); }
+long my_templated_method_class::get_double_size() { return
(long)sizeof(double); }
+long my_templated_method_class::get_self_size() { return
(long)sizeof(my_templated_method_class); }
diff --git a/pypy/module/cppyy/test/advancedcpp.h
b/pypy/module/cppyy/test/advancedcpp.h
--- a/pypy/module/cppyy/test/advancedcpp.h
+++ b/pypy/module/cppyy/test/advancedcpp.h
@@ -371,3 +371,49 @@
void* operator new(std::size_t, void* p) throw();
void operator delete(void* p, std::size_t size);
};
+
+
+//===========================================================================
+template<class T> // more template testing
+class my_templated_class {
+public:
+ T m_b;
+};
+
+template<class T>
+T my_templated_function(T t) { return t; }
+
+template class my_templated_class<std::vector<float> >;
+template int my_templated_function<int>(int);
+template double my_templated_function<double>(double);
+
+class my_templated_method_class {
+public:
+ long get_size(); // to get around bug in genreflex
+ template<class B> long get_size();
+
+ long get_char_size();
+ long get_int_size();
+ long get_long_size();
+ long get_float_size();
+ long get_double_size();
+
+ long get_self_size();
+
+private:
+ double m_data[3];
+};
+
+template<class B>
+inline long my_templated_method_class::get_size() {
+ return sizeof(B);
+}
+
+template long my_templated_method_class::get_size<char>();
+template long my_templated_method_class::get_size<int>();
+template long my_templated_method_class::get_size<long>();
+template long my_templated_method_class::get_size<float>();
+template long my_templated_method_class::get_size<double>();
+
+typedef my_templated_method_class my_typedef_t;
+template long my_templated_method_class::get_size<my_typedef_t>();
diff --git a/pypy/module/cppyy/test/advancedcpp.xml
b/pypy/module/cppyy/test/advancedcpp.xml
--- a/pypy/module/cppyy/test/advancedcpp.xml
+++ b/pypy/module/cppyy/test/advancedcpp.xml
@@ -39,4 +39,10 @@
<class name="new_overloader" />
+ <class name="std::vector<float>" />
+ <class pattern="my_templated_class<*>" />
+ <function pattern="my_templated_function<*>" />
+ <class name="my_templated_method_class" />
+ <class name="my_typedef_t" />
+
</lcgdict>
diff --git a/pypy/module/cppyy/test/test_advancedcpp.py
b/pypy/module/cppyy/test/test_advancedcpp.py
--- a/pypy/module/cppyy/test/test_advancedcpp.py
+++ b/pypy/module/cppyy/test/test_advancedcpp.py
@@ -539,3 +539,31 @@
import gc
gc.collect()
assert cppyy.gbl.new_overloader.s_instances == 0
+
+ def test15_template_instantiation_with_vector_of_float(self):
+ """Test template instantiation with a std::vector<float>"""
+
+ import cppyy
+
+ # the following will simply fail if there is a naming problem (e.g.
+ # std::, allocator<int>, etc., etc.); note the parsing required ...
+ b = cppyy.gbl.my_templated_class(cppyy.gbl.std.vector(float))()
+
+ for i in range(5):
+ b.m_b.push_back(i)
+ assert round(b.m_b[i], 5) == float(i)
+
+ def test16_template_member_functions(self):
+ """Test template member functions lookup and calls"""
+
+ import cppyy
+
+ m = cppyy.gbl.my_templated_method_class()
+
+ assert m.get_size('char')() == m.get_char_size()
+ assert m.get_size(int)() == m.get_int_size()
+ assert m.get_size(long)() == m.get_long_size()
+ assert m.get_size(float)() == m.get_float_size()
+ assert m.get_size('double')() == m.get_double_size()
+ assert m.get_size('my_templated_method_class')() == m.get_self_size()
+ assert m.get_size('my_typedef_t')() == m.get_self_size()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit