📄 function.cpp
字号:
// Copyright David Abrahams 2001.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/object/function.hpp>
#include <boost/python/object/function_object.hpp>
#include <boost/python/object/function_handle.hpp>
#include <boost/python/errors.hpp>
#include <boost/python/str.hpp>
#include <boost/python/object_attributes.hpp>
#include <boost/python/args.hpp>
#include <boost/python/refcount.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/list.hpp>
#include <boost/python/detail/api_placeholder.hpp>
#include <boost/python/detail/signature.hpp>
#include <boost/mpl/vector/vector10.hpp>
#include <boost/bind.hpp>
#include <algorithm>
#include <cstring>
#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
# include <cstdio>
#endif
namespace boost { namespace python { namespace objects {
py_function_impl_base::~py_function_impl_base()
{
}
unsigned py_function_impl_base::max_arity() const
{
return this->min_arity();
}
extern PyTypeObject function_type;
function::function(
py_function const& implementation
#if BOOST_WORKAROUND(__EDG_VERSION__, == 245)
, python::detail::keyword const* names_and_defaults
#else
, python::detail::keyword const* const names_and_defaults
#endif
, unsigned num_keywords
)
: m_fn(implementation)
, m_nkeyword_values(0)
{
if (names_and_defaults != 0)
{
unsigned int max_arity = m_fn.max_arity();
unsigned int keyword_offset
= max_arity > num_keywords ? max_arity - num_keywords : 0;
unsigned tuple_size = num_keywords ? max_arity : 0;
m_arg_names = object(handle<>(PyTuple_New(tuple_size)));
if (num_keywords != 0)
{
for (unsigned j = 0; j < keyword_offset; ++j)
PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None));
}
for (unsigned i = 0; i < num_keywords; ++i)
{
tuple kv;
python::detail::keyword const* const p = names_and_defaults + i;
if (p->default_value)
{
kv = make_tuple(p->name, p->default_value);
++m_nkeyword_values;
}
else
{
kv = make_tuple(p->name);
}
PyTuple_SET_ITEM(
m_arg_names.ptr()
, i + keyword_offset
, incref(kv.ptr())
);
}
}
PyObject* p = this;
if (function_type.ob_type == 0)
{
function_type.ob_type = &PyType_Type;
::PyType_Ready(&function_type);
}
(void)( // warning suppression for GCC
PyObject_INIT(p, &function_type)
);
}
function::~function()
{
}
PyObject* function::call(PyObject* args, PyObject* keywords) const
{
std::size_t n_unnamed_actual = PyTuple_GET_SIZE(args);
std::size_t n_keyword_actual = keywords ? PyDict_Size(keywords) : 0;
std::size_t n_actual = n_unnamed_actual + n_keyword_actual;
function const* f = this;
// Try overloads looking for a match
do
{
// Check for a plausible number of arguments
unsigned min_arity = f->m_fn.min_arity();
unsigned max_arity = f->m_fn.max_arity();
if (n_actual + f->m_nkeyword_values >= min_arity
&& n_actual <= max_arity)
{
// This will be the args that actually get passed
handle<>inner_args(allow_null(borrowed(args)));
if (n_keyword_actual > 0 // Keyword arguments were supplied
|| n_actual < min_arity) // or default keyword values are needed
{
if (f->m_arg_names.ptr() == Py_None)
{
// this overload doesn't accept keywords
inner_args = handle<>();
}
else
{
// "all keywords are none" is a special case
// indicating we will accept any number of keyword
// arguments
if (PyTuple_Size(f->m_arg_names.ptr()) == 0)
{
// no argument preprocessing
}
else if (n_actual > max_arity)
{
// too many arguments
inner_args = handle<>();
}
else
{
// build a new arg tuple, will adjust its size later
inner_args = handle<>(PyTuple_New(max_arity));
// Fill in the positional arguments
for (std::size_t i = 0; i < n_unnamed_actual; ++i)
PyTuple_SET_ITEM(inner_args.get(), i, incref(PyTuple_GET_ITEM(args, i)));
// Grab remaining arguments by name from the keyword dictionary
std::size_t n_actual_processed = n_unnamed_actual;
for (std::size_t arg_pos = n_unnamed_actual; arg_pos < max_arity ; ++arg_pos)
{
// Get the keyword[, value pair] corresponding
PyObject* kv = PyTuple_GET_ITEM(f->m_arg_names.ptr(), arg_pos);
// If there were any keyword arguments,
// look up the one we need for this
// argument position
PyObject* value = n_keyword_actual
? PyDict_GetItem(keywords, PyTuple_GET_ITEM(kv, 0))
: 0;
if (!value)
{
// Not found; check if there's a default value
if (PyTuple_GET_SIZE(kv) > 1)
value = PyTuple_GET_ITEM(kv, 1);
if (!value)
{
// still not found; matching fails
PyErr_Clear();
inner_args = handle<>();
break;
}
}
else
{
++n_actual_processed;
}
PyTuple_SET_ITEM(inner_args.get(), arg_pos, incref(value));
}
if (inner_args.get())
{
//check if we proccessed all the arguments
if(n_actual_processed < n_actual)
inner_args = handle<>();
}
}
}
}
// Call the function. Pass keywords in case it's a
// function accepting any number of keywords
PyObject* result = inner_args ? f->m_fn(inner_args.get(), keywords) : 0;
// If the result is NULL but no error was set, m_fn failed
// the argument-matching test.
// This assumes that all other error-reporters are
// well-behaved and never return NULL to python without
// setting an error.
if (result != 0 || PyErr_Occurred())
return result;
}
f = f->m_overloads.get();
}
while (f);
// None of the overloads matched; time to generate the error message
argument_error(args, keywords);
return 0;
}
void function::argument_error(PyObject* args, PyObject* keywords) const
{
static handle<> exception(
PyErr_NewException("Boost.Python.ArgumentError", PyExc_TypeError, 0));
object message = "Python argument types in\n %s.%s("
% make_tuple(this->m_namespace, this->m_name);
list actual_args;
for (int i = 0; i < PyTuple_Size(args); ++i)
{
char const* name = PyTuple_GetItem(args, i)->ob_type->tp_name;
actual_args.append(str(name));
}
message += str(", ").join(actual_args);
message += ")\ndid not match C++ signature:\n ";
list signatures;
for (function const* f = this; f; f = f->m_overloads.get())
{
py_function const& impl = f->m_fn;
python::detail::signature_element const* s
= impl.signature() + 1; // skip over return type
list formal_params;
if (impl.max_arity() == 0)
formal_params.append("void");
for (unsigned n = 0; n < impl.max_arity(); ++n)
{
if (s[n].basename == 0)
{
formal_params.append("...");
break;
}
str param(s[n].basename);
if (s[n].lvalue)
param += " {lvalue}";
if (f->m_arg_names) // None or empty tuple will test false
{
object kv(f->m_arg_names[n]);
if (kv)
{
char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s";
param += fmt % kv;
}
}
formal_params.append(param);
}
signatures.append(
"%s(%s)" % make_tuple(f->m_name, str(", ").join(formal_params))
);
}
message += str("\n ").join(signatures);
#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES
std::printf("\n--------\n%s\n--------\n", extract<const char*>(message)());
#endif
PyErr_SetObject(exception.get(), message.ptr());
throw_error_already_set();
}
void function::add_overload(handle<function> const& overload_)
{
function* parent = this;
while (parent->m_overloads)
parent = parent->m_overloads.get();
parent->m_overloads = overload_;
// If we have no documentation, get the docs from the overload
if (!m_doc)
m_doc = overload_->m_doc;
}
namespace
{
char const* const binary_operator_names[] =
{
"add__",
"and__",
"div__",
"divmod__",
"eq__",
"floordiv__",
"ge__",
"gt__",
"le__",
"lshift__",
"lt__",
"mod__",
"mul__",
"ne__",
"or__",
"pow__",
"radd__",
"rand__",
"rdiv__",
"rdivmod__",
"rfloordiv__",
"rlshift__",
"rmod__",
"rmul__",
"ror__",
"rpow__",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -