2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-23 05:42:30 +00:00

Added the new arg class from

"nickm-at-sitius.com" (Nikolay Mladenov) which supplies the
ability to wrap functions that can be called with ommitted
arguments in the middle.


[SVN r19441]
This commit is contained in:
Dave Abrahams
2003-08-04 17:46:48 +00:00
parent 714b5dc26e
commit 07c1319b99
6 changed files with 241 additions and 54 deletions

View File

@@ -3,7 +3,7 @@
<html>
<head>
<meta name="generator" content=
"HTML Tidy for Windows (vers 1st August 2002), see www.w3.org">
"HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="../boost.css">
@@ -35,7 +35,31 @@
<dt><a href="#keyword-expression"><i>keyword-expressions</i></a></dt>
<dt><a href="#functions">Functions</a></dt>
<dt><a href="#classes">Classes</a></dt>
<dd>
<dl class="page-index">
<dt><a href="#arg-spec">class <code>arg</code></a></dt>
<dd>
<dl class="page-index">
<dt><a href="#arg-synopsis">class <code>arg</code>
synopsis</a></dt>
<dt><a href="#arg-ctor">class <code>arg</code>
constructor</a></dt>
<dt><a href="#arg-operator">class <code>arg</code> template
<code>operator =</code></a></dt>
</dl>
</dd>
</dl>
</dd>
<dt><a href="#keyword-expression-operators"><i>Keyword-expression</i>
operator <code>,</code></a></dt>
<dt><a href="#functions">Functions (deprecated)</a></dt>
<dd>
<dl class="page-index">
@@ -57,27 +81,95 @@
<p>A <b>keyword-expression</b> results in an object which holds a
sequence of <a href="definitions.html#ntbs">ntbs</a>es, and whose type
encodes the number of keywords specified.</p>
encodes the number of keywords specified. The <b>keyword-expression</b>
may contain default values for some or all of the keywords it holds</p>
<h2><a name="functions"></a>Functions</h2>
<h2><a name="classes"></a>Classes</h2>
<h3><a name="args-spec"></a><code>args(</code>...<code>)</code></h3>
<h3><a name="arg-spec"></a><code>class arg;</code></h3>
<p>The objects of class arg are keyword-expressions holding one keyword (
size one )</p>
<h4><a name="arg-synopsis"></a>Class <code>arg</code> synopsis</h4>
<pre>
<i>unspecified1</i> args(char const*);
<i>unspecified2</i> args(char const*, char const*);
.
.
.
<i>unspecifiedN</i> args(char const*, char const*, ... char const*);
namespace boost { namespace python
{
struct arg
{
template &lt;class T&gt;
arg &amp;perator = (T const &amp;value);
explicit arg (char const *name){elements[0].name = name;}
};
}}
</pre>
<h4><a name="arg-ctor"></a>Class <code>arg</code> constructor</h4>
<pre>
arg(char const* name);
</pre>
<dl class="function-semantics">
<dt><b>Requires:</b> Every argument must be a <a href=
<dt><b>Requires:</b> The argument must be a <a href=
"definitions.html#ntbs">ntbs</a>.</dt>
<dt><b>Returns:</b> an object representing a <a href=
"#keyword-expression"><i>keyword-expression</i></a> encapsulating the
arguments passed.</dt>
<dt><b>Effects:</b> Constructs an <code>arg</code> object holding a
keyword with name <code>name</code>.</dt>
</dl>
<h4><a name="arg-operator"></a>Class <code>arg</code> operator =</h4>
<pre>
template &lt;class T&gt; arg &amp;operator = (T const &amp;value);
</pre>
<dl class="function-semantics">
<dt><b>Requires:</b> The argument must convertible to python.</dt>
<dt><b>Effects:</b> Assigns default value for the keyword.</dt>
<dt><b>Returns:</b> Reference to <code>this</code>.</dt>
</dl>
<h2><a name="keyword-expression-operators"><i>Keyword-expression</i>
operator <code>,</code></a></h2>
<pre>
<i>keyword-expression</i> operator , (<i>keyword-expression</i>, const arg &amp;kw) const
<i>keyword-expression</i> operator , (<i>keyword-expression</i>, const char *name) const;
</pre>
<dl class="function-semantics">
<dt><b>Requires:</b> The argument <code>name</code> must be a <a href=
"definitions.html#ntbs">ntbs</a>.</dt>
<dt><b>Effects:</b> Extends the <i>keyword-expression</i> argument with
one more keyword.</dt>
<dt><b>Returns:</b> The extended <i>keyword-expression</i>.</dt>
</dl>
<h2><font color="#7F7F7F"><a name="functions"></a>Functions
(deprecated)</font></h2>
<h3><a name="args-spec"></a><code><font color=
"#7F7F7F">args</font>(</code>...<code>)</code></h3>
<pre>
<font color="#7F7F7F"> <i>unspecified1</i> args(char const*);
<i>unspecified2</i> args(char const*, char const*);
.
.
.
<i>unspecifiedN</i> args(char const*, char const*, ... char const*);
</font>
</pre>
<dl class="function-semantics">
<dt><font color="#7F7F7F"><b>Requires:</b> Every argument must be a <a
href="definitions.html#ntbs">ntbs</a>.</font></dt>
<dt><font color="#7F7F7F"><b>Returns:</b> an object representing a <a
href="#keyword-expression"><i>keyword-expression</i></a> encapsulating
the arguments passed.</font></dt>
</dl>
<h2><a name="examples"></a>Example</h2>
@@ -85,19 +177,21 @@
#include &lt;boost/python/def.hpp&gt;
using namespace boost::python;
int f(int x, int y, int z);
int f(double x, double y, double z=0.0, double w=1.0);
BOOST_PYTHON_MODULE(xxx)
{
def("f", f, args("x", "y", "z"));
def("f", f
, ( arg("x"), "y", arg("z")=0.0, arg("w")=1.0 )
);
}
</pre>
<p>Revised 05 November, 2001</p>
<p>Revised 01 August, 2003</p>
<p><i>&copy; Copyright <a href=
"../../../../people/dave_abrahams.htm">Dave Abrahams</a> 2002. All Rights
Reserved.</i></p>
"../../../../people/dave_abrahams.htm">Dave Abrahams</a> 2002-2003. All
Rights Reserved.</i></p>
</body>
</html>

View File

@@ -23,11 +23,12 @@
# include <boost/preprocessor/iteration/local.hpp>
# include <boost/python/detail/mpl_lambda.hpp>
# include <boost/python/object_core.hpp>
# include <boost/mpl/bool.hpp>
# include <cstddef>
# include <algorithm>
namespace boost { namespace python {
@@ -42,6 +43,16 @@ namespace detail
{
return keyword_range(elements, elements + nkeywords);
}
keywords<nkeywords+1> operator,(const keywords<1> &k) const
{
python::detail::keywords<size+1> res;
std::copy(elements, elements+size, res.elements);
res.elements[size] = k.elements[0];
return res;
}
keywords<nkeywords+1> operator,(const char *name) const;
keyword elements[nkeywords];
};
@@ -97,6 +108,37 @@ namespace detail
# endif
}
struct arg : detail::keywords<1>
{
explicit arg(char const *name)
{
elements[0].name = name;
}
template <class T>
arg& operator=(T const& value)
{
object z(value);
elements[0].default_value = handle<>(python::borrowed(object(value).ptr()));
return *this;
}
operator detail::keyword const&() const
{
return elements[0];
}
};
namespace detail
{
template <std::size_t nkeywords>
inline keywords<nkeywords + 1>
keywords<nkeywords>::operator,(const char *name) const
{
return this->operator,(arg(name));
}
}
# define BOOST_PYTHON_ASSIGN_NAME(z, n, _) result.elements[n].name = name##n;
# define BOOST_PP_LOCAL_MACRO(n) \
inline detail::keywords<n> args(BOOST_PP_ENUM_PARAMS_Z(1, n, char const* name)) \

View File

@@ -19,6 +19,10 @@ namespace detail
{
struct keyword
{
keyword(char const* name_=0)
: name(name_)
{}
char const* name;
handle<> default_value;
};

View File

@@ -51,6 +51,7 @@ struct BOOST_PYTHON_DECL function : PyObject
object m_namespace;
object m_doc;
object m_arg_names;
unsigned m_nkeyword_values;
};
//

View File

@@ -43,10 +43,11 @@ extern PyTypeObject function_type;
function::function(
py_function const& implementation
, python::detail::keyword const* names_and_defaults
, python::detail::keyword const* const names_and_defaults
, unsigned num_keywords
)
: m_fn(implementation)
, m_nkeyword_values(0)
{
if (names_and_defaults != 0)
{
@@ -66,12 +67,23 @@ function::function(
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
, expect_non_null(
PyString_FromString(const_cast<char*>(names_and_defaults[i].name))
)
, incref(kv.ptr())
);
}
}
@@ -91,9 +103,9 @@ function::~function()
PyObject* function::call(PyObject* args, PyObject* keywords) const
{
std::size_t nargs = PyTuple_GET_SIZE(args);
std::size_t nkeywords = keywords ? PyDict_Size(keywords) : 0;
std::size_t total_args = nargs + nkeywords;
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;
@@ -101,56 +113,88 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const
do
{
// Check for a plausible number of arguments
if (total_args >= f->m_fn.min_arity()
&& total_args <= f->m_fn.max_arity())
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<> args2(allow_null(borrowed(args)));
handle<>inner_args(allow_null(borrowed(args)));
if (nkeywords > 0) // Keyword arguments were supplied
{
if (f->m_arg_names.ptr() == Py_None) // this overload doesn't accept keywords
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)
{
args2 = handle<>(); // signal failure
// this overload doesn't accept keywords
inner_args = handle<>();
}
else
{
std::size_t max_args
= static_cast<std::size_t>(PyTuple_Size(f->m_arg_names.ptr()));
// "all keywords are none" is a special case
// indicating we will accept any number of keyword
// arguments
if (max_args == 0)
if (PyTuple_Size(f->m_arg_names.ptr()) == 0)
{
// no argument preprocessing
}
else if (max_args < total_args)
else if (n_actual > max_arity)
{
args2 = handle<>();
// too many arguments
inner_args = handle<>();
}
else
{
// build a new arg tuple
args2 = handle<>(PyTuple_New(total_args));
// 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 < nargs; ++i)
PyTuple_SET_ITEM(args2.get(), i, incref(PyTuple_GET_ITEM(args, i)));
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
for (std::size_t j = nargs; j < total_args; ++j)
std::size_t n_actual_processed = n_unnamed_actual;
for (std::size_t arg_pos = n_unnamed_actual; arg_pos < max_arity ; ++arg_pos)
{
PyObject* value = PyDict_GetItem(
keywords, PyTuple_GET_ITEM(f->m_arg_names.ptr(), j));
// 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)
{
PyErr_Clear();
args2 = handle<>();
break;
// 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;
}
}
PyTuple_SET_ITEM(args2.get(), j, incref(value));
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<>();
}
}
}
@@ -158,7 +202,7 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const
// Call the function. Pass keywords in case it's a
// function accepting any number of keywords
PyObject* result = args2 ? f->m_fn(args2.get(), keywords) : 0;
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.

View File

@@ -84,6 +84,8 @@ bpl-test exception_translator ;
bpl-test pearu1 : test_cltree.py cltree.cpp ;
bpl-test try : newtest.py m1.cpp m2.cpp ;
bpl-test keywords : keywords.cpp keywords_test.py ;
extension builtin_converters : test_builtin_converters.cpp <dll>../build/boost_python ;
boost-python-runtest builtin_converters : test_builtin_converters.py <pyd>builtin_converters ;