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
1f2e417d
Commit
1f2e417d
authored
Sep 10, 2016
by
Wenzel Jakob
Committed by
GitHub
Sep 10, 2016
Browse files
Merge pull request #403 from jagerman/alias-initialization
Implement py::init_alias<>() constructors
parents
3d1bb29e
ec62d977
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
190 additions
and
56 deletions
+190
-56
docs/advanced.rst
docs/advanced.rst
+30
-0
include/pybind11/pybind11.h
include/pybind11/pybind11.h
+17
-8
tests/CMakeLists.txt
tests/CMakeLists.txt
+1
-0
tests/test_alias_initialization.cpp
tests/test_alias_initialization.cpp
+62
-0
tests/test_alias_initialization.py
tests/test_alias_initialization.py
+80
-0
tests/test_issues.cpp
tests/test_issues.cpp
+0
-24
tests/test_issues.py
tests/test_issues.py
+0
-24
No files found.
docs/advanced.rst
View file @
1f2e417d
...
...
@@ -480,6 +480,36 @@ can now create a python class that inherits from ``Dog``:
See the file :file:`tests/test_virtual_functions.cpp` for complete examples
using both the duplication and templated trampoline approaches.
Extended trampoline class functionality
=======================================
The trampoline classes described in the previous sections are, by default, only
initialized when needed. More specifically, they are initialized when a python
class actually inherits from a registered type (instead of merely creating an
instance of the registered type), or when a registered constructor is only
valid for the trampoline class but not the registered class. This is primarily
for performance reasons: when the trampoline class is not needed for anything
except virtual method dispatching, not initializing the trampoline class
improves performance by avoiding needing to do a run-time check to see if the
inheriting python instance has an overloaded method.
Sometimes, however, it is useful to always initialize a trampoline class as an
intermediate class that does more than just handle virtual method dispatching.
For example, such a class might perform extra class initialization, extra
destruction operations, and might define new members and methods to enable a
more python-like interface to a class.
In order to tell pybind11 that it should *always* initialize the trampoline
class when creating new instances of a type, the class constructors should be
declared using ``py::init_alias<Args, ...>()`` instead of the usual
``py::init<Args, ...>()``. This forces construction via the trampoline class,
ensuring member initialization and (eventual) destruction.
.. seealso::
See the file :file:`tests/test_alias_initialization.cpp` for complete examples
showing both normal and forced trampoline instantiation.
.. _macro_notes:
General notes regarding convenience macros
...
...
include/pybind11/pybind11.h
View file @
1f2e417d
...
...
@@ -1112,17 +1112,17 @@ private:
NAMESPACE_BEGIN
(
detail
)
template
<
typename
...
Args
>
struct
init
{
template
<
typename
Class
,
typename
...
Extra
,
typename
std
::
enable_if
<!
Class
::
has_alias
,
int
>
::
type
=
0
>
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
const
{
template
<
typename
Class
,
typename
...
Extra
,
enable_if
_t
<!
Class
::
has_alias
,
int
>
=
0
>
static
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
{
using
Base
=
typename
Class
::
type
;
/// Function which calls a specific C++ in-place constructor
cl
.
def
(
"__init__"
,
[](
Base
*
self_
,
Args
...
args
)
{
new
(
self_
)
Base
(
args
...);
},
extra
...);
}
template
<
typename
Class
,
typename
...
Extra
,
typename
std
::
enable_if
<
Class
::
has_alias
&&
std
::
is_constructible
<
typename
Class
::
type
,
Args
...>
::
value
,
int
>
::
type
=
0
>
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
const
{
enable_if
_t
<
Class
::
has_alias
&&
std
::
is_constructible
<
typename
Class
::
type
,
Args
...>
::
value
,
int
>
=
0
>
static
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
{
using
Base
=
typename
Class
::
type
;
using
Alias
=
typename
Class
::
type_alias
;
handle
cl_type
=
cl
;
...
...
@@ -1135,14 +1135,22 @@ template <typename... Args> struct init {
}
template
<
typename
Class
,
typename
...
Extra
,
typename
std
::
enable_if
<
Class
::
has_alias
&&
!
std
::
is_constructible
<
typename
Class
::
type
,
Args
...>
::
value
,
int
>::
type
=
0
>
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
const
{
enable_if_t
<
Class
::
has_alias
&&
!
std
::
is_constructible
<
typename
Class
::
type
,
Args
...>
::
value
,
int
>
=
0
>
static
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
{
init_alias
<
Args
...
>::
execute
(
cl
,
extra
...);
}
};
template
<
typename
...
Args
>
struct
init_alias
{
template
<
typename
Class
,
typename
...
Extra
,
enable_if_t
<
Class
::
has_alias
&&
std
::
is_constructible
<
typename
Class
::
type_alias
,
Args
...>
::
value
,
int
>
=
0
>
static
void
execute
(
Class
&
cl
,
const
Extra
&
...
extra
)
{
using
Alias
=
typename
Class
::
type_alias
;
cl
.
def
(
"__init__"
,
[](
Alias
*
self_
,
Args
...
args
)
{
new
(
self_
)
Alias
(
args
...);
},
extra
...);
}
};
inline
void
keep_alive_impl
(
handle
nurse
,
handle
patient
)
{
/* Clever approach based on weak references taken from Boost.Python */
if
(
!
nurse
||
!
patient
)
...
...
@@ -1177,6 +1185,7 @@ struct iterator_state {
NAMESPACE_END
(
detail
)
template
<
typename
...
Args
>
detail
::
init
<
Args
...
>
init
()
{
return
detail
::
init
<
Args
...
>
();
}
template
<
typename
...
Args
>
detail
::
init_alias
<
Args
...
>
init_alias
()
{
return
detail
::
init_alias
<
Args
...
>
();
}
template
<
typename
Iterator
,
typename
Sentinel
,
...
...
tests/CMakeLists.txt
View file @
1f2e417d
...
...
@@ -4,6 +4,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
endif
()
set
(
PYBIND11_TEST_FILES
test_alias_initialization.cpp
test_buffers.cpp
test_callbacks.cpp
test_class_args.cpp
...
...
tests/test_alias_initialization.cpp
0 → 100644
View file @
1f2e417d
/*
tests/test_alias_initialization.cpp -- test cases and example of different trampoline
initialization modes
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, Jason Rhinelander <jason@imaginary.ca>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
test_initializer
alias_initialization
([](
py
::
module
&
m
)
{
// don't invoke Python dispatch classes by default when instantiating C++ classes that were not
// extended on the Python side
struct
A
{
virtual
~
A
()
{}
virtual
void
f
()
{
py
::
print
(
"A.f()"
);
}
};
struct
PyA
:
A
{
PyA
()
{
py
::
print
(
"PyA.PyA()"
);
}
~
PyA
()
{
py
::
print
(
"PyA.~PyA()"
);
}
void
f
()
override
{
py
::
print
(
"PyA.f()"
);
PYBIND11_OVERLOAD
(
void
,
A
,
f
);
}
};
auto
call_f
=
[](
A
*
a
)
{
a
->
f
();
};
py
::
class_
<
A
,
PyA
>
(
m
,
"A"
)
.
def
(
py
::
init
<>
())
.
def
(
"f"
,
&
A
::
f
);
m
.
def
(
"call_f"
,
call_f
);
// ... unless we explicitly request it, as in this example:
struct
A2
{
virtual
~
A2
()
{}
virtual
void
f
()
{
py
::
print
(
"A2.f()"
);
}
};
struct
PyA2
:
A2
{
PyA2
()
{
py
::
print
(
"PyA2.PyA2()"
);
}
~
PyA2
()
{
py
::
print
(
"PyA2.~PyA2()"
);
}
void
f
()
override
{
py
::
print
(
"PyA2.f()"
);
PYBIND11_OVERLOAD
(
void
,
A2
,
f
);
}
};
py
::
class_
<
A2
,
PyA2
>
(
m
,
"A2"
)
.
def
(
py
::
init_alias
<>
())
.
def
(
"f"
,
&
A2
::
f
);
m
.
def
(
"call_f"
,
[](
A2
*
a2
)
{
a2
->
f
();
});
});
tests/test_alias_initialization.py
0 → 100644
View file @
1f2e417d
import
pytest
import
gc
def
test_alias_delay_initialization
(
capture
,
msg
):
# A only initializes its trampoline class when we inherit from it; if we
# just create and use an A instance directly, the trampoline initialization
# is bypassed and we only initialize an A() instead (for performance
# reasons)
from
pybind11_tests
import
A
,
call_f
class
B
(
A
):
def
__init__
(
self
):
super
(
B
,
self
).
__init__
()
def
f
(
self
):
print
(
"In python f()"
)
# C++ version
with
capture
:
a
=
A
()
call_f
(
a
)
del
a
gc
.
collect
()
assert
capture
==
"A.f()"
# Python version
with
capture
:
b
=
B
()
call_f
(
b
)
del
b
gc
.
collect
()
assert
capture
==
"""
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def
test_alias_delay_initialization
(
capture
,
msg
):
from
pybind11_tests
import
A2
,
call_f
# A2, unlike the above, is configured to always initialize the alias; while
# the extra initialization and extra class layer has small virtual dispatch
# performance penalty, it also allows us to do more things with the
# trampoline class such as defining local variables and performing
# construction/destruction.
class
B2
(
A2
):
def
__init__
(
self
):
super
(
B2
,
self
).
__init__
()
def
f
(
self
):
print
(
"In python B2.f()"
)
# No python subclass version
with
capture
:
a2
=
A2
()
call_f
(
a2
)
del
a2
gc
.
collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with
capture
:
b2
=
B2
()
call_f
(
b2
)
del
b2
gc
.
collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""
tests/test_issues.cpp
View file @
1f2e417d
...
...
@@ -134,30 +134,6 @@ void init_issues(py::module &m) {
m2
.
def
(
"expect_float"
,
[](
float
f
)
{
return
f
;
});
m2
.
def
(
"expect_int"
,
[](
int
i
)
{
return
i
;
});
// (no id): don't invoke Python dispatch code when instantiating C++
// classes that were not extended on the Python side
struct
A
{
virtual
~
A
()
{}
virtual
void
f
()
{
py
::
print
(
"A.f()"
);
}
};
struct
PyA
:
A
{
PyA
()
{
py
::
print
(
"PyA.PyA()"
);
}
void
f
()
override
{
py
::
print
(
"PyA.f()"
);
PYBIND11_OVERLOAD
(
void
,
A
,
f
);
}
};
auto
call_f
=
[](
A
*
a
)
{
a
->
f
();
};
pybind11
::
class_
<
A
,
std
::
unique_ptr
<
A
>
,
PyA
>
(
m2
,
"A"
)
.
def
(
py
::
init
<>
())
.
def
(
"f"
,
&
A
::
f
);
m2
.
def
(
"call_f"
,
call_f
);
try
{
py
::
class_
<
Placeholder
>
(
m2
,
"Placeholder"
);
throw
std
::
logic_error
(
"Expected an exception!"
);
...
...
tests/test_issues.py
View file @
1f2e417d
...
...
@@ -79,30 +79,6 @@ def test_no_id(capture, msg):
"""
assert
expect_float
(
12
)
==
12
from
pybind11_tests.issues
import
A
,
call_f
class
B
(
A
):
def
__init__
(
self
):
super
(
B
,
self
).
__init__
()
def
f
(
self
):
print
(
"In python f()"
)
# C++ version
with
capture
:
a
=
A
()
call_f
(
a
)
assert
capture
==
"A.f()"
# Python version
with
capture
:
b
=
B
()
call_f
(
b
)
assert
capture
==
"""
PyA.PyA()
PyA.f()
In python f()
"""
def
test_str_issue
(
msg
):
...
...
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