⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 function.cpp

📁 C++的一个好库。。。现在很流行
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// 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 + -