From 07c1319b99d2248af70e23a34d2a6765a51cb002 Mon Sep 17 00:00:00 2001
From: Dave Abrahams
Date: Mon, 4 Aug 2003 17:46:48 +0000
Subject: [PATCH] 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]
---
doc/v2/args.html | 134 +++++++++++++++++++----
include/boost/python/args.hpp | 44 +++++++-
include/boost/python/args_fwd.hpp | 4 +
include/boost/python/object/function.hpp | 1 +
src/object/function.cpp | 110 +++++++++++++------
test/Jamfile | 2 +
6 files changed, 241 insertions(+), 54 deletions(-)
diff --git a/doc/v2/args.html b/doc/v2/args.html
index c2f29269..8519b3bc 100644
--- a/doc/v2/args.html
+++ b/doc/v2/args.html
@@ -3,7 +3,7 @@
+ "HTML Tidy for Cygwin (vers 1st April 2002), see www.w3.org">
@@ -35,7 +35,31 @@
keyword-expressions
- Functions
+ Classes
+
+
+
+ - class
arg
+
+ -
+
+ - class
arg
+ synopsis
+
+ - class
arg
+ constructor
+
+ - class
arg template
+ operator =
+
+
+
+
+
+ Keyword-expression
+ operator ,
+
+ Functions (deprecated)
@@ -57,27 +81,95 @@
A keyword-expression results in an object which holds a
sequence of ntbses, and whose type
- encodes the number of keywords specified.
+ encodes the number of keywords specified. The keyword-expression
+ may contain default values for some or all of the keywords it holds
- Functions
+ Classes
- args(...)
+ class arg;
+
+ The objects of class arg are keyword-expressions holding one keyword (
+ size one )
+
+ Class arg synopsis
-unspecified1 args(char const*);
-unspecified2 args(char const*, char const*);
- .
- .
- .
-unspecifiedN args(char const*, char const*, ... char const*);
+namespace boost { namespace python
+{
+ struct arg
+ {
+ template <class T>
+ arg &perator = (T const &value);
+ explicit arg (char const *name){elements[0].name = name;}
+ };
+
+}}
+
+
+ Class arg constructor
+
+arg(char const* name);
- - Requires: Every argument must be a Requires: The argument must be a ntbs.
- - Returns: an object representing a keyword-expression encapsulating the
- arguments passed.
+ - Effects: Constructs an
arg object holding a
+ keyword with name name.
+
+
+ Class arg operator =
+
+template <class T> arg &operator = (T const &value);
+
+
+
+ - Requires: The argument must convertible to python.
+
+ - Effects: Assigns default value for the keyword.
+
+ - Returns: Reference to
this.
+
+
+
+
+ keyword-expression operator , (keyword-expression, const arg &kw) const
+ keyword-expression operator , (keyword-expression, const char *name) const;
+
+
+
+ - Requires: The argument
name must be a ntbs.
+
+ - Effects: Extends the keyword-expression argument with
+ one more keyword.
+
+ - Returns: The extended keyword-expression.
+
+
+ Functions
+ (deprecated)
+
+ args(...)
+
+ unspecified1 args(char const*);
+ unspecified2 args(char const*, char const*);
+ .
+ .
+ .
+ unspecifiedN args(char const*, char const*, ... char const*);
+
+
+
+
+ - Requires: Every argument must be a ntbs.
+
+ - Returns: an object representing a keyword-expression encapsulating
+ the arguments passed.
Example
@@ -85,19 +177,21 @@
#include <boost/python/def.hpp>
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 )
+ );
}
- Revised 05 November, 2001
+ Revised 01 August, 2003
© Copyright Dave Abrahams 2002. All Rights
- Reserved.
+ "../../../../people/dave_abrahams.htm">Dave Abrahams 2002-2003. All
+ Rights Reserved.
diff --git a/include/boost/python/args.hpp b/include/boost/python/args.hpp
index a3cd6934..1d0576f0 100644
--- a/include/boost/python/args.hpp
+++ b/include/boost/python/args.hpp
@@ -23,11 +23,12 @@
# include
# include
+# include
# include
# include
-
+# include
namespace boost { namespace python {
@@ -42,6 +43,16 @@ namespace detail
{
return keyword_range(elements, elements + nkeywords);
}
+
+ keywords operator,(const keywords<1> &k) const
+ {
+ python::detail::keywords res;
+ std::copy(elements, elements+size, res.elements);
+ res.elements[size] = k.elements[0];
+ return res;
+ }
+
+ keywords 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
+ 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
+ inline keywords
+ keywords::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 args(BOOST_PP_ENUM_PARAMS_Z(1, n, char const* name)) \
diff --git a/include/boost/python/args_fwd.hpp b/include/boost/python/args_fwd.hpp
index bfc45881..f45592ad 100644
--- a/include/boost/python/args_fwd.hpp
+++ b/include/boost/python/args_fwd.hpp
@@ -19,6 +19,10 @@ namespace detail
{
struct keyword
{
+ keyword(char const* name_=0)
+ : name(name_)
+ {}
+
char const* name;
handle<> default_value;
};
diff --git a/include/boost/python/object/function.hpp b/include/boost/python/object/function.hpp
index ec08edba..9a3e0818 100644
--- a/include/boost/python/object/function.hpp
+++ b/include/boost/python/object/function.hpp
@@ -51,6 +51,7 @@ struct BOOST_PYTHON_DECL function : PyObject
object m_namespace;
object m_doc;
object m_arg_names;
+ unsigned m_nkeyword_values;
};
//
diff --git a/src/object/function.cpp b/src/object/function.cpp
index c4ce1565..5594ef7a 100644
--- a/src/object/function.cpp
+++ b/src/object/function.cpp
@@ -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(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(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.
diff --git a/test/Jamfile b/test/Jamfile
index 8812ed66..a1eb1cfc 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -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 ../build/boost_python ;
boost-python-runtest builtin_converters : test_builtin_converters.py builtin_converters ;