mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
keyword argument support, removed last traces of std::function<> usage
This commit is contained in:
parent
71867830f5
commit
a576e6a8ca
@ -24,7 +24,7 @@ string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-unsequenced")
|
||||
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -flto")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -60,6 +60,7 @@ add_library(example SHARED
|
||||
example/example8.cpp
|
||||
example/example9.cpp
|
||||
example/example10.cpp
|
||||
example/example11.cpp
|
||||
)
|
||||
|
||||
set_target_properties(example PROPERTIES PREFIX "")
|
||||
|
17
README.md
17
README.md
@ -56,19 +56,6 @@ In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
- It is possible to bind C++11 lambda functions with captured variables. The
|
||||
lambda capture data is stored inside the resulting Python function object.
|
||||
|
||||
## Limitations
|
||||
Various things that Boost.Python can do remain unsupported, e.g.:
|
||||
|
||||
- Fine grained exception translation: currently, all exceptions derived from
|
||||
`std::exception` are mapped to a Python `Exception`, but that's it.
|
||||
|
||||
- Default arguments in C++ functions are ignored, though their effect can be
|
||||
emulated by binding multiple overloads using anonymous functions.
|
||||
|
||||
- Python keyword arguments are not supported in bindings
|
||||
|
||||
- Weak pointers are not supported
|
||||
|
||||
## What does the binding code look like?
|
||||
Here is a simple example. The directory `example` contains many more.
|
||||
```C++
|
||||
@ -180,7 +167,7 @@ objects (e.g. a NumPy matrix).
|
||||
py::class_<Eigen::MatrixXd>(m, "MatrixXd")
|
||||
.def("__init__", [](Eigen::MatrixXd &m, py::buffer b) {
|
||||
/* Request a buffer descriptor from Python */
|
||||
py::buffer_info info = b.request();
|
||||
py::buffer_info info = b.request();
|
||||
|
||||
/* Some sanity checks ... */
|
||||
if (info.format != py::format_descriptor<double>::value())
|
||||
@ -231,7 +218,7 @@ m.def("vectorized_func", py::vectorize(my_func));
|
||||
```
|
||||
Invoking the function like below causes 4 calls to be made to ``my_func`` with
|
||||
each of the the array elements. The result is returned as a NumPy array of type
|
||||
``numpy.dtype.float64``.
|
||||
``numpy.dtype.float64``.
|
||||
```Python
|
||||
>>> x = np.array([[1, 3],[5, 7]])
|
||||
>>> y = np.array([[2, 4],[6, 8]])
|
||||
|
@ -19,6 +19,7 @@ void init_ex7(py::module &);
|
||||
void init_ex8(py::module &);
|
||||
void init_ex9(py::module &);
|
||||
void init_ex10(py::module &);
|
||||
void init_ex11(py::module &);
|
||||
|
||||
PYTHON_PLUGIN(example) {
|
||||
py::module m("example", "pybind example plugin");
|
||||
@ -33,6 +34,7 @@ PYTHON_PLUGIN(example) {
|
||||
init_ex8(m);
|
||||
init_ex9(m);
|
||||
init_ex10(m);
|
||||
init_ex11(m);
|
||||
|
||||
return m.ptr();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
example/example1.cpp -- Example 1: constructors, deconstructors,
|
||||
attribute access, __str__, argument and return value conventions
|
||||
example/example1.cpp -- constructors, deconstructors, attribute access,
|
||||
__str__, argument and return value conventions
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
example/example10.cpp -- Example 10: auto-vectorize functions for NumPy
|
||||
example/example10.cpp -- auto-vectorize functions over NumPy array
|
||||
arguments
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
@ -20,14 +21,16 @@ std::complex<double> my_func3(std::complex<double> c) {
|
||||
}
|
||||
|
||||
void init_ex10(py::module &m) {
|
||||
// Vectorize all arguments (though non-vector arguments are also allowed)
|
||||
// Vectorize all arguments of a function (though non-vector arguments are also allowed)
|
||||
m.def("vectorized_func", py::vectorize(my_func));
|
||||
|
||||
// Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
|
||||
m.def("vectorized_func2",
|
||||
[](py::array_dtype<int> x, py::array_dtype<float> y, float z) {
|
||||
return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y);
|
||||
}
|
||||
);
|
||||
// Vectorize all arguments (complex numbers)
|
||||
|
||||
// Vectorize a complex-valued function
|
||||
m.def("vectorized_func3", py::vectorize(my_func3));
|
||||
}
|
||||
|
0
example/example10.py
Normal file → Executable file
0
example/example10.py
Normal file → Executable file
17
example/example11.cpp
Normal file
17
example/example11.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
example/example11.cpp -- keyword arguments and default values
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "example.h"
|
||||
|
||||
void kw_func(int x, int y) { std::cout << "kw_func(x=" << x << ", y=" << y << ")" << std::endl; }
|
||||
|
||||
void init_ex11(py::module &m) {
|
||||
m.def("kw_func", &kw_func, py::arg("x"), py::arg("y"));
|
||||
m.def("kw_func2", &kw_func, py::arg("x") = 100, py::arg("y") = 200);
|
||||
}
|
25
example/example11.py
Executable file
25
example/example11.py
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, pydoc
|
||||
sys.path.append('.')
|
||||
|
||||
import example
|
||||
|
||||
from example import kw_func
|
||||
from example import kw_func2
|
||||
|
||||
print(pydoc.render_doc(kw_func, "Help on %s"))
|
||||
print(pydoc.render_doc(kw_func2, "Help on %s"))
|
||||
|
||||
kw_func(5, 10)
|
||||
kw_func(5, y = 10)
|
||||
kw_func(y = 10, x = 5)
|
||||
|
||||
kw_func2()
|
||||
|
||||
kw_func2(5)
|
||||
kw_func2(x=5)
|
||||
|
||||
kw_func2(y=10)
|
||||
|
||||
kw_func2(5, 10)
|
||||
kw_func2(x=5, y=10)
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
example/example2.cpp2 -- Example 2: singleton design pattern, static
|
||||
functions and variables, passing and interacting with Python types
|
||||
example/example2.cpp2 -- singleton design pattern, static functions and
|
||||
variables, passing and interacting with Python types
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
example/example3.cpp -- Example 3: operator overloading
|
||||
example/example3.cpp -- operator overloading
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
example/example4.cpp -- Example 4: global constants and functions, enumerations
|
||||
example/example4.cpp -- global constants and functions, enumerations
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
example/example5.cpp -- Example 5: inheritance, callbacks, acquiring
|
||||
and releasing the global interpreter lock
|
||||
example/example5.cpp -- inheritance, callbacks, acquiring and releasing the
|
||||
global interpreter lock
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
example/example6.cpp -- Example 6: supporting Pythons' sequence
|
||||
protocol, iterators, etc.
|
||||
example/example6.cpp -- supporting Pythons' sequence protocol, iterators,
|
||||
etc.
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
example/example7.cpp -- Example 7: supporting Pythons' buffer protocol
|
||||
example/example7.cpp -- supporting Pythons' buffer protocol
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
example/example8.cpp -- Example 8: binding classes with
|
||||
custom reference counting, implicit conversions between types
|
||||
example/example8.cpp -- binding classes with custom reference counting,
|
||||
implicit conversions between types
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
0
example/example8.py
Normal file → Executable file
0
example/example8.py
Normal file → Executable file
@ -1,6 +1,5 @@
|
||||
/*
|
||||
example/example9.cpp -- Example 9: nested modules
|
||||
and internal references
|
||||
example/example9.cpp -- nested modules and internal references
|
||||
|
||||
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
|
||||
|
0
example/example9.py
Normal file → Executable file
0
example/example9.py
Normal file → Executable file
0
example/run_test.py
Normal file → Executable file
0
example/run_test.py
Normal file → Executable file
@ -392,14 +392,22 @@ public:
|
||||
return cast(src, policy, parent, typename make_index_sequence<size>::type());
|
||||
}
|
||||
|
||||
static std::string name() {
|
||||
static std::string name(const char **keywords = nullptr, const char **values = nullptr) {
|
||||
std::array<std::string, size> names {{
|
||||
type_caster<typename detail::decay<Tuple>::type>::name()...
|
||||
}};
|
||||
std::string result("(");
|
||||
int counter = 0;
|
||||
for (auto const &name : names) {
|
||||
if (keywords && keywords[counter]) {
|
||||
result += keywords[counter];
|
||||
result += " : ";
|
||||
}
|
||||
result += name;
|
||||
if (values && values[counter]) {
|
||||
result += " = ";
|
||||
result += values[counter];
|
||||
}
|
||||
if (++counter < size)
|
||||
result += ", ";
|
||||
}
|
||||
@ -407,12 +415,12 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ReturnValue, typename Func> typename std::enable_if<!std::is_void<ReturnValue>::value, ReturnValue>::type call(Func &f) {
|
||||
return call<ReturnValue, Func>(f, typename make_index_sequence<sizeof...(Tuple)>::type());
|
||||
template <typename ReturnValue, typename Func> typename std::enable_if<!std::is_void<ReturnValue>::value, ReturnValue>::type call(Func &&f) {
|
||||
return call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
|
||||
}
|
||||
|
||||
template <typename ReturnValue, typename Func> typename std::enable_if<std::is_void<ReturnValue>::value, detail::void_type>::type call(Func &f) {
|
||||
call<ReturnValue, Func>(f, typename make_index_sequence<sizeof...(Tuple)>::type());
|
||||
template <typename ReturnValue, typename Func> typename std::enable_if<std::is_void<ReturnValue>::value, detail::void_type>::type call(Func &&f) {
|
||||
call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
|
||||
return detail::void_type();
|
||||
}
|
||||
|
||||
@ -421,7 +429,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename ReturnValue, typename Func, size_t ... Index> ReturnValue call(Func &f, index_sequence<Index...>) {
|
||||
template <typename ReturnValue, typename Func, size_t ... Index> ReturnValue call(Func &&f, index_sequence<Index...>) {
|
||||
return f((Tuple) std::get<Index>(value)...);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <pybind/pybind.h>
|
||||
#include <functional>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
@ -132,6 +134,15 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
|
||||
DECL_FMT(int8_t, NPY_BYTE); DECL_FMT(uint8_t, NPY_UBYTE); DECL_FMT(int16_t, NPY_SHORT);
|
||||
DECL_FMT(uint16_t, NPY_USHORT); DECL_FMT(int32_t, NPY_INT); DECL_FMT(uint32_t, NPY_UINT);
|
||||
DECL_FMT(int64_t, NPY_LONGLONG); DECL_FMT(uint64_t, NPY_ULONGLONG); DECL_FMT(float, NPY_FLOAT);
|
||||
DECL_FMT(double, NPY_DOUBLE); DECL_FMT(bool, NPY_BOOL); DECL_FMT(std::complex<float>, NPY_CFLOAT);
|
||||
DECL_FMT(std::complex<double>, NPY_CDOUBLE);
|
||||
#undef DECL_FMT
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
PYBIND_TYPE_CASTER_PYTYPE(array)
|
||||
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<int8_t>) PYBIND_TYPE_CASTER_PYTYPE(array_dtype<uint8_t>)
|
||||
@ -142,24 +153,20 @@ PYBIND_TYPE_CASTER_PYTYPE(array_dtype<float>) PYBIND_TYPE_CASTER_PYTYPE(array_
|
||||
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<std::complex<float>>)
|
||||
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<std::complex<double>>)
|
||||
PYBIND_TYPE_CASTER_PYTYPE(array_dtype<bool>)
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
#define DECL_FMT(t, n) template<> struct npy_format_descriptor<t> { enum { value = array::API::n }; }
|
||||
DECL_FMT(int8_t, NPY_BYTE); DECL_FMT(uint8_t, NPY_UBYTE); DECL_FMT(int16_t, NPY_SHORT);
|
||||
DECL_FMT(uint16_t, NPY_USHORT); DECL_FMT(int32_t, NPY_INT); DECL_FMT(uint32_t, NPY_UINT);
|
||||
DECL_FMT(int64_t, NPY_LONGLONG); DECL_FMT(uint64_t, NPY_ULONGLONG); DECL_FMT(float, NPY_FLOAT);
|
||||
DECL_FMT(double, NPY_DOUBLE); DECL_FMT(bool, NPY_BOOL); DECL_FMT(std::complex<float>, NPY_CFLOAT);
|
||||
DECL_FMT(std::complex<double>, NPY_CDOUBLE);
|
||||
#undef DECL_FMT
|
||||
template <typename Func, typename Return, typename... Args>
|
||||
struct vectorize_helper {
|
||||
typename std::remove_reference<Func>::type f;
|
||||
|
||||
template <typename func_type, typename return_type, typename... args_type, size_t... Index>
|
||||
std::function<object(array_dtype<args_type>...)>
|
||||
vectorize(func_type &&f, return_type (*) (args_type ...),
|
||||
detail::index_sequence<Index...>) {
|
||||
vectorize_helper(const Func &f) : f(f) { }
|
||||
|
||||
return [f](array_dtype<args_type>... args) -> array {
|
||||
object operator()(array_dtype<Args>... args) {
|
||||
return run(args..., typename make_index_sequence<sizeof...(Args)>::type());
|
||||
}
|
||||
|
||||
template <size_t ... Index> object run(array_dtype<Args>&... args, index_sequence<Index...>) {
|
||||
/* Request buffers from all parameters */
|
||||
const size_t N = sizeof...(args_type);
|
||||
const size_t N = sizeof...(Args);
|
||||
std::array<buffer_info, N> buffers {{ args.request()... }};
|
||||
|
||||
/* Determine dimensions parameters of output array */
|
||||
@ -174,7 +181,7 @@ template <typename func_type, typename return_type, typename... args_type, size_
|
||||
}
|
||||
std::vector<size_t> strides(ndim);
|
||||
if (ndim > 0) {
|
||||
strides[ndim-1] = sizeof(return_type);
|
||||
strides[ndim-1] = sizeof(Return);
|
||||
for (int i=ndim-1; i>0; --i)
|
||||
strides[i-1] = strides[i] * shape[i];
|
||||
}
|
||||
@ -186,31 +193,32 @@ template <typename func_type, typename return_type, typename... args_type, size_
|
||||
}
|
||||
|
||||
/* Call the function */
|
||||
std::vector<return_type> result(count);
|
||||
std::vector<Return> result(count);
|
||||
for (size_t i=0; i<count; ++i)
|
||||
result[i] = f((buffers[Index].count == 1
|
||||
? *((args_type *) buffers[Index].ptr)
|
||||
: ((args_type *) buffers[Index].ptr)[i])...);
|
||||
? *((Args *) buffers[Index].ptr)
|
||||
: ((Args *) buffers[Index].ptr)[i])...);
|
||||
|
||||
if (count == 1)
|
||||
return cast(result[0]);
|
||||
|
||||
/* Return the result */
|
||||
return array(buffer_info(result.data(), sizeof(return_type),
|
||||
format_descriptor<return_type>::value(),
|
||||
return array(buffer_info(result.data(), sizeof(Return),
|
||||
format_descriptor<Return>::value(),
|
||||
ndim, shape, strides));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <typename Func, typename Return, typename... Args>
|
||||
detail::vectorize_helper<Func, Return, Args...> vectorize(const Func &f, Return (*) (Args ...)) {
|
||||
return detail::vectorize_helper<Func, Return, Args...>(f);
|
||||
}
|
||||
|
||||
template <typename func_type, typename return_type, typename... args_type>
|
||||
std::function<object(array_dtype<args_type>...)>
|
||||
vectorize(func_type &&f, return_type (*f_) (args_type ...) = nullptr) {
|
||||
return vectorize(f, f_, typename detail::make_index_sequence<sizeof...(args_type)>::type());
|
||||
}
|
||||
|
||||
template <typename return_type, typename... args_type>
|
||||
std::function<object(array_dtype<args_type>...)> vectorize(return_type (*f) (args_type ...)) {
|
||||
return vectorize(f, f);
|
||||
template <typename Return, typename... Args>
|
||||
detail::vectorize_helper<Return (*) (Args ...), Return, Args...> vectorize(Return (*f) (Args ...)) {
|
||||
return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
|
||||
}
|
||||
|
||||
template <typename func> auto vectorize(func &&f) -> decltype(
|
||||
|
@ -76,26 +76,82 @@ private:
|
||||
template <typename... T> using arg_value_caster =
|
||||
detail::type_caster<typename std::tuple<T...>>;
|
||||
|
||||
|
||||
template <typename... T> void process_args(const std::tuple<T...> &args, function_entry *entry) {
|
||||
process_args(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
|
||||
template <typename... T> static void process_extras(const std::tuple<T...> &args,
|
||||
function_entry *entry, const char **kw, const char **def) {
|
||||
process_extras(args, entry, kw, def, typename detail::make_index_sequence<sizeof...(T)>::type());
|
||||
}
|
||||
|
||||
template <typename... T, size_t ... Index> void process_args(const
|
||||
std::tuple<T...> &args, function_entry *entry,
|
||||
detail::index_sequence<Index...>) {
|
||||
int unused[] = { 0, (process_arg(std::get<Index>(args), entry), 0)... };
|
||||
template <typename... T, size_t ... Index> static void process_extras(const std::tuple<T...> &args,
|
||||
function_entry *entry, const char **kw, const char **def, detail::index_sequence<Index...>) {
|
||||
int unused[] = { 0, (process_extra(std::get<Index>(args), entry, kw, def), 0)... };
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
void process_arg(const char *doc, function_entry *entry) { entry->doc = doc; }
|
||||
void process_arg(const pybind::doc &d, function_entry *entry) { entry->doc = d.value; }
|
||||
void process_arg(const pybind::name &n, function_entry *entry) { entry->name = n.value; }
|
||||
void process_arg(const pybind::arg &, function_entry *entry) { entry->keywords++; }
|
||||
void process_arg(const pybind::is_method &, function_entry *entry) { entry->is_method = true; }
|
||||
void process_arg(const pybind::return_value_policy p, function_entry *entry) { entry->policy = p; }
|
||||
void process_arg(pybind::sibling s, function_entry *entry) { entry->sibling = s.value; }
|
||||
template <typename... T> static void process_extras(const std::tuple<T...> &args,
|
||||
PyObject *pyArgs, PyObject *kwargs, bool is_method) {
|
||||
process_extras(args, pyArgs, kwargs, is_method, typename detail::make_index_sequence<sizeof...(T)>::type());
|
||||
}
|
||||
|
||||
template <typename... T, size_t... Index> static void process_extras(const std::tuple<T...> &args,
|
||||
PyObject *pyArgs, PyObject *kwargs, bool is_method, detail::index_sequence<Index...>) {
|
||||
int index = is_method ? 1 : 0;
|
||||
int unused[] = { 0, (process_extra(std::get<Index>(args), index, pyArgs, kwargs), 0)... };
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
static void process_extra(const char *doc, function_entry *entry, const char **, const char **) { entry->doc = doc; }
|
||||
static void process_extra(const pybind::doc &d, function_entry *entry, const char **, const char **) { entry->doc = d.value; }
|
||||
static void process_extra(const pybind::name &n, function_entry *entry, const char **, const char **) { entry->name = n.value; }
|
||||
static void process_extra(const pybind::arg &a, function_entry *entry, const char **kw, const char **) {
|
||||
if (entry->is_method && entry->keywords == 0)
|
||||
kw[entry->keywords++] = "self";
|
||||
kw[entry->keywords++] = a.name;
|
||||
}
|
||||
template <typename T>
|
||||
static void process_extra(const pybind::arg_t<T> &a, function_entry *entry, const char **kw, const char **def) {
|
||||
if (entry->is_method && entry->keywords == 0)
|
||||
kw[entry->keywords++] = "self";
|
||||
kw[entry->keywords] = a.name;
|
||||
def[entry->keywords++] = strdup(std::to_string(a.value).c_str());
|
||||
}
|
||||
|
||||
static void process_extra(const pybind::is_method &, function_entry *entry, const char **, const char **) { entry->is_method = true; }
|
||||
static void process_extra(const pybind::return_value_policy p, function_entry *entry, const char **, const char **) { entry->policy = p; }
|
||||
static void process_extra(pybind::sibling s, function_entry *entry, const char **, const char **) { entry->sibling = s.value; }
|
||||
|
||||
template <typename T> static void process_extra(T, int &, PyObject *, PyObject *) { }
|
||||
static void process_extra(const pybind::arg &a, int &index, PyObject *args, PyObject *kwargs) {
|
||||
if (kwargs) {
|
||||
if (PyTuple_GET_ITEM(args, index) != nullptr) {
|
||||
index++;
|
||||
return;
|
||||
}
|
||||
PyObject *value = PyDict_GetItemString(kwargs, a.name);
|
||||
if (value) {
|
||||
Py_INCREF(value);
|
||||
PyTuple_SetItem(args, index, value);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
template <typename T>
|
||||
static void process_extra(const pybind::arg_t<T> &a, int &index, PyObject *args, PyObject *kwargs) {
|
||||
if (PyTuple_GET_ITEM(args, index) != nullptr) {
|
||||
index++;
|
||||
return;
|
||||
}
|
||||
PyObject *value = nullptr;
|
||||
if (kwargs)
|
||||
value = PyDict_GetItemString(kwargs, a.name);
|
||||
if (value) {
|
||||
Py_INCREF(value);
|
||||
} else {
|
||||
value = detail::type_caster<typename detail::decay<T>::type>::cast(
|
||||
a.value, return_value_policy::automatic, nullptr);
|
||||
}
|
||||
PyTuple_SetItem(args, index, value);
|
||||
index++;
|
||||
}
|
||||
public:
|
||||
cpp_function() { }
|
||||
|
||||
@ -104,7 +160,7 @@ public:
|
||||
cpp_function(Return (*f)(Arg...), Extra&&... extra) {
|
||||
struct capture {
|
||||
Return (*f)(Arg...);
|
||||
std::tuple<Extra...> extra;
|
||||
std::tuple<Extra...> extras;
|
||||
};
|
||||
|
||||
function_entry *entry = new function_entry();
|
||||
@ -114,18 +170,23 @@ public:
|
||||
typedef return_value_caster<Return> cast_out;
|
||||
|
||||
entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * {
|
||||
capture *data = (capture *) entry->data;
|
||||
process_extras(data->extras, pyArgs, kwargs, entry->is_method);
|
||||
cast_in args;
|
||||
if (!args.load(pyArgs, true)) return nullptr;
|
||||
auto f = ((capture *) entry->data)->f;
|
||||
(void)kwargs;
|
||||
return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
|
||||
if (!args.load(pyArgs, true))
|
||||
return nullptr;
|
||||
return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
|
||||
};
|
||||
|
||||
entry->signature = cast_in::name();
|
||||
const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
|
||||
std::array<const char *, N> kw{}, def{};
|
||||
process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
|
||||
|
||||
entry->signature = cast_in::name(kw.data(), def.data());
|
||||
entry->signature += " -> ";
|
||||
entry->signature += cast_out::name();
|
||||
process_args(((capture *) entry->data)->extra, entry);
|
||||
initialize(entry);
|
||||
|
||||
initialize(entry, sizeof...(Arg));
|
||||
}
|
||||
|
||||
/// Delegating helper constructor to deal with lambda functions
|
||||
@ -136,7 +197,6 @@ public:
|
||||
std::forward<Extra>(extra)...);
|
||||
}
|
||||
|
||||
|
||||
/// Class methods (non-const)
|
||||
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
|
||||
Return (Class::*f)(Arg...), Extra&&... extra) {
|
||||
@ -157,7 +217,7 @@ private:
|
||||
void initialize(Func &&f, Return (*)(Arg...), Extra&&... extra) {
|
||||
struct capture {
|
||||
typename std::remove_reference<Func>::type f;
|
||||
std::tuple<Extra...> extra;
|
||||
std::tuple<Extra...> extras;
|
||||
};
|
||||
|
||||
function_entry *entry = new function_entry();
|
||||
@ -167,27 +227,50 @@ private:
|
||||
typedef return_value_caster<Return> cast_out;
|
||||
|
||||
entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{
|
||||
capture *data = (capture *)entry->data;
|
||||
process_extras(data->extras, pyArgs, kwargs, entry->is_method);
|
||||
cast_in args;
|
||||
if (!args.load(pyArgs, true)) return nullptr;
|
||||
Func &f = ((capture *) entry->data)->f;
|
||||
(void)kwargs;
|
||||
return cast_out::cast(args.template call<Return>(f), entry->policy, parent);
|
||||
if (!args.load(pyArgs, true))
|
||||
return nullptr;
|
||||
return cast_out::cast(args.template call<Return>(data->f), entry->policy, parent);
|
||||
};
|
||||
|
||||
entry->signature = cast_in::name();
|
||||
const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg);
|
||||
std::array<const char *, N> kw{}, def{};
|
||||
process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data());
|
||||
|
||||
entry->signature = cast_in::name(kw.data(), def.data());
|
||||
entry->signature += " -> ";
|
||||
entry->signature += cast_out::name();
|
||||
process_args(((capture *) entry->data)->extra, entry);
|
||||
initialize(entry);
|
||||
|
||||
initialize(entry, sizeof...(Arg));
|
||||
}
|
||||
|
||||
static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) {
|
||||
function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr);
|
||||
int nargs = (int) PyTuple_Size(args);
|
||||
PyObject *result = nullptr;
|
||||
PyObject *parent = PyTuple_Size(args) > 0 ? PyTuple_GetItem(args, 0) : nullptr;
|
||||
PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr;
|
||||
try {
|
||||
for (function_entry *it = overloads; it != nullptr; it = it->next) {
|
||||
if ((result = it->impl(it, args, kwargs, parent)) != nullptr)
|
||||
PyObject *args_ = args;
|
||||
|
||||
if (it->keywords != 0 && it->keywords != nargs) {
|
||||
args_ = PyTuple_New(it->keywords);
|
||||
for (int i=0; i<nargs; ++i) {
|
||||
PyObject *item = PyTuple_GET_ITEM(args, i);
|
||||
Py_INCREF(item);
|
||||
PyTuple_SET_ITEM(args_, i, item);
|
||||
}
|
||||
}
|
||||
|
||||
result = it->impl(it, args_, kwargs, parent);
|
||||
|
||||
if (args_ != args) {
|
||||
Py_DECREF(args_);
|
||||
}
|
||||
|
||||
if (result != nullptr)
|
||||
break;
|
||||
}
|
||||
} catch (const error_already_set &) { return nullptr;
|
||||
@ -221,9 +304,14 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void initialize(function_entry *entry) {
|
||||
void initialize(function_entry *entry, int args) {
|
||||
if (entry->name == nullptr)
|
||||
entry->name = "";
|
||||
if (entry->keywords != 0 && entry->keywords != args)
|
||||
throw std::runtime_error(
|
||||
"cpp_function(): function \"" + std::string(entry->name) + "\" takes " +
|
||||
std::to_string(args) + " arguments, but " + std::to_string(entry->keywords) +
|
||||
" pybind::arg entries were specified!");
|
||||
|
||||
entry->is_constructor = !strcmp(entry->name, "__init__");
|
||||
|
||||
@ -578,32 +666,32 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
class_ &def_property_readonly(const char *name, const cpp_function &fget) {
|
||||
def_property(name, fget, cpp_function());
|
||||
class_ &def_property_readonly(const char *name, const cpp_function &fget, const char *doc = nullptr) {
|
||||
def_property(name, fget, cpp_function(), doc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
class_ &def_property_readonly_static(const char *name, const cpp_function &fget) {
|
||||
def_property_static(name, fget, cpp_function());
|
||||
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const char *doc = nullptr) {
|
||||
def_property_static(name, fget, cpp_function(), doc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset) {
|
||||
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
|
||||
object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
|
||||
object property(
|
||||
PyObject_CallFunction((PyObject *)&PyProperty_Type,
|
||||
const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
|
||||
fset.ptr() ? fset.ptr() : Py_None, Py_None,
|
||||
((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
|
||||
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
|
||||
attr(name) = property;
|
||||
return *this;
|
||||
}
|
||||
|
||||
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset) {
|
||||
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
|
||||
object doc_obj = doc ? pybind::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
|
||||
object property(
|
||||
PyObject_CallFunction((PyObject *)&PyProperty_Type,
|
||||
const_cast<char *>("OOOO"), fget.ptr() ? fget.ptr() : Py_None,
|
||||
fset.ptr() ? fset.ptr() : Py_None, Py_None,
|
||||
((object) const_cast<cpp_function&>(fget).attr("__doc__")).ptr()), false);
|
||||
const_cast<char *>("OOOs"), fget.ptr() ? fget.ptr() : Py_None,
|
||||
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr()), false);
|
||||
metaclass().attr(name) = property;
|
||||
return *this;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user