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

Reply via email to