[pypy-commit] pypy reflex-support: o) default function values on fast path for integer types

wlav noreply at buildbot.pypy.org
Sat Feb 4 05:39:20 CET 2012


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r52079:2d3f34563372
Date: 2012-02-03 16:12 -0800
http://bitbucket.org/pypy/pypy/changeset/2d3f34563372/

Log:	o) default function values on fast path for integer types

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
@@ -168,6 +168,10 @@
     "cppyy_method_arg_type",
     [C_TYPEHANDLE, rffi.INT, rffi.INT], rffi.CCHARP,
     compilation_info=backend.eci)
+c_method_arg_default = rffi.llexternal(
+    "cppyy_method_arg_default",
+    [C_TYPEHANDLE, rffi.INT, rffi.INT], rffi.CCHARP,
+    compilation_info=backend.eci)
 
 c_is_constructor = rffi.llexternal(
     "cppyy_is_constructor",
@@ -204,6 +208,11 @@
     [C_TYPEHANDLE, rffi.INT], rffi.INT,
     compilation_info=backend.eci)
 
+c_atoi = rffi.llexternal(
+    "cppyy_atoi",
+    [rffi.CCHARP], rffi.INT,
+    compilation_info=backend.eci)
+
 c_free = rffi.llexternal(
     "cppyy_free",
     [rffi.VOIDP], lltype.Void,
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
@@ -32,7 +32,7 @@
 
     name = ""
 
-    def __init__(self, space, array_size):
+    def __init__(self, space, extra):
         pass
 
     @jit.dont_look_inside
@@ -221,6 +221,9 @@
     _immutable_ = True
     libffitype = libffi.types.sint
 
+    def __init__(self, space, default):
+        self.default = capi.c_atoi(default)
+
     def _unwrap_object(self, space, w_obj):
         return rffi.cast(rffi.INT, space.c_int_w(w_obj))
 
@@ -231,6 +234,9 @@
     def convert_argument_libffi(self, space, w_obj, argchain):
         argchain.arg(self._unwrap_object(space, w_obj))
 
+    def default_argument_libffi(self, space, argchain):
+        argchain.arg(self.default)
+
     def from_memory(self, space, w_obj, w_type, offset):
         address = self._get_raw_address(space, w_obj, offset)
         intptr = rffi.cast(rffi.INTP, address)
@@ -558,11 +564,13 @@
         return interp_cppyy.new_instance(space, w_type, self.cpptype, address, False)
 
 
-_converters = {}
-def get_converter(space, name):
+_converters = {}         # builtin and custom types
+_a_converters = {}       # array and ptr versions of above
+def get_converter(space, name, default):
     from pypy.module.cppyy import interp_cppyy
     # The matching of the name to a converter should follow:
     #   1) full, exact match
+    #       1a) const-removed match
     #   2) match of decorated, unqualified type
     #   3) accept const ref as by value
     #   4) accept ref as pointer
@@ -573,7 +581,13 @@
 
     #   1) full, exact match
     try:
-        return _converters[name](space, -1)
+        return _converters[name](space, default)
+    except KeyError, k:
+        pass
+
+    #   1a) const-removed match
+    try:
+        return _converters[helper.remove_const(name)](space, default)
     except KeyError, k:
         pass
 
@@ -583,14 +597,14 @@
     try:
         # array_index may be negative to indicate no size or no size found
         array_size = helper.array_size(name)
-        return _converters[clean_name+compound](space, array_size)
+        return _a_converters[clean_name+compound](space, array_size)
     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)
+            return _converters[clean_name](space, default)
         except KeyError:
             pass
 
@@ -617,42 +631,44 @@
 _converters["unsigned char"]            = CharConverter
 _converters["short int"]                = ShortConverter
 _converters["short"]                    = _converters["short int"]
-_converters["short int*"]               = ShortPtrConverter
-_converters["short*"]                   = _converters["short int*"]
-_converters["short int[]"]              = ShortArrayConverter
-_converters["short[]"]                  = _converters["short int[]"]
 _converters["unsigned short int"]       = ShortConverter
 _converters["unsigned short"]           = _converters["unsigned short int"]
-_converters["unsigned short int*"]      = ShortPtrConverter
-_converters["unsigned short*"]          = _converters["unsigned short int*"]
-_converters["unsigned short int[]"]     = ShortArrayConverter
-_converters["unsigned short[]"]         = _converters["unsigned short int[]"]
 _converters["int"]                      = IntConverter
-_converters["int*"]                     = IntPtrConverter
-_converters["int[]"]                    = IntArrayConverter
 _converters["unsigned int"]             = UnsignedIntConverter
-_converters["unsigned int*"]            = UnsignedIntPtrConverter
-_converters["unsigned int[]"]           = UnsignedIntArrayConverter
 _converters["long int"]                 = LongConverter
 _converters["long"]                     = _converters["long int"]
-_converters["long int*"]                = LongPtrConverter
-_converters["long*"]                    = _converters["long int*"]
-_converters["long int[]"]               = LongArrayConverter
-_converters["long[]"]                   = _converters["long int[]"]
 _converters["unsigned long int"]        = UnsignedLongConverter
 _converters["unsigned long"]            = _converters["unsigned long int"]
-_converters["unsigned long int*"]       = LongPtrConverter
-_converters["unsigned long*"]           = _converters["unsigned long int*"]
-_converters["unsigned long int[]"]      = LongArrayConverter
-_converters["unsigned long[]"]          = _converters["unsigned long int[]"]
 _converters["float"]                    = FloatConverter
-_converters["float*"]                   = FloatPtrConverter
-_converters["float[]"]                  = FloatArrayConverter
 _converters["double"]                   = DoubleConverter
-_converters["double*"]                  = DoublePtrConverter
-_converters["double[]"]                 = DoubleArrayConverter
 _converters["const char*"]              = CStringConverter
 _converters["char*"]                    = CStringConverter
 _converters["void*"]                    = VoidPtrConverter
 _converters["void**"]                   = VoidPtrPtrConverter
 _converters["void*&"]                   = VoidPtrRefConverter
+
+# it should be possible to generate these:
+_a_converters["short int*"]               = ShortPtrConverter
+_a_converters["short*"]                   = _a_converters["short int*"]
+_a_converters["short int[]"]              = ShortArrayConverter
+_a_converters["short[]"]                  = _a_converters["short int[]"]
+_a_converters["unsigned short int*"]      = ShortPtrConverter
+_a_converters["unsigned short*"]          = _a_converters["unsigned short int*"]
+_a_converters["unsigned short int[]"]     = ShortArrayConverter
+_a_converters["unsigned short[]"]         = _a_converters["unsigned short int[]"]
+_a_converters["int*"]                     = IntPtrConverter
+_a_converters["int[]"]                    = IntArrayConverter
+_a_converters["unsigned int*"]            = UnsignedIntPtrConverter
+_a_converters["unsigned int[]"]           = UnsignedIntArrayConverter
+_a_converters["long int*"]                = LongPtrConverter
+_a_converters["long*"]                    = _a_converters["long int*"]
+_a_converters["long int[]"]               = LongArrayConverter
+_a_converters["long[]"]                   = _a_converters["long int[]"]
+_a_converters["unsigned long int*"]       = LongPtrConverter
+_a_converters["unsigned long*"]           = _a_converters["unsigned long int*"]
+_a_converters["unsigned long int[]"]      = LongArrayConverter
+_a_converters["unsigned long[]"]          = _a_converters["unsigned long int[]"]
+_a_converters["float*"]                   = FloatPtrConverter
+_a_converters["float[]"]                  = FloatArrayConverter
+_a_converters["double*"]                  = DoublePtrConverter
+_a_converters["double[]"]                 = DoubleArrayConverter
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
@@ -5,6 +5,9 @@
 def _remove_const(name):
     return "".join(rstring.split(name, "const")) # poor man's replace
 
+def remove_const(name):
+    return _remove_const(name).strip(' ')
+
 def compound(name):
     name = _remove_const(name)
     if name.endswith("]"):                       # array type?
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
@@ -6,7 +6,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif // ifdef __cplusplus
-    typedef long cppyy_typehandle_t;
+    typedef void* cppyy_typehandle_t;
     typedef void* cppyy_object_t;
     typedef void* (*cppyy_methptrgetter_t)(cppyy_object_t);
 
@@ -55,6 +55,7 @@
     int cppyy_method_num_args(cppyy_typehandle_t handle, int method_index);
     int cppyy_method_req_args(cppyy_typehandle_t handle, int method_index);
     char* cppyy_method_arg_type(cppyy_typehandle_t handle, int method_index, int index);
+    char* cppyy_method_arg_default(cppyy_typehandle_t handle, int method_index, int index);
 
     /* method properties */
     int cppyy_is_constructor(cppyy_typehandle_t handle, int method_index);
@@ -70,8 +71,9 @@
     int cppyy_is_publicdata(cppyy_typehandle_t handle, int data_member_index);
     int cppyy_is_staticdata(cppyy_typehandle_t handle, int data_member_index);
 
-    /* misc helper */
+    /* misc helpers */
     void cppyy_free(void* ptr);
+    int cppyy_atoi(const char* str);
 
 #ifdef __cplusplus
 }
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
@@ -93,13 +93,13 @@
 class CPPMethod(object):
     """ A concrete function after overloading has been resolved """
     _immutable_ = True
-    _immutable_fields_ = ["arg_types[*]", "arg_converters[*]"]
+    _immutable_fields_ = ["arg_defs[*]", "arg_converters[*]"]
     
-    def __init__(self, cpptype, method_index, result_type, arg_types, args_required):
+    def __init__(self, cpptype, method_index, result_type, arg_defs, args_required):
         self.cpptype = cpptype
         self.space = cpptype.space
         self.method_index = method_index
-        self.arg_types = arg_types
+        self.arg_defs = arg_defs
         self.args_required = args_required
         self.executor = executor.get_executor(self.space, result_type)
         self.arg_converters = None
@@ -114,7 +114,7 @@
         if self.executor is None:
             raise OperationError(self.space.w_TypeError,
                                  self.space.wrap("return type not handled"))
-        if len(self.arg_types) < len(args_w) or len(args_w) < self.args_required:
+        if len(self.arg_defs) < len(args_w) or len(args_w) < self.args_required:
             raise OperationError(self.space.w_TypeError, self.space.wrap("wrong number of arguments"))
 
         if self.methgetter and cppthis: # only for methods
@@ -142,10 +142,14 @@
 
         argchain = libffi.ArgChain()
         argchain.arg(cppthis)
+        i = len(self.arg_defs)
         for i in range(len(args_w)):
             conv = self.arg_converters[i]
             w_arg = args_w[i]
             conv.convert_argument_libffi(space, w_arg, argchain)
+        for j in range(i+1, len(self.arg_defs)):
+            conv = self.arg_converters[j]
+            conv.default_argument_libffi(space, argchain)
         return self.executor.execute_libffi(space, w_type, libffi_func, argchain)
 
     @jit.elidable_promote()
@@ -167,8 +171,8 @@
         return libffifunc
 
     def _build_converters(self):
-        self.arg_converters = [converter.get_converter(self.space, arg_type)
-                                   for arg_type in self.arg_types]
+        self.arg_converters = [converter.get_converter(self.space, arg_type, arg_dflt)
+                                   for arg_type, arg_dflt in self.arg_defs]
 
     @jit.unroll_safe
     def prepare_arguments(self, args_w):
@@ -205,7 +209,7 @@
 
     def __repr__(self):
         return "CPPFunction(%s, %s, %r, %s)" % (
-            self.cpptype, self.method_index, self.executor, self.arg_types)
+            self.cpptype, self.method_index, self.executor, self.arg_defs)
 
     def _freeze_(self):
         assert 0, "you should never have a pre-built instance of this!"
@@ -302,7 +306,7 @@
         self.space = space
         assert lltype.typeOf(scope_handle) == capi.C_TYPEHANDLE
         self.scope_handle = scope_handle
-        self.converter = converter.get_converter(self.space, type_name)
+        self.converter = converter.get_converter(self.space, type_name, '')
         self.offset = offset
         self._is_static = is_static
 
@@ -415,11 +419,12 @@
         result_type = capi.charp2str_free(capi.c_method_result_type(self.handle, method_index))
         num_args = capi.c_method_num_args(self.handle, method_index)
         args_required = capi.c_method_req_args(self.handle, method_index)
-        argtypes = []
+        arg_defs = []
         for i in range(num_args):
-            argtype = capi.charp2str_free(capi.c_method_arg_type(self.handle, method_index, i))
-            argtypes.append(argtype)
-        return CPPFunction(self, method_index, result_type, argtypes, args_required)
+            arg_type = capi.charp2str_free(capi.c_method_arg_type(self.handle, method_index, i))
+            arg_dflt = capi.charp2str_free(capi.c_method_arg_default(self.handle, method_index, i))
+            arg_defs.append((arg_type, arg_dflt))
+        return CPPFunction(self, method_index, result_type, arg_defs, args_required)
 
     def _find_data_members(self):
         num_data_members = capi.c_num_data_members(self.handle)
@@ -460,10 +465,11 @@
         result_type = capi.charp2str_free(capi.c_method_result_type(self.handle, method_index))
         num_args = capi.c_method_num_args(self.handle, method_index)
         args_required = capi.c_method_req_args(self.handle, method_index)
-        argtypes = []
+        arg_defs = []
         for i in range(num_args):
-            argtype = capi.charp2str_free(capi.c_method_arg_type(self.handle, method_index, i))
-            argtypes.append(argtype)
+            arg_type = capi.charp2str_free(capi.c_method_arg_type(self.handle, method_index, i))
+            arg_dflt = capi.charp2str_free(capi.c_method_arg_default(self.handle, method_index, i))
+            arg_defs.append((arg_type, arg_dflt))
         if capi.c_is_constructor(self.handle, method_index):
             result_type = "void"       # b/c otherwise CINT v.s. Reflex difference
             cls = CPPConstructor
@@ -471,7 +477,7 @@
             cls = CPPFunction
         else:
             cls = CPPMethod
-        return cls(self, method_index, result_type, argtypes, args_required)
+        return cls(self, method_index, result_type, arg_defs, args_required)
 
     def _find_data_members(self):
         num_data_members = capi.c_num_data_members(self.handle)
diff --git a/pypy/module/cppyy/src/cintcwrapper.cxx b/pypy/module/cppyy/src/cintcwrapper.cxx
--- a/pypy/module/cppyy/src/cintcwrapper.cxx
+++ b/pypy/module/cppyy/src/cintcwrapper.cxx
@@ -462,6 +462,10 @@
 
 
 /* misc helpers ----------------------------------------------------------- */
+int cppyy_atoi(const char* str) {
+    return atoi(str);
+}
+
 void cppyy_free(void* ptr) {
     free(ptr);
 }
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
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include <stdlib.h>
+
 
 /* local helpers ---------------------------------------------------------- */
 static inline char* cppstring_to_cstring(const std::string& name) {
@@ -296,6 +298,13 @@
     return cppstring_to_cstring(name);
 }
 
+char* cppyy_method_arg_default(cppyy_typehandle_t handle, int method_index, int arg_index) {
+    Reflex::Scope s = scope_from_handle(handle);
+    Reflex::Member m = s.FunctionMemberAt(method_index);
+    std::string dflt = m.FunctionParameterDefaultAt(arg_index);
+    return cppstring_to_cstring(dflt);
+}
+
 
 int cppyy_is_constructor(cppyy_typehandle_t handle, int method_index) {
     Reflex::Scope s = scope_from_handle(handle);
@@ -350,7 +359,11 @@
 }
 
 
-/* misc helper ------------------------------------------------------------ */
+/* misc helpers ----------------------------------------------------------- */
+int cppyy_atoi(const char* str) {
+    return atoi(str);
+}
+
 void cppyy_free(void* ptr) {
     free(ptr);
 }
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
@@ -137,5 +137,42 @@
 }
 
 
+// argument passing
+int ArgPasser::intValue(int arg0, int argn, int arg1, int arg2)
+{
+   switch (argn) {
+   case 0:
+      return arg0;
+   case 1:
+      return arg1;
+   case 2:
+      return arg2;
+   default:
+      break;
+   }
+
+   return -1;
+}
+
+std::string ArgPasser::stringValue(std::string arg0, int argn, std::string arg1)
+{
+   switch (argn) {
+   case 0:
+      return arg0;
+   case 1:
+      return arg1;
+   default:
+      break;
+   }
+
+   return "argn invalid";
+}
+
+std::string ArgPasser::stringRef(const std::string& arg0, int argn, const std::string& arg1)
+{
+   return stringValue(arg0, argn, arg1);
+}
+
+
 // special case naming
 z_& z_::gime_z_(z_& z) { return z; }
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
@@ -1,3 +1,5 @@
+#include <string>
+
 class payload {
 public:
     payload(double d = 0.);
@@ -60,6 +62,19 @@
 }
 
 
+// argument passing
+class ArgPasser {        // use a class for now as methptrgetter not
+public:                  // implemented for global functions
+   int intValue(int arg0, int argn=0, int arg1=1, int arg2=2);
+
+   std::string stringValue(
+      std::string arg0, int argn=0, std::string arg1 = "default");
+
+   std::string stringRef(
+      const std::string& arg0, int argn=0, const std::string& arg1="default");
+};
+
+
 // 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="std::string" />
   <class name="z_" />
 
   <function name="globalAddOneToInt" />
@@ -9,4 +10,6 @@
   <namespace name="ns_example01" />
   <function name="ns_example01::globalAddOneToInt" />
 
+  <class name="ArgPasser" />
+
 </lcgdict>
diff --git a/pypy/module/cppyy/test/test_cppyy.py b/pypy/module/cppyy/test/test_cppyy.py
--- a/pypy/module/cppyy/test/test_cppyy.py
+++ b/pypy/module/cppyy/test/test_cppyy.py
@@ -24,7 +24,7 @@
         adddouble = w_cppyyclass.methods["staticAddToDouble"]
         func, = adddouble.functions
         assert isinstance(func.executor, executor.DoubleExecutor)
-        assert func.arg_types == ["double"]
+        assert func.arg_defs == [("double", "")]
 
 
 class AppTestCPPYY:
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
@@ -1,5 +1,8 @@
 from pypy.module.cppyy import helper
 
+def test_remove_const():
+    assert helper.remove_const("const int") == "int"
+
 def test_compound():
     assert helper.compound("int*") == "*"
     assert helper.compound("int* const *&") == "**&"
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
@@ -37,7 +37,7 @@
         cl2 = cppyy.gbl.example01
         assert example01_class is cl2
 
-        raises(AttributeError, "cppyy.gbl.nonexistingclass")
+        raises(AttributeError, 'cppyy.gbl.nonexistingclass')
 
     def test03_calling_static_functions(self):
         """Test calling of static methods."""
@@ -244,7 +244,30 @@
 
         # TODO: need ReferenceError on touching pl_a
 
-    def test10_underscore_in_class_name(self):
+    def test10_default_arguments(self):
+        """Test propagation of default function arguments"""
+
+        import cppyy
+        a = cppyy.gbl.ArgPasser()
+
+        # NOTE: when called through the stub, default args are fine
+        f = a.stringRef
+        s = cppyy.gbl.std.string
+        assert f(s("aap"), 0, s("noot")).c_str() == "aap"
+        assert f(s("noot"), 1).c_str() == "default"
+        assert f(s("mies")).c_str() == "mies"
+
+        g = a.intValue
+        raises(TypeError, 'g(1, 2, 3, 4, 6)')
+        assert g(11, 0, 12, 13) == 11
+        assert g(11, 1, 12, 13) == 12
+        assert g(11, 1, 12)     == 12
+        assert g(11, 2, 12)     ==  2
+        assert g(11, 1)         ==  1
+        assert g(11, 2)         ==  2
+        assert g(11)            == 11
+
+    def test12_underscore_in_class_name(self):
         """Test recognition of '_' as part of a valid class name"""
 
         import cppyy
@@ -255,4 +278,3 @@
 
         assert hasattr(z, 'myint')
         assert z.gime_z_(z)
-
diff --git a/pypy/rlib/libffi.py b/pypy/rlib/libffi.py
--- a/pypy/rlib/libffi.py
+++ b/pypy/rlib/libffi.py
@@ -238,7 +238,7 @@
         self = jit.promote(self)
         if argchain.numargs != len(self.argtypes):
             raise TypeError, 'Wrong number of arguments: %d expected, got %d' %\
-                (argchain.numargs, len(self.argtypes))
+                (len(self.argtypes), argchain.numargs)
         ll_args = self._prepare()
         i = 0
         arg = argchain.first


More information about the pypy-commit mailing list