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
bb1ee389
Commit
bb1ee389
authored
Aug 09, 2016
by
Wenzel Jakob
Committed by
GitHub
Aug 09, 2016
Browse files
Merge pull request #297 from jagerman/move-python-return-value
Move support for return values of called Python functions
parents
6697f80f
ed14879a
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
196 additions
and
1 deletion
+196
-1
example/example-virtual-functions.cpp
example/example-virtual-functions.cpp
+61
-0
example/example-virtual-functions.py
example/example-virtual-functions.py
+35
-0
example/example-virtual-functions.ref
example/example-virtual-functions.ref
+16
-0
include/pybind11/cast.h
include/pybind11/cast.h
+79
-1
include/pybind11/pytypes.h
include/pybind11/pytypes.h
+5
-0
No files found.
example/example-virtual-functions.cpp
View file @
bb1ee389
...
...
@@ -69,6 +69,53 @@ public:
}
};
class
NonCopyable
{
public:
NonCopyable
(
int
a
,
int
b
)
:
value
{
new
int
(
a
*
b
)}
{}
NonCopyable
(
NonCopyable
&&
)
=
default
;
NonCopyable
(
const
NonCopyable
&
)
=
delete
;
NonCopyable
()
=
delete
;
void
operator
=
(
const
NonCopyable
&
)
=
delete
;
void
operator
=
(
NonCopyable
&&
)
=
delete
;
std
::
string
get_value
()
const
{
if
(
value
)
return
std
::
to_string
(
*
value
);
else
return
"(null)"
;
}
~
NonCopyable
()
{
std
::
cout
<<
"NonCopyable destructor @ "
<<
this
<<
"; value = "
<<
get_value
()
<<
std
::
endl
;
}
private:
std
::
unique_ptr
<
int
>
value
;
};
// This is like the above, but is both copy and movable. In effect this means it should get moved
// when it is not referenced elsewhere, but copied if it is still referenced.
class
Movable
{
public:
Movable
(
int
a
,
int
b
)
:
value
{
a
+
b
}
{}
Movable
(
const
Movable
&
m
)
{
value
=
m
.
value
;
std
::
cout
<<
"Movable @ "
<<
this
<<
" copy constructor"
<<
std
::
endl
;
}
Movable
(
Movable
&&
m
)
{
value
=
std
::
move
(
m
.
value
);
std
::
cout
<<
"Movable @ "
<<
this
<<
" move constructor"
<<
std
::
endl
;
}
int
get_value
()
const
{
return
value
;
}
~
Movable
()
{
std
::
cout
<<
"Movable destructor @ "
<<
this
<<
"; value = "
<<
get_value
()
<<
std
::
endl
;
}
private:
int
value
;
};
class
NCVirt
{
public:
virtual
NonCopyable
get_noncopyable
(
int
a
,
int
b
)
{
return
NonCopyable
(
a
,
b
);
}
virtual
Movable
get_movable
(
int
a
,
int
b
)
=
0
;
void
print_nc
(
int
a
,
int
b
)
{
std
::
cout
<<
get_noncopyable
(
a
,
b
).
get_value
()
<<
std
::
endl
;
}
void
print_movable
(
int
a
,
int
b
)
{
std
::
cout
<<
get_movable
(
a
,
b
).
get_value
()
<<
std
::
endl
;
}
};
class
NCVirtTrampoline
:
public
NCVirt
{
virtual
NonCopyable
get_noncopyable
(
int
a
,
int
b
)
{
PYBIND11_OVERLOAD
(
NonCopyable
,
NCVirt
,
get_noncopyable
,
a
,
b
);
}
virtual
Movable
get_movable
(
int
a
,
int
b
)
{
PYBIND11_OVERLOAD_PURE
(
Movable
,
NCVirt
,
get_movable
,
a
,
b
);
}
};
int
runExampleVirt
(
ExampleVirt
*
ex
,
int
value
)
{
return
ex
->
run
(
value
);
}
...
...
@@ -240,6 +287,20 @@ void init_ex_virtual_functions(py::module &m) {
.
def
(
"run_bool"
,
&
ExampleVirt
::
run_bool
)
.
def
(
"pure_virtual"
,
&
ExampleVirt
::
pure_virtual
);
py
::
class_
<
NonCopyable
>
(
m
,
"NonCopyable"
)
.
def
(
py
::
init
<
int
,
int
>
())
;
py
::
class_
<
Movable
>
(
m
,
"Movable"
)
.
def
(
py
::
init
<
int
,
int
>
())
;
py
::
class_
<
NCVirt
,
std
::
unique_ptr
<
NCVirt
>
,
NCVirtTrampoline
>
(
m
,
"NCVirt"
)
.
def
(
py
::
init
<>
())
.
def
(
"get_noncopyable"
,
&
NCVirt
::
get_noncopyable
)
.
def
(
"get_movable"
,
&
NCVirt
::
get_movable
)
.
def
(
"print_nc"
,
&
NCVirt
::
print_nc
)
.
def
(
"print_movable"
,
&
NCVirt
::
print_movable
)
;
m
.
def
(
"runExampleVirt"
,
&
runExampleVirt
);
m
.
def
(
"runExampleVirtBool"
,
&
runExampleVirtBool
);
m
.
def
(
"runExampleVirtVirtual"
,
&
runExampleVirtVirtual
);
...
...
example/example-virtual-functions.py
View file @
bb1ee389
...
...
@@ -5,6 +5,8 @@ sys.path.append('.')
from
example
import
ExampleVirt
,
runExampleVirt
,
runExampleVirtVirtual
,
runExampleVirtBool
from
example
import
A_Repeat
,
B_Repeat
,
C_Repeat
,
D_Repeat
,
A_Tpl
,
B_Tpl
,
C_Tpl
,
D_Tpl
from
example
import
NCVirt
,
NonCopyable
,
Movable
class
ExtendedExampleVirt
(
ExampleVirt
):
def
__init__
(
self
,
state
):
...
...
@@ -87,3 +89,36 @@ for cl in classes:
if
hasattr
(
obj
,
"lucky_number"
):
print
(
"Lucky = %.2f"
%
obj
.
lucky_number
())
class
NCVirtExt
(
NCVirt
):
def
get_noncopyable
(
self
,
a
,
b
):
# Constructs and returns a new instance:
nc
=
NonCopyable
(
a
*
a
,
b
*
b
)
return
nc
def
get_movable
(
self
,
a
,
b
):
# Return a referenced copy
self
.
movable
=
Movable
(
a
,
b
)
return
self
.
movable
class
NCVirtExt2
(
NCVirt
):
def
get_noncopyable
(
self
,
a
,
b
):
# Keep a reference: this is going to throw an exception
self
.
nc
=
NonCopyable
(
a
,
b
)
return
self
.
nc
def
get_movable
(
self
,
a
,
b
):
# Return a new instance without storing it
return
Movable
(
a
,
b
)
ncv1
=
NCVirtExt
()
print
(
"2^2 * 3^2 ="
)
ncv1
.
print_nc
(
2
,
3
)
print
(
"4 + 5 ="
)
ncv1
.
print_movable
(
4
,
5
)
ncv2
=
NCVirtExt2
()
print
(
"7 + 7 ="
)
ncv2
.
print_movable
(
7
,
7
)
try
:
ncv2
.
print_nc
(
9
,
9
)
print
(
"Something's wrong: exception not raised!"
)
except
RuntimeError
as
e
:
# Don't print the exception message here because it differs under debug/non-debug mode
print
(
"Caught expected exception"
)
example/example-virtual-functions.ref
View file @
bb1ee389
...
...
@@ -77,5 +77,21 @@ VI_DT:
VI_DT says: quack quack quack
Unlucky = 1234
Lucky = -4.25
2^2 * 3^2 =
NonCopyable destructor @ 0x1a6c3f0; value = (null)
36
NonCopyable destructor @ 0x7ffc6d1fbaa8; value = 36
4 + 5 =
Movable @ 0x7ffc6d1fbacc copy constructor
9
Movable destructor @ 0x7ffc6d1fbacc; value = 9
7 + 7 =
Movable @ 0x7ffc6d1fbacc move constructor
Movable destructor @ 0x1a6c4d0; value = 14
14
Movable destructor @ 0x7ffc6d1fbacc; value = 14
Caught expected exception
NonCopyable destructor @ 0x29a64b0; value = 81
Movable destructor @ 0x1a6c410; value = 9
Destructing ExampleVirt..
Destructing ExampleVirt..
include/pybind11/cast.h
View file @
bb1ee389
...
...
@@ -809,9 +809,36 @@ public:
PYBIND11_TYPE_CASTER
(
type
,
handle_type_name
<
type
>::
name
());
};
// Our conditions for enabling moving are quite restrictive:
// At compile time:
// - T needs to be a non-const, non-pointer, non-reference type
// - type_caster<T>::operator T&() must exist
// - the type must be move constructible (obviously)
// At run-time:
// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it
// must have ref_count() == 1)h
// If any of the above are not satisfied, we fall back to copying.
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
move_is_plain_type
:
std
::
false_type
{};
template
<
typename
T
>
struct
move_is_plain_type
<
T
,
typename
std
::
enable_if
<
!
std
::
is_void
<
T
>::
value
&&
!
std
::
is_pointer
<
T
>::
value
&&
!
std
::
is_reference
<
T
>::
value
&&
!
std
::
is_const
<
T
>::
value
>::
type
>
:
std
::
true_type
{};
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
move_always
:
std
::
false_type
{};
template
<
typename
T
>
struct
move_always
<
T
,
typename
std
::
enable_if
<
move_is_plain_type
<
T
>::
value
&&
!
std
::
is_copy_constructible
<
T
>::
value
&&
std
::
is_move_constructible
<
T
>::
value
&&
std
::
is_same
<
decltype
(
std
::
declval
<
type_caster
<
T
>>
().
operator
T
&
()),
T
&>::
value
>::
type
>
:
std
::
true_type
{};
template
<
typename
T
,
typename
SFINAE
=
void
>
struct
move_if_unreferenced
:
std
::
false_type
{};
template
<
typename
T
>
struct
move_if_unreferenced
<
T
,
typename
std
::
enable_if
<
move_is_plain_type
<
T
>::
value
&&
!
move_always
<
T
>::
value
&&
std
::
is_move_constructible
<
T
>::
value
&&
std
::
is_same
<
decltype
(
std
::
declval
<
type_caster
<
T
>>
().
operator
T
&
()),
T
&>::
value
>::
type
>
:
std
::
true_type
{};
template
<
typename
T
>
using
move_never
=
std
::
integral_constant
<
bool
,
!
move_always
<
T
>::
value
&&
!
move_if_unreferenced
<
T
>::
value
>
;
NAMESPACE_END
(
detail
)
template
<
typename
T
>
T
cast
(
handle
handle
)
{
template
<
typename
T
>
T
cast
(
const
handle
&
handle
)
{
typedef
detail
::
type_caster
<
typename
detail
::
intrinsic_type
<
T
>::
type
>
type_caster
;
type_caster
conv
;
if
(
!
conv
.
load
(
handle
,
true
))
{
...
...
@@ -838,6 +865,57 @@ template <typename T> object cast(const T &value,
template
<
typename
T
>
T
handle
::
cast
()
const
{
return
pybind11
::
cast
<
T
>
(
*
this
);
}
template
<
>
inline
void
handle
::
cast
()
const
{
return
;
}
template
<
typename
T
>
typename
std
::
enable_if
<
detail
::
move_always
<
T
>::
value
||
detail
::
move_if_unreferenced
<
T
>::
value
,
T
>::
type
move
(
object
&&
obj
)
{
if
(
obj
.
ref_count
()
>
1
)
#if defined(NDEBUG)
throw
cast_error
(
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
" (compile in debug mode for details)"
);
#else
throw
cast_error
(
"Unable to move from Python "
+
(
std
::
string
)
obj
.
get_type
().
str
()
+
" instance to C++ "
+
type_id
<
T
>
()
+
" instance: instance has multiple references"
);
#endif
typedef
detail
::
type_caster
<
T
>
type_caster
;
type_caster
conv
;
if
(
!
conv
.
load
(
obj
,
true
))
#if defined(NDEBUG)
throw
cast_error
(
"Unable to cast Python instance to C++ type (compile in debug mode for details)"
);
#else
throw
cast_error
(
"Unable to cast Python instance of type "
+
(
std
::
string
)
obj
.
get_type
().
str
()
+
" to C++ type '"
+
type_id
<
T
>
()
+
"''"
);
#endif
// Move into a temporary and return that, because the reference may be a local value of `conv`
T
ret
=
std
::
move
(
conv
.
operator
T
&
());
return
ret
;
}
// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does:
// - If we have to move (because T has no copy constructor), do it. This will fail if the moved
// object has multiple references, but trying to copy will fail to compile.
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
// - Otherwise (not movable), copy.
template
<
typename
T
>
typename
std
::
enable_if
<
detail
::
move_always
<
T
>::
value
,
T
>::
type
cast
(
object
&&
object
)
{
return
move
<
T
>
(
std
::
move
(
object
));
}
template
<
typename
T
>
typename
std
::
enable_if
<
detail
::
move_if_unreferenced
<
T
>::
value
,
T
>::
type
cast
(
object
&&
object
)
{
if
(
object
.
ref_count
()
>
1
)
return
cast
<
T
>
(
object
);
else
return
move
<
T
>
(
std
::
move
(
object
));
}
template
<
typename
T
>
typename
std
::
enable_if
<
detail
::
move_never
<
T
>::
value
,
T
>::
type
cast
(
object
&&
object
)
{
return
cast
<
T
>
(
object
);
}
template
<
typename
T
>
T
object
::
cast
()
const
&
{
return
pybind11
::
cast
<
T
>
(
*
this
);
}
template
<
typename
T
>
T
object
::
cast
()
&&
{
return
pybind11
::
cast
<
T
>
(
std
::
move
(
*
this
));
}
template
<
>
inline
void
object
::
cast
()
const
&
{
return
;
}
template
<
>
inline
void
object
::
cast
()
&&
{
return
;
}
template
<
return_value_policy
policy
=
return_value_policy
::
automatic_reference
,
typename
...
Args
>
tuple
make_tuple
(
Args
&&
...
args_
)
{
const
size_t
size
=
sizeof
...(
Args
);
...
...
include/pybind11/pytypes.h
View file @
bb1ee389
...
...
@@ -89,6 +89,11 @@ public:
}
return
*
this
;
}
// Calling cast() on an object lvalue just copies (via handle::cast)
template
<
typename
T
>
T
cast
()
const
&
;
// Calling on an object rvalue does a move, if needed and/or possible
template
<
typename
T
>
T
cast
()
&&
;
};
NAMESPACE_BEGIN
(
detail
)
...
...
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