Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pybind11
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
pybind11
Commits
18e34cb2
Commit
18e34cb2
authored
Feb 05, 2017
by
Wenzel Jakob
Committed by
GitHub
Feb 05, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #634 from jagerman/noconvert-arguments
Add support for non-converting arguments
parents
40db2c75
abc29cad
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
276 additions
and
61 deletions
+276
-61
docs/advanced/classes.rst
+2
-0
docs/advanced/functions.rst
+54
-0
include/pybind11/attr.h
+13
-23
include/pybind11/cast.h
+80
-22
include/pybind11/complex.h
+3
-1
include/pybind11/pybind11.h
+23
-15
tests/test_methods_and_attributes.cpp
+57
-0
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
(
nam
e
)
{
}
///
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
(
fals
e
)
{
}
/// 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