Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
gaoqiong
pybind11
Commits
18e34cb2
Commit
18e34cb2
authored
Feb 05, 2017
by
Wenzel Jakob
Committed by
GitHub
Feb 05, 2017
Browse files
Merge pull request #634 from jagerman/noconvert-arguments
Add support for non-converting arguments
parents
40db2c75
abc29cad
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
276 additions
and
61 deletions
+276
-61
docs/advanced/classes.rst
docs/advanced/classes.rst
+2
-0
docs/advanced/functions.rst
docs/advanced/functions.rst
+54
-0
include/pybind11/attr.h
include/pybind11/attr.h
+13
-23
include/pybind11/cast.h
include/pybind11/cast.h
+80
-22
include/pybind11/complex.h
include/pybind11/complex.h
+3
-1
include/pybind11/pybind11.h
include/pybind11/pybind11.h
+23
-15
tests/test_methods_and_attributes.cpp
tests/test_methods_and_attributes.cpp
+57
-0
tests/test_methods_and_attributes.py
tests/test_methods_and_attributes.py
+44
-0
No files found.
docs/advanced/classes.rst
View file @
18e34cb2
...
...
@@ -388,6 +388,8 @@ crucial that instances are deallocated on the C++ side to avoid memory leaks.
py::class_<MyClass, std::unique_ptr<MyClass, py::nodelete>>(m, "MyClass")
.def(py::init<>())
.. _implicit_conversions:
Implicit conversions
====================
...
...
docs/advanced/functions.rst
View file @
18e34cb2
...
...
@@ -318,3 +318,57 @@ like so:
py::class_<MyClass>("MyClass")
.def("myFunction", py::arg("arg") = (SomeType *) nullptr);
Non-converting arguments
========================
Certain argument types may support conversion from one type to another. Some
examples of conversions are:
* :ref:`implicit_conversions` declared using ``py::implicitly_convertible<A,B>()``
* Calling a method accepting a double with an integer argument
* Calling a ``std::complex<float>`` argument with a non-complex python type
(for example, with a float). (Requires the optional ``pybind11/complex.h``
header).
* Calling a function taking an Eigen matrix reference with a numpy array of the
wrong type or of an incompatible data layout. (Requires the optional
``pybind11/eigen.h`` header).
This behaviour is sometimes undesirable: the binding code may prefer to raise
an error rather than convert the argument. This behaviour can be obtained
through ``py::arg`` by calling the ``.noconvert()`` method of the ``py::arg``
object, such as:
.. code-block:: cpp
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
Attempting the call the second function (the one without ``.noconvert()``) with
an integer will succeed, but attempting to call the ``.noconvert()`` version
will fail with a ``TypeError``:
.. code-block:: pycon
>>> floats_preferred(4)
2.0
>>> floats_only(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: floats_only(): incompatible function arguments. The following argument types are supported:
1. (f: float) -> float
Invoked with: 4
You may, of course, combine this with the :var:`_a` shorthand notation (see
:ref:`keyword_args`) and/or :ref:`default_args`. It is also permitted to omit
the argument name by using the ``py::arg()`` constructor without an argument
name, i.e. by specifying ``py::arg().noconvert()``.
.. note::
When specifying ``py::arg`` options it is necessary to provide the same
number of options as the bound function has arguments. Thus if you want to
enable no-convert behaviour for just one of several arguments, you will
need to specify a ``py::arg()`` annotation for each argument with the
no-convert argument modified to ``py::arg().noconvert()``.
include/pybind11/attr.h
View file @
18e34cb2
...
...
@@ -69,7 +69,6 @@ struct undefined_t;
template
<
op_id
id
,
op_type
ot
,
typename
L
=
undefined_t
,
typename
R
=
undefined_t
>
struct
op_
;
template
<
typename
...
Args
>
struct
init
;
template
<
typename
...
Args
>
struct
init_alias
;
struct
function_call
;
inline
void
keep_alive_impl
(
size_t
Nurse
,
size_t
Patient
,
function_call
&
call
,
handle
ret
);
/// Internal data structure which holds metadata about a keyword argument
...
...
@@ -77,9 +76,10 @@ struct argument_record {
const
char
*
name
;
///< Argument name
const
char
*
descr
;
///< Human-readable version of the argument value
handle
value
;
///< Associated Python object
bool
convert
:
1
;
///< True if the argument is allowed to convert when loading
argument_record
(
const
char
*
name
,
const
char
*
descr
,
handle
value
)
:
name
(
name
),
descr
(
descr
),
value
(
value
)
{
}
argument_record
(
const
char
*
name
,
const
char
*
descr
,
handle
value
,
bool
convert
)
:
name
(
name
),
descr
(
descr
),
value
(
value
)
,
convert
(
convert
)
{
}
};
/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.)
...
...
@@ -131,7 +131,7 @@ struct function_record {
bool
is_method
:
1
;
/// Number of arguments (including py::args and/or py::kwargs, if present)
uint16_t
nargs
;
std
::
uint16_t
nargs
;
/// Python method object
PyMethodDef
*
def
=
nullptr
;
...
...
@@ -222,21 +222,11 @@ struct type_record {
}
};
/// Internal data associated with a single function call
struct
function_call
{
function_call
(
function_record
&
f
,
handle
p
)
:
func
(
f
),
parent
(
p
)
{
args
.
reserve
(
f
.
nargs
);
}
/// The function data:
const
function_record
&
func
;
/// Arguments passed to the function:
std
::
vector
<
handle
>
args
;
/// The parent, if any
handle
parent
;
};
inline
function_call
::
function_call
(
function_record
&
f
,
handle
p
)
:
func
(
f
),
parent
(
p
)
{
args
.
reserve
(
f
.
nargs
);
args_convert
.
reserve
(
f
.
nargs
);
}
/**
* Partial template specializations to process custom attributes provided to
...
...
@@ -300,8 +290,8 @@ template <> struct process_attribute<is_operator> : process_attribute_default<is
template
<
>
struct
process_attribute
<
arg
>
:
process_attribute_default
<
arg
>
{
static
void
init
(
const
arg
&
a
,
function_record
*
r
)
{
if
(
r
->
is_method
&&
r
->
args
.
empty
())
r
->
args
.
emplace_back
(
"self"
,
nullptr
,
handle
());
r
->
args
.
emplace_back
(
a
.
name
,
nullptr
,
handle
());
r
->
args
.
emplace_back
(
"self"
,
nullptr
,
handle
()
,
true
/*convert*/
);
r
->
args
.
emplace_back
(
a
.
name
,
nullptr
,
handle
()
,
!
a
.
flag_noconvert
);
}
};
...
...
@@ -309,7 +299,7 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
template
<
>
struct
process_attribute
<
arg_v
>
:
process_attribute_default
<
arg_v
>
{
static
void
init
(
const
arg_v
&
a
,
function_record
*
r
)
{
if
(
r
->
is_method
&&
r
->
args
.
empty
())
r
->
args
.
emplace_back
(
"self"
,
nullptr
,
handle
()
);
r
->
args
.
emplace_back
(
"self"
,
nullptr
/*descr*/
,
handle
()
/*parent*/
,
true
/*convert*/
);
if
(
!
a
.
value
)
{
#if !defined(NDEBUG)
...
...
@@ -330,7 +320,7 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
"Compile in debug mode for more information."
);
#endif
}
r
->
args
.
emplace_back
(
a
.
name
,
a
.
descr
,
a
.
value
.
inc_ref
());
r
->
args
.
emplace_back
(
a
.
name
,
a
.
descr
,
a
.
value
.
inc_ref
()
,
!
a
.
flag_noconvert
);
}
};
...
...
include/pybind11/cast.h
View file @
18e34cb2
...
...
@@ -473,18 +473,22 @@ public:
template
<
typename
T
>
struct
type_caster
<
T
,
enable_if_t
<
std
::
is_arithmetic
<
T
>::
value
>>
{
typedef
typename
std
::
conditional
<
sizeof
(
T
)
<=
sizeof
(
long
),
long
,
long
long
>
::
type
_py_type_0
;
typedef
typename
std
::
conditional
<
std
::
is_signed
<
T
>::
value
,
_py_type_0
,
typename
std
::
make_unsigned
<
_py_type_0
>::
type
>
::
type
_py_type_1
;
typedef
typename
std
::
conditional
<
std
::
is_floating_point
<
T
>::
value
,
double
,
_py_type_1
>
::
type
py_type
;
using
_py_type_0
=
conditional
_t
<
sizeof
(
T
)
<=
sizeof
(
long
),
long
,
long
long
>
;
using
_py_type_1
=
conditional
_t
<
std
::
is_signed
<
T
>::
value
,
_py_type_0
,
typename
std
::
make_unsigned
<
_py_type_0
>::
type
>
;
using
py_type
=
conditional
_t
<
std
::
is_floating_point
<
T
>::
value
,
double
,
_py_type_1
>
;
public:
bool
load
(
handle
src
,
bool
)
{
bool
load
(
handle
src
,
bool
convert
)
{
py_type
py_value
;
if
(
!
src
)
{
if
(
!
src
)
return
false
;
}
if
(
std
::
is_floating_point
<
T
>::
value
)
{
py_value
=
(
py_type
)
PyFloat_AsDouble
(
src
.
ptr
());
if
(
std
::
is_floating_point
<
T
>::
value
)
{
if
(
convert
||
PyFloat_Check
(
src
.
ptr
()))
py_value
=
(
py_type
)
PyFloat_AsDouble
(
src
.
ptr
());
else
return
false
;
}
else
if
(
sizeof
(
T
)
<=
sizeof
(
long
))
{
if
(
PyFloat_Check
(
src
.
ptr
()))
return
false
;
...
...
@@ -511,7 +515,7 @@ public:
bool
type_error
=
PyErr_ExceptionMatches
(
PyExc_TypeError
);
#endif
PyErr_Clear
();
if
(
type_error
&&
PyNumber_Check
(
src
.
ptr
()))
{
if
(
type_error
&&
convert
&&
PyNumber_Check
(
src
.
ptr
()))
{
auto
tmp
=
reinterpret_borrow
<
object
>
(
std
::
is_floating_point
<
T
>::
value
?
PyNumber_Float
(
src
.
ptr
())
:
PyNumber_Long
(
src
.
ptr
()));
...
...
@@ -1198,22 +1202,26 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
}
/// \ingroup annotations
/// Annotation for
keyword
arguments
/// Annotation for arguments
struct
arg
{
///
Set the name of the argument
constexpr
explicit
arg
(
const
char
*
name
)
:
name
(
name
)
{
}
///
Constructs an argument with the name of the argument; if null or omitted, this is a positional argument.
constexpr
explicit
arg
(
const
char
*
name
=
nullptr
)
:
name
(
name
)
,
flag_noconvert
(
false
)
{
}
/// Assign a value to this argument
template
<
typename
T
>
arg_v
operator
=
(
T
&&
value
)
const
;
/// Indicate that the type should not be converted in the type caster
arg
&
noconvert
(
bool
flag
=
true
)
{
flag_noconvert
=
flag
;
return
*
this
;
}
const
char
*
name
;
const
char
*
name
;
///< If non-null, this is a named kwargs argument
bool
flag_noconvert
:
1
;
///< If set, do not allow conversion (requires a supporting type caster!)
};
/// \ingroup annotations
/// Annotation for
keyword
arguments with values
/// Annotation for arguments with values
struct
arg_v
:
arg
{
private:
template
<
typename
T
>
arg_v
(
const
char
*
nam
e
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg
(
nam
e
),
arg_v
(
arg
&&
bas
e
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg
(
bas
e
),
value
(
reinterpret_steal
<
object
>
(
detail
::
make_caster
<
T
>::
cast
(
x
,
return_value_policy
::
automatic
,
{})
)),
...
...
@@ -1223,15 +1231,32 @@ struct arg_v : arg {
#endif
{
}
public:
/// Direct construction with name, default, and description
template
<
typename
T
>
arg_v
(
const
char
*
name
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg_v
(
arg
(
name
),
std
::
forward
<
T
>
(
x
),
descr
)
{
}
/// Called internally when invoking `py::arg("a") = value`
template
<
typename
T
>
arg_v
(
const
arg
&
base
,
T
&&
x
,
const
char
*
descr
=
nullptr
)
:
arg_v
(
arg
(
base
),
std
::
forward
<
T
>
(
x
),
descr
)
{
}
/// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg&
arg_v
&
noconvert
(
bool
flag
=
true
)
{
arg
::
noconvert
(
flag
);
return
*
this
;
}
/// The default value
object
value
;
/// The (optional) description of the default value
const
char
*
descr
;
#if !defined(NDEBUG)
/// The C++ type name of the default value (only available when compiled in debug mode)
std
::
string
type
;
#endif
};
template
<
typename
T
>
arg_v
arg
::
operator
=
(
T
&&
value
)
const
{
return
{
name
,
std
::
forward
<
T
>
(
value
)};
}
arg_v
arg
::
operator
=
(
T
&&
value
)
const
{
return
{
std
::
move
(
*
this
)
,
std
::
forward
<
T
>
(
value
)};
}
/// Alias for backward compatibility -- to be removed in version 2.0
template
<
typename
/*unused*/
>
using
arg_t
=
arg_v
;
...
...
@@ -1248,11 +1273,28 @@ NAMESPACE_BEGIN(detail)
// forward declaration
struct
function_record
;
/// Internal data associated with a single function call
struct
function_call
{
function_call
(
function_record
&
f
,
handle
p
);
// Implementation in attr.h
/// The function data:
const
function_record
&
func
;
/// Arguments passed to the function:
std
::
vector
<
handle
>
args
;
/// The `convert` value the arguments should be loaded with
std
::
vector
<
bool
>
args_convert
;
/// The parent, if any
handle
parent
;
};
/// Helper class which loads arguments for C++ functions called from Python
template
<
typename
...
Args
>
class
argument_loader
{
using
indices
=
make_index_sequence
<
sizeof
...(
Args
)
>
;
using
function_arguments
=
const
std
::
vector
<
handle
>
&
;
template
<
typename
Arg
>
using
argument_is_args
=
std
::
is_same
<
intrinsic_t
<
Arg
>
,
args
>
;
template
<
typename
Arg
>
using
argument_is_kwargs
=
std
::
is_same
<
intrinsic_t
<
Arg
>
,
kwargs
>
;
...
...
@@ -1270,8 +1312,8 @@ public:
static
PYBIND11_DESCR
arg_names
()
{
return
detail
::
concat
(
make_caster
<
Args
>::
name
()...);
}
bool
load_args
(
function_
arguments
args
)
{
return
load_impl_sequence
(
args
,
indices
{});
bool
load_args
(
function_
call
&
call
)
{
return
load_impl_sequence
(
call
,
indices
{});
}
template
<
typename
Return
,
typename
Func
>
...
...
@@ -1287,11 +1329,11 @@ public:
private:
static
bool
load_impl_sequence
(
function_
arguments
,
index_sequence
<>
)
{
return
true
;
}
static
bool
load_impl_sequence
(
function_
call
&
,
index_sequence
<>
)
{
return
true
;
}
template
<
size_t
...
Is
>
bool
load_impl_sequence
(
function_
arguments
args
,
index_sequence
<
Is
...
>
)
{
for
(
bool
r
:
{
std
::
get
<
Is
>
(
value
).
load
(
args
[
Is
],
true
)...})
bool
load_impl_sequence
(
function_
call
&
call
,
index_sequence
<
Is
...
>
)
{
for
(
bool
r
:
{
std
::
get
<
Is
>
(
value
).
load
(
call
.
args
[
Is
],
call
.
args_convert
[
Is
]
)...})
if
(
!
r
)
return
false
;
return
true
;
...
...
@@ -1380,6 +1422,13 @@ private:
}
void
process
(
list
&
/*args_list*/
,
arg_v
a
)
{
if
(
!
a
.
name
)
#if defined(NDEBUG)
nameless_argument_error
();
#else
nameless_argument_error
(
a
.
type
);
#endif
if
(
m_kwargs
.
contains
(
a
.
name
))
{
#if defined(NDEBUG)
multiple_values_error
();
...
...
@@ -1412,6 +1461,15 @@ private:
}
}
[[
noreturn
]]
static
void
nameless_argument_error
()
{
throw
type_error
(
"Got kwargs without a name; only named arguments "
"may be passed via py::arg() to a python function call. "
"(compile in debug mode for details)"
);
}
[[
noreturn
]]
static
void
nameless_argument_error
(
std
::
string
type
)
{
throw
type_error
(
"Got kwargs without a name of type '"
+
type
+
"'; only named "
"arguments may be passed via py::arg() to a python function call. "
);
}
[[
noreturn
]]
static
void
multiple_values_error
()
{
throw
type_error
(
"Got multiple values for keyword argument "
"(compile in debug mode for details)"
);
...
...
include/pybind11/complex.h
View file @
18e34cb2
...
...
@@ -28,9 +28,11 @@ template <typename T> struct is_fmt_numeric<std::complex<T>> {
template
<
typename
T
>
class
type_caster
<
std
::
complex
<
T
>>
{
public:
bool
load
(
handle
src
,
bool
)
{
bool
load
(
handle
src
,
bool
convert
)
{
if
(
!
src
)
return
false
;
if
(
!
convert
&&
!
PyComplex_Check
(
src
.
ptr
()))
return
false
;
Py_complex
result
=
PyComplex_AsCComplex
(
src
.
ptr
());
if
(
result
.
real
==
-
1.0
&&
PyErr_Occurred
())
{
PyErr_Clear
();
...
...
include/pybind11/pybind11.h
View file @
18e34cb2
...
...
@@ -122,7 +122,7 @@ protected:
cast_in
args_converter
;
/* Try to cast the function arguments into the C++ domain */
if
(
!
args_converter
.
load_args
(
call
.
args
))
if
(
!
args_converter
.
load_args
(
call
))
return
PYBIND11_TRY_NEXT_OVERLOAD
;
/* Invoke call policy pre-call hook */
...
...
@@ -198,7 +198,7 @@ protected:
if
(
c
==
'{'
)
{
// Write arg name for everything except *args, **kwargs and return type.
if
(
type_depth
==
0
&&
text
[
char_index
]
!=
'*'
&&
arg_index
<
args
)
{
if
(
!
rec
->
args
.
empty
())
{
if
(
!
rec
->
args
.
empty
()
&&
rec
->
args
[
arg_index
].
name
)
{
signature
+=
rec
->
args
[
arg_index
].
name
;
}
else
if
(
arg_index
==
0
&&
rec
->
is_method
)
{
signature
+=
"self"
;
...
...
@@ -257,7 +257,7 @@ protected:
rec
->
signature
=
strdup
(
signature
.
c_str
());
rec
->
args
.
shrink_to_fit
();
rec
->
is_constructor
=
!
strcmp
(
rec
->
name
,
"__init__"
)
||
!
strcmp
(
rec
->
name
,
"__setstate__"
);
rec
->
nargs
=
(
uint16_t
)
args
;
rec
->
nargs
=
(
std
::
uint16_t
)
args
;
#if PY_MAJOR_VERSION < 3
if
(
rec
->
sibling
&&
PyMethod_Check
(
rec
->
sibling
.
ptr
()))
...
...
@@ -392,8 +392,10 @@ protected:
handle
parent
=
n_args_in
>
0
?
PyTuple_GET_ITEM
(
args_in
,
0
)
:
nullptr
,
result
=
PYBIND11_TRY_NEXT_OVERLOAD
;
try
{
for
(;
it
!=
nullptr
;
it
=
it
->
next
)
{
/* For each overload:
1. Copy all positional arguments we were given, also checking to make sure that
named positional arguments weren't *also* specified via kwarg.
...
...
@@ -435,14 +437,15 @@ protected:
// raise a TypeError like Python does. (We could also continue with the next
// overload, but this seems highly likely to be a caller mistake rather than a
// legitimate overload).
if
(
kwargs_in
&&
args_copied
<
it
->
args
.
size
())
{
handle
value
=
PyDict_GetItemString
(
kwargs_in
,
it
->
args
[
args_copied
].
name
);
if
(
kwargs_in
&&
args_copied
<
func
.
args
.
size
()
&&
func
.
args
[
args_copied
].
name
)
{
handle
value
=
PyDict_GetItemString
(
kwargs_in
,
func
.
args
[
args_copied
].
name
);
if
(
value
)
throw
type_error
(
std
::
string
(
it
->
name
)
+
"(): got multiple values for argument '"
+
std
::
string
(
it
->
args
[
args_copied
].
name
)
+
"'"
);
throw
type_error
(
std
::
string
(
func
.
name
)
+
"(): got multiple values for argument '"
+
std
::
string
(
func
.
args
[
args_copied
].
name
)
+
"'"
);
}
call
.
args
.
push_back
(
PyTuple_GET_ITEM
(
args_in
,
args_copied
));
call
.
args_convert
.
push_back
(
args_copied
<
func
.
args
.
size
()
?
func
.
args
[
args_copied
].
convert
:
true
);
}
// We'll need to copy this if we steal some kwargs for defaults
...
...
@@ -453,10 +456,10 @@ protected:
bool
copied_kwargs
=
false
;
for
(;
args_copied
<
pos_args
;
++
args_copied
)
{
const
auto
&
arg
=
it
->
args
[
args_copied
];
const
auto
&
arg
=
func
.
args
[
args_copied
];
handle
value
;
if
(
kwargs_in
)
if
(
kwargs_in
&&
arg
.
name
)
value
=
PyDict_GetItemString
(
kwargs
.
ptr
(),
arg
.
name
);
if
(
value
)
{
...
...
@@ -470,8 +473,10 @@ protected:
value
=
arg
.
value
;
}
if
(
value
)
if
(
value
)
{
call
.
args
.
push_back
(
value
);
call
.
args_convert
.
push_back
(
arg
.
convert
);
}
else
break
;
}
...
...
@@ -481,12 +486,12 @@ protected:
}
// 3. Check everything was consumed (unless we have a kwargs arg)
if
(
kwargs
&&
kwargs
.
size
()
>
0
&&
!
it
->
has_kwargs
)
if
(
kwargs
&&
kwargs
.
size
()
>
0
&&
!
func
.
has_kwargs
)
continue
;
// Unconsumed kwargs, but no py::kwargs argument to accept them
// 4a. If we have a py::args argument, create a new tuple with leftovers
tuple
extra_args
;
if
(
it
->
has_args
)
{
if
(
func
.
has_args
)
{
if
(
args_to_copy
==
0
)
{
// We didn't copy out any position arguments from the args_in tuple, so we
// can reuse it directly without copying:
...
...
@@ -502,31 +507,34 @@ protected:
}
}
call
.
args
.
push_back
(
extra_args
);
call
.
args_convert
.
push_back
(
false
);
}
// 4b. If we have a py::kwargs, pass on any remaining kwargs
if
(
it
->
has_kwargs
)
{
if
(
func
.
has_kwargs
)
{
if
(
!
kwargs
.
ptr
())
kwargs
=
dict
();
// If we didn't get one, send an empty one
call
.
args
.
push_back
(
kwargs
);
call
.
args_convert
.
push_back
(
false
);
}
// 5. Put everything in a vector. Not technically step 5, we've been building it
// in `call.args` all along.
#if !defined(NDEBUG)
if
(
call
.
args
.
size
()
!=
call
.
func
.
nargs
)
if
(
call
.
args
.
size
()
!=
func
.
nargs
||
call
.
args_convert
.
size
()
!=
func
.
nargs
)
pybind11_fail
(
"Internal error: function call dispatcher inserted wrong number of arguments!"
);
#endif
// 6. Call the function.
try
{
result
=
it
->
impl
(
call
);
result
=
func
.
impl
(
call
);
}
catch
(
reference_cast_error
&
)
{
result
=
PYBIND11_TRY_NEXT_OVERLOAD
;
}
if
(
result
.
ptr
()
!=
PYBIND11_TRY_NEXT_OVERLOAD
)
break
;
}
}
catch
(
error_already_set
&
e
)
{
e
.
restore
();
...
...
tests/test_methods_and_attributes.cpp
View file @
18e34cb2
...
...
@@ -97,6 +97,42 @@ public:
class
CppDerivedDynamicClass
:
public
DynamicClass
{
};
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
class
ArgInspector1
{
public
:
std
::
string
arg
=
"(default arg inspector 1)"
;
};
class
ArgInspector2
{
public
:
std
::
string
arg
=
"(default arg inspector 2)"
;
};
namespace
pybind11
{
namespace
detail
{
template
<
>
struct
type_caster
<
ArgInspector1
>
{
public:
PYBIND11_TYPE_CASTER
(
ArgInspector1
,
_
(
"ArgInspector1"
));
bool
load
(
handle
src
,
bool
convert
)
{
value
.
arg
=
"loading ArgInspector1 argument "
+
std
::
string
(
convert
?
"WITH"
:
"WITHOUT"
)
+
" conversion allowed. "
"Argument value = "
+
(
std
::
string
)
str
(
src
);
return
true
;
}
static
handle
cast
(
const
ArgInspector1
&
src
,
return_value_policy
,
handle
)
{
return
str
(
src
.
arg
).
release
();
}
};
template
<
>
struct
type_caster
<
ArgInspector2
>
{
public:
PYBIND11_TYPE_CASTER
(
ArgInspector2
,
_
(
"ArgInspector2"
));
bool
load
(
handle
src
,
bool
convert
)
{
value
.
arg
=
"loading ArgInspector2 argument "
+
std
::
string
(
convert
?
"WITH"
:
"WITHOUT"
)
+
" conversion allowed. "
"Argument value = "
+
(
std
::
string
)
str
(
src
);
return
true
;
}
static
handle
cast
(
const
ArgInspector2
&
src
,
return_value_policy
,
handle
)
{
return
str
(
src
.
arg
).
release
();
}
};
}}
test_initializer
methods_and_attributes
([](
py
::
module
&
m
)
{
py
::
class_
<
ExampleMandA
>
(
m
,
"ExampleMandA"
)
.
def
(
py
::
init
<>
())
...
...
@@ -183,4 +219,25 @@ test_initializer methods_and_attributes([](py::module &m) {
py
::
class_
<
CppDerivedDynamicClass
,
DynamicClass
>
(
m
,
"CppDerivedDynamicClass"
)
.
def
(
py
::
init
());
#endif
class
ArgInspector
{
public:
ArgInspector1
f
(
ArgInspector1
a
)
{
return
a
;
}
std
::
string
g
(
ArgInspector1
a
,
const
ArgInspector1
&
b
,
int
c
,
ArgInspector2
*
d
)
{
return
a
.
arg
+
"
\n
"
+
b
.
arg
+
"
\n
"
+
std
::
to_string
(
c
)
+
"
\n
"
+
d
->
arg
;
}
static
ArgInspector2
h
(
ArgInspector2
a
)
{
return
a
;
}
};
py
::
class_
<
ArgInspector
>
(
m
,
"ArgInspector"
)
.
def
(
py
::
init
<>
())
.
def
(
"f"
,
&
ArgInspector
::
f
)
.
def
(
"g"
,
&
ArgInspector
::
g
,
"a"
_a
.
noconvert
(),
"b"
_a
,
"c"
_a
.
noconvert
()
=
13
,
"d"
_a
=
ArgInspector2
())
.
def_static
(
"h"
,
&
ArgInspector
::
h
,
py
::
arg
().
noconvert
())
;
m
.
def
(
"arg_inspect_func"
,
[](
ArgInspector2
a
,
ArgInspector1
b
)
{
return
a
.
arg
+
"
\n
"
+
b
.
arg
;
},
py
::
arg
().
noconvert
(
false
),
py
::
arg_v
(
nullptr
,
ArgInspector1
()).
noconvert
(
true
));
m
.
def
(
"floats_preferred"
,
[](
double
f
)
{
return
0.5
*
f
;
},
py
::
arg
(
"f"
));
m
.
def
(
"floats_only"
,
[](
double
f
)
{
return
0.5
*
f
;
},
py
::
arg
(
"f"
).
noconvert
());
});
tests/test_methods_and_attributes.py
View file @
18e34cb2
...
...
@@ -203,3 +203,47 @@ def test_cyclic_gc():
assert
cstats
.
alive
()
==
2
del
i1
,
i2
assert
cstats
.
alive
()
==
0
def
test_noconvert_args
(
msg
):
from
pybind11_tests
import
ArgInspector
,
arg_inspect_func
,
floats_only
,
floats_preferred
a
=
ArgInspector
()
assert
msg
(
a
.
f
(
"hi"
))
==
"""
loading ArgInspector1 argument WITH conversion allowed. Argument value = hi
"""
assert
msg
(
a
.
g
(
"this is a"
,
"this is b"
))
==
"""
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
13
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
"""
# noqa: E501 line too long
assert
msg
(
a
.
g
(
"this is a"
,
"this is b"
,
42
))
==
"""
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
42
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
"""
# noqa: E501 line too long
assert
msg
(
a
.
g
(
"this is a"
,
"this is b"
,
42
,
"this is d"
))
==
"""
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = this is a
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
42
loading ArgInspector2 argument WITH conversion allowed. Argument value = this is d
"""
assert
(
a
.
h
(
"arg 1"
)
==
"loading ArgInspector2 argument WITHOUT conversion allowed. Argument value = arg 1"
)
assert
msg
(
arg_inspect_func
(
"A1"
,
"A2"
))
==
"""
loading ArgInspector2 argument WITH conversion allowed. Argument value = A1
loading ArgInspector1 argument WITHOUT conversion allowed. Argument value = A2
"""
assert
floats_preferred
(
4
)
==
2.0
assert
floats_only
(
4.0
)
==
2.0
with
pytest
.
raises
(
TypeError
)
as
excinfo
:
floats_only
(
4
)
assert
msg
(
excinfo
.
value
)
==
"""
floats_only(): incompatible function arguments. The following argument types are supported:
1. (f: float) -> float
Invoked with: 4
"""
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment