Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/python/src/object/function.cpp @ 25

Last change on this file since 25 was 12, checked in by landauf, 18 years ago

added boost

File size: 21.0 KB
Line 
1// Copyright David Abrahams 2001.
2// Distributed under the Boost Software License, Version 1.0. (See
3// accompanying file LICENSE_1_0.txt or copy at
4// http://www.boost.org/LICENSE_1_0.txt)
5
6#include <boost/python/object/function.hpp>
7#include <boost/python/object/function_object.hpp>
8#include <boost/python/object/function_handle.hpp>
9#include <boost/python/errors.hpp>
10#include <boost/python/str.hpp>
11#include <boost/python/object_attributes.hpp>
12#include <boost/python/args.hpp>
13#include <boost/python/refcount.hpp>
14#include <boost/python/extract.hpp>
15#include <boost/python/tuple.hpp>
16#include <boost/python/list.hpp>
17
18#include <boost/python/detail/api_placeholder.hpp>
19#include <boost/python/detail/signature.hpp>
20#include <boost/mpl/vector/vector10.hpp>
21
22#include <boost/bind.hpp>
23
24#include <algorithm>
25#include <cstring>
26
27#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
28# include <cstdio>
29#endif
30
31namespace boost { namespace python { namespace objects { 
32
33py_function_impl_base::~py_function_impl_base()
34{
35}
36
37unsigned py_function_impl_base::max_arity() const
38{
39    return this->min_arity();
40}
41
42extern PyTypeObject function_type;
43
44function::function(
45    py_function const& implementation
46#if BOOST_WORKAROUND(__EDG_VERSION__, == 245)
47    , python::detail::keyword const*       names_and_defaults
48#else
49    , python::detail::keyword const* const names_and_defaults
50#endif
51    , unsigned num_keywords
52    )
53    : m_fn(implementation)
54    , m_nkeyword_values(0)
55{
56    if (names_and_defaults != 0)
57    {
58        unsigned int max_arity = m_fn.max_arity();
59        unsigned int keyword_offset
60            = max_arity > num_keywords ? max_arity - num_keywords : 0;
61
62
63        unsigned tuple_size = num_keywords ? max_arity : 0;
64        m_arg_names = object(handle<>(PyTuple_New(tuple_size)));
65
66        if (num_keywords != 0)
67        {
68            for (unsigned j = 0; j < keyword_offset; ++j)
69                PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None));
70        }
71       
72        for (unsigned i = 0; i < num_keywords; ++i)
73        {
74            tuple kv;
75
76            python::detail::keyword const* const p = names_and_defaults + i;
77            if (p->default_value)
78            {
79                kv = make_tuple(p->name, p->default_value);
80                ++m_nkeyword_values;
81            }
82            else
83            {
84                kv = make_tuple(p->name);
85            }
86
87            PyTuple_SET_ITEM(
88                m_arg_names.ptr()
89                , i + keyword_offset
90                , incref(kv.ptr())
91                );
92        }
93    }
94   
95    PyObject* p = this;
96    if (function_type.ob_type == 0)
97    {
98        function_type.ob_type = &PyType_Type;
99        ::PyType_Ready(&function_type);
100    }
101   
102    (void)(     // warning suppression for GCC
103        PyObject_INIT(p, &function_type)
104    );
105}
106
107function::~function()
108{
109}
110
111PyObject* function::call(PyObject* args, PyObject* keywords) const
112{
113    std::size_t n_unnamed_actual = PyTuple_GET_SIZE(args);
114    std::size_t n_keyword_actual = keywords ? PyDict_Size(keywords) : 0;
115    std::size_t n_actual = n_unnamed_actual + n_keyword_actual;
116   
117    function const* f = this;
118
119    // Try overloads looking for a match
120    do
121    {
122        // Check for a plausible number of arguments
123        unsigned min_arity = f->m_fn.min_arity();
124        unsigned max_arity = f->m_fn.max_arity();
125
126        if (n_actual + f->m_nkeyword_values >= min_arity
127            && n_actual <= max_arity)
128        {
129            // This will be the args that actually get passed
130            handle<>inner_args(allow_null(borrowed(args)));
131
132            if (n_keyword_actual > 0      // Keyword arguments were supplied
133                 || n_actual < min_arity) // or default keyword values are needed
134            {                           
135                if (f->m_arg_names.ptr() == Py_None) 
136                {
137                    // this overload doesn't accept keywords
138                    inner_args = handle<>();
139                }
140                else
141                {
142                    // "all keywords are none" is a special case
143                    // indicating we will accept any number of keyword
144                    // arguments
145                    if (PyTuple_Size(f->m_arg_names.ptr()) == 0)
146                    {
147                        // no argument preprocessing
148                    }
149                    else if (n_actual > max_arity)
150                    {
151                        // too many arguments
152                        inner_args = handle<>();
153                    }
154                    else
155                    {
156                        // build a new arg tuple, will adjust its size later
157                        inner_args = handle<>(PyTuple_New(max_arity));
158
159                        // Fill in the positional arguments
160                        for (std::size_t i = 0; i < n_unnamed_actual; ++i)
161                            PyTuple_SET_ITEM(inner_args.get(), i, incref(PyTuple_GET_ITEM(args, i)));
162
163                        // Grab remaining arguments by name from the keyword dictionary
164                        std::size_t n_actual_processed = n_unnamed_actual;
165               
166                        for (std::size_t arg_pos = n_unnamed_actual; arg_pos < max_arity ; ++arg_pos)
167                        {
168                            // Get the keyword[, value pair] corresponding
169                            PyObject* kv = PyTuple_GET_ITEM(f->m_arg_names.ptr(), arg_pos);
170
171                            // If there were any keyword arguments,
172                            // look up the one we need for this
173                            // argument position
174                            PyObject* value = n_keyword_actual
175                                ? PyDict_GetItem(keywords, PyTuple_GET_ITEM(kv, 0))
176                                : 0;
177
178                            if (!value)
179                            {
180                                // Not found; check if there's a default value
181                                if (PyTuple_GET_SIZE(kv) > 1)
182                                    value = PyTuple_GET_ITEM(kv, 1);
183                       
184                                if (!value)
185                                {
186                                    // still not found; matching fails
187                                    PyErr_Clear();
188                                    inner_args = handle<>();
189                                    break;
190                                }
191                            }
192                            else
193                            {
194                                ++n_actual_processed;
195                            }
196
197                            PyTuple_SET_ITEM(inner_args.get(), arg_pos, incref(value));
198                        }
199
200                        if (inner_args.get())
201                        {
202                            //check if we proccessed all the arguments
203                            if(n_actual_processed < n_actual)
204                                inner_args = handle<>();
205                        }
206                    }
207                }
208            }
209           
210            // Call the function.  Pass keywords in case it's a
211            // function accepting any number of keywords
212            PyObject* result = inner_args ? f->m_fn(inner_args.get(), keywords) : 0;
213           
214            // If the result is NULL but no error was set, m_fn failed
215            // the argument-matching test.
216
217            // This assumes that all other error-reporters are
218            // well-behaved and never return NULL to python without
219            // setting an error.
220            if (result != 0 || PyErr_Occurred())
221                return result;
222        }
223        f = f->m_overloads.get();
224    }
225    while (f);
226    // None of the overloads matched; time to generate the error message
227    argument_error(args, keywords);
228    return 0;
229}
230
231void function::argument_error(PyObject* args, PyObject* keywords) const
232{
233    static handle<> exception(
234        PyErr_NewException("Boost.Python.ArgumentError", PyExc_TypeError, 0));
235
236    object message = "Python argument types in\n    %s.%s("
237        % make_tuple(this->m_namespace, this->m_name);
238   
239    list actual_args;
240    for (int i = 0; i < PyTuple_Size(args); ++i)
241    {
242        char const* name = PyTuple_GetItem(args, i)->ob_type->tp_name;
243        actual_args.append(str(name));
244    }
245    message += str(", ").join(actual_args);
246    message += ")\ndid not match C++ signature:\n    ";
247
248    list signatures;
249    for (function const* f = this; f; f = f->m_overloads.get())
250    {
251        py_function const& impl = f->m_fn;
252       
253        python::detail::signature_element const* s
254            = impl.signature() + 1; // skip over return type
255       
256        list formal_params;
257        if (impl.max_arity() == 0)
258            formal_params.append("void");
259
260        for (unsigned n = 0; n < impl.max_arity(); ++n)
261        {
262            if (s[n].basename == 0)
263            {
264                formal_params.append("...");
265                break;
266            }
267
268            str param(s[n].basename);
269            if (s[n].lvalue)
270                param += " {lvalue}";
271           
272            if (f->m_arg_names) // None or empty tuple will test false
273            {
274                object kv(f->m_arg_names[n]);
275                if (kv)
276                {
277                    char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s";
278                    param += fmt % kv;
279                }
280            }
281           
282            formal_params.append(param);
283        }
284
285        signatures.append(
286            "%s(%s)" % make_tuple(f->m_name, str(", ").join(formal_params))
287            );
288    }
289
290    message += str("\n    ").join(signatures);
291
292#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
293    std::printf("\n--------\n%s\n--------\n", extract<const char*>(message)());
294#endif
295    PyErr_SetObject(exception.get(), message.ptr());
296    throw_error_already_set();
297}
298
299void function::add_overload(handle<function> const& overload_)
300{
301    function* parent = this;
302   
303    while (parent->m_overloads)
304        parent = parent->m_overloads.get();
305
306    parent->m_overloads = overload_;
307
308    // If we have no documentation, get the docs from the overload
309    if (!m_doc)
310        m_doc = overload_->m_doc;
311}
312
313namespace
314{
315  char const* const binary_operator_names[] =
316  {
317      "add__",
318      "and__",
319      "div__",
320      "divmod__",
321      "eq__",
322      "floordiv__",
323      "ge__",
324      "gt__",
325      "le__",
326      "lshift__",
327      "lt__",
328      "mod__",
329      "mul__",
330      "ne__",
331      "or__",
332      "pow__",
333      "radd__",
334      "rand__",
335      "rdiv__",
336      "rdivmod__", 
337      "rfloordiv__",
338      "rlshift__",
339      "rmod__",
340      "rmul__",
341      "ror__",
342      "rpow__", 
343      "rrshift__",
344      "rshift__",
345      "rsub__",
346      "rtruediv__",
347      "rxor__",
348      "sub__",
349      "truediv__", 
350      "xor__"
351  };
352
353  struct less_cstring
354  {
355      bool operator()(char const* x, char const* y) const
356      {
357          return BOOST_CSTD_::strcmp(x,y) < 0;
358      }
359  };
360 
361  inline bool is_binary_operator(char const* name)
362  {
363      return name[0] == '_'
364          && name[1] == '_'
365          && std::binary_search(
366              &binary_operator_names[0]
367              , binary_operator_names + sizeof(binary_operator_names)/sizeof(*binary_operator_names)
368              , name + 2
369              , less_cstring()
370              );
371  }
372
373  // Something for the end of the chain of binary operators
374  PyObject* not_implemented(PyObject*, PyObject*)
375  {
376      Py_INCREF(Py_NotImplemented);
377      return Py_NotImplemented;
378  }
379 
380  handle<function> not_implemented_function()
381  {
382     
383      static object keeper(
384          function_object(
385              py_function(&not_implemented, mpl::vector1<void>(), 2)
386            , python::detail::keyword_range())
387          );
388      return handle<function>(borrowed(downcast<function>(keeper.ptr())));
389  }
390}
391
392void function::add_to_namespace(
393    object const& name_space, char const* name_, object const& attribute)
394{
395    str const name(name_);
396    PyObject* const ns = name_space.ptr();
397   
398    if (attribute.ptr()->ob_type == &function_type)
399    {
400        function* new_func = downcast<function>(attribute.ptr());
401        PyObject* dict = 0;
402       
403        if (PyClass_Check(ns))
404            dict = ((PyClassObject*)ns)->cl_dict;
405        else if (PyType_Check(ns))
406            dict = ((PyTypeObject*)ns)->tp_dict;
407        else   
408            dict = PyObject_GetAttrString(ns, "__dict__");
409
410        if (dict == 0)
411            throw_error_already_set();
412
413        handle<> existing(allow_null(::PyObject_GetItem(dict, name.ptr())));
414       
415        if (existing)
416        {
417            if (existing->ob_type == &function_type)
418            {
419                new_func->add_overload(
420                    handle<function>(
421                        borrowed(
422                            downcast<function>(existing.get())
423                        )
424                    )
425                );
426            }
427            else if (existing->ob_type == &PyStaticMethod_Type)
428            {
429                char const* name_space_name = extract<char const*>(name_space.attr("__name__"));
430               
431                ::PyErr_Format(
432                    PyExc_RuntimeError
433                    , "Boost.Python - All overloads must be exported "
434                      "before calling \'class_<...>(\"%s\").staticmethod(\"%s\")\'"
435                    , name_space_name
436                    , name_
437                    );
438                throw_error_already_set();
439            }
440        }
441        else if (is_binary_operator(name_))
442        {
443            // Binary operators need an additional overload which
444            // returns NotImplemented, so that Python will try the
445            // __rxxx__ functions on the other operand. We add this
446            // when no overloads for the operator already exist.
447            new_func->add_overload(not_implemented_function());
448        }
449
450        // A function is named the first time it is added to a namespace.
451        if (new_func->name().ptr() == Py_None)
452            new_func->m_name = name;
453
454        handle<> name_space_name(
455            allow_null(::PyObject_GetAttrString(name_space.ptr(), "__name__")));
456       
457        if (name_space_name)
458            new_func->m_namespace = object(name_space_name);
459    }
460
461    // The PyObject_GetAttrString() or PyObject_GetItem calls above may
462    // have left an active error
463    PyErr_Clear();
464    if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0)
465        throw_error_already_set();
466}
467
468void function::add_to_namespace(
469    object const& name_space, char const* name_, object const& attribute, char const* doc)
470{
471    add_to_namespace(name_space, name_, attribute);
472    if (doc != 0)
473    {
474        // Accumulate documentation
475        object mutable_attribute(attribute);
476       
477        if (
478            PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__")
479            && mutable_attribute.attr("__doc__"))
480        {
481            mutable_attribute.attr("__doc__") += "\n\n";
482            mutable_attribute.attr("__doc__") += doc;
483        }
484        else
485            mutable_attribute.attr("__doc__") = doc;
486    }
487}
488
489BOOST_PYTHON_DECL void add_to_namespace(
490    object const& name_space, char const* name, object const& attribute)
491{
492    function::add_to_namespace(name_space, name, attribute);
493}
494
495BOOST_PYTHON_DECL void add_to_namespace(
496    object const& name_space, char const* name, object const& attribute, char const* doc)
497{
498    function::add_to_namespace(name_space, name, attribute, doc);   
499}
500
501
502namespace
503{
504  struct bind_return
505  {
506      bind_return(PyObject*& result, function const* f, PyObject* args, PyObject* keywords)
507          : m_result(result)
508            , m_f(f)
509            , m_args(args)
510            , m_keywords(keywords)
511      {}
512
513      void operator()() const
514      {
515          m_result = m_f->call(m_args, m_keywords);
516      }
517     
518   private:
519      PyObject*& m_result;
520      function const* m_f;
521      PyObject* m_args;
522      PyObject* m_keywords;
523  };
524}
525
526extern "C"
527{
528    // Stolen from Python's funcobject.c
529    static PyObject *
530    function_descr_get(PyObject *func, PyObject *obj, PyObject *type_)
531    {
532        if (obj == Py_None)
533            obj = NULL;
534        return PyMethod_New(func, obj, type_);
535    }
536
537    static void
538    function_dealloc(PyObject* p)
539    {
540        delete static_cast<function*>(p);
541    }
542
543    static PyObject *
544    function_call(PyObject *func, PyObject *args, PyObject *kw)
545    {
546        PyObject* result = 0;
547        handle_exception(bind_return(result, static_cast<function*>(func), args, kw));
548        return result;
549    }
550
551    //
552    // Here we're using the function's tp_getset rather than its
553    // tp_members to set up __doc__ and __name__, because tp_members
554    // really depends on having a POD object type (it relies on
555    // offsets). It might make sense to reformulate function as a POD
556    // at some point, but this is much more expedient.
557    //
558    static PyObject* function_get_doc(PyObject* op, void*)
559    {
560        function* f = downcast<function>(op);
561        return python::incref(f->doc().ptr());
562    }
563   
564    static int function_set_doc(PyObject* op, PyObject* doc, void*)
565    {
566        function* f = downcast<function>(op);
567        f->doc(doc ? object(python::detail::borrowed_reference(doc)) : object());
568        return 0;
569    }
570   
571    static PyObject* function_get_name(PyObject* op, void*)
572    {
573        function* f = downcast<function>(op);
574        if (f->name().ptr() == Py_None)
575            return PyString_InternFromString("<unnamed Boost.Python function>");
576        else
577            return python::incref(f->name().ptr());
578    }
579
580    // We add a dummy __class__ attribute in order to fool PyDoc into
581    // treating these as built-in functions and scanning their
582    // documentation
583    static PyObject* function_get_class(PyObject* op, void*)
584    {
585        return python::incref(upcast<PyObject>(&PyCFunction_Type));
586    }
587}
588   
589static PyGetSetDef function_getsetlist[] = {
590    {"__name__", (getter)function_get_name, 0 },
591    {"func_name", (getter)function_get_name, 0 },
592    {"__class__", (getter)function_get_class, 0 },    // see note above
593    {"__doc__", (getter)function_get_doc, (setter)function_set_doc},
594    {"func_doc", (getter)function_get_doc, (setter)function_set_doc},
595    {NULL} /* Sentinel */
596};
597
598PyTypeObject function_type = {
599    PyObject_HEAD_INIT(0)
600    0,
601    "Boost.Python.function",
602    sizeof(function),
603    0,
604    (destructor)function_dealloc,               /* tp_dealloc */
605    0,                                  /* tp_print */
606    0,                                  /* tp_getattr */
607    0,                                  /* tp_setattr */
608    0,                                  /* tp_compare */
609    0, //(reprfunc)func_repr,                   /* tp_repr */
610    0,                                  /* tp_as_number */
611    0,                                  /* tp_as_sequence */
612    0,                                  /* tp_as_mapping */
613    0,                                  /* tp_hash */
614    function_call,                              /* tp_call */
615    0,                                  /* tp_str */
616    0, // PyObject_GenericGetAttr,            /* tp_getattro */
617    0, // PyObject_GenericSetAttr,            /* tp_setattro */
618    0,                                  /* tp_as_buffer */
619    Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */
620    0,                                  /* tp_doc */
621    0, // (traverseproc)func_traverse,          /* tp_traverse */
622    0,                                  /* tp_clear */
623    0,                                  /* tp_richcompare */
624    0, //offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */
625    0,                                  /* tp_iter */
626    0,                                  /* tp_iternext */
627    0,                                  /* tp_methods */
628    0, // func_memberlist,              /* tp_members */
629    function_getsetlist,                /* tp_getset */
630    0,                                  /* tp_base */
631    0,                                  /* tp_dict */
632    function_descr_get,                 /* tp_descr_get */
633    0,                                  /* tp_descr_set */
634    0, //offsetof(PyFunctionObject, func_dict),      /* tp_dictoffset */
635    0,                                      /* tp_init */
636    0,                                      /* tp_alloc */
637    0,
638    0                                       /* tp_new */
639};
640
641object function_object(
642    py_function const& f
643    , python::detail::keyword_range const& keywords)
644{
645    return python::object(
646        python::detail::new_non_null_reference(
647            new function(
648                f, keywords.first, keywords.second - keywords.first)));
649}
650
651object function_object(py_function const& f)
652{
653    return function_object(f, python::detail::keyword_range());
654}
655
656
657handle<> function_handle_impl(py_function const& f)
658{
659    return python::handle<>(
660        allow_null(
661            new function(f, 0, 0)));
662}
663
664} // namespace objects
665
666namespace detail
667{
668  object BOOST_PYTHON_DECL make_raw_function(objects::py_function f)
669  {
670      static keyword k;
671   
672      return objects::function_object(
673          f
674          , keyword_range(&k,&k));
675  }
676  void BOOST_PYTHON_DECL pure_virtual_called()
677  {
678      PyErr_SetString(PyExc_RuntimeError, "Pure virtual function called");
679      throw_error_already_set();
680  }
681}
682
683}} // namespace boost::python
Note: See TracBrowser for help on using the repository browser.