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
410e364a
Commit
410e364a
authored
Nov 18, 2020
by
Ralf W. Grosse-Kunstleve
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
first quick experiment adding yclass
parent
7caf5821
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
358 additions
and
2 deletions
+358
-2
include/pybind11/yclass.h
+352
-0
tests/test_unique_ptr_member.cpp
+6
-2
No files found.
include/pybind11/yclass.h
0 → 100644
View file @
410e364a
#pragma once
#include "pybind11.h"
PYBIND11_NAMESPACE_BEGIN
(
PYBIND11_NAMESPACE
)
template
<
typename
type_
,
typename
...
options
>
class
yclass
:
public
detail
::
generic_type
{
template
<
typename
T
>
using
is_holder
=
detail
::
is_holder_type
<
type_
,
T
>
;
template
<
typename
T
>
using
is_subtype
=
detail
::
is_strict_base_of
<
type_
,
T
>
;
template
<
typename
T
>
using
is_base
=
detail
::
is_strict_base_of
<
T
,
type_
>
;
// struct instead of using here to help MSVC:
template
<
typename
T
>
struct
is_valid_class_option
:
detail
::
any_of
<
is_holder
<
T
>
,
is_subtype
<
T
>
,
is_base
<
T
>>
{};
public
:
using
type
=
type_
;
using
type_alias
=
detail
::
exactly_one_t
<
is_subtype
,
void
,
options
...
>
;
constexpr
static
bool
has_alias
=
!
std
::
is_void
<
type_alias
>::
value
;
using
holder_type
=
detail
::
exactly_one_t
<
is_holder
,
std
::
unique_ptr
<
type
>
,
options
...
>
;
static_assert
(
detail
::
all_of
<
is_valid_class_option
<
options
>
...
>::
value
,
"Unknown/invalid yclass template parameters provided"
);
static_assert
(
!
has_alias
||
std
::
is_polymorphic
<
type
>::
value
,
"Cannot use an alias class with a non-polymorphic type"
);
PYBIND11_OBJECT
(
yclass
,
generic_type
,
PyType_Check
)
template
<
typename
...
Extra
>
yclass
(
handle
scope
,
const
char
*
name
,
const
Extra
&
...
extra
)
{
using
namespace
detail
;
// MI can only be specified via yclass template options, not constructor parameters
static_assert
(
none_of
<
is_pyobject
<
Extra
>
...
>::
value
||
// no base class arguments, or:
(
constexpr_sum
(
is_pyobject
<
Extra
>::
value
...)
==
1
&&
// Exactly one base
constexpr_sum
(
is_base
<
options
>::
value
...)
==
0
&&
// no template option bases
none_of
<
std
::
is_same
<
multiple_inheritance
,
Extra
>
...
>::
value
),
// no multiple_inheritance attr
"Error: multiple inheritance bases must be specified via yclass template options"
);
type_record
record
;
record
.
scope
=
scope
;
record
.
name
=
name
;
record
.
type
=
&
typeid
(
type
);
record
.
type_size
=
sizeof
(
conditional_t
<
has_alias
,
type_alias
,
type
>
);
record
.
type_align
=
alignof
(
conditional_t
<
has_alias
,
type_alias
,
type
>&
);
record
.
holder_size
=
sizeof
(
holder_type
);
record
.
init_instance
=
init_instance
;
record
.
dealloc
=
dealloc
;
record
.
default_holder
=
detail
::
is_instantiation
<
std
::
unique_ptr
,
holder_type
>::
value
;
set_operator_new
<
type
>
(
&
record
);
/* Register base classes specified via template arguments to yclass, if any */
PYBIND11_EXPAND_SIDE_EFFECTS
(
add_base
<
options
>
(
record
));
/* Process optional arguments, if any */
process_attributes
<
Extra
...
>::
init
(
extra
...,
&
record
);
generic_type
::
initialize
(
record
);
if
(
has_alias
)
{
auto
&
instances
=
record
.
module_local
?
registered_local_types_cpp
()
:
get_internals
().
registered_types_cpp
;
instances
[
std
::
type_index
(
typeid
(
type_alias
))]
=
instances
[
std
::
type_index
(
typeid
(
type
))];
}
}
template
<
typename
Base
,
detail
::
enable_if_t
<
is_base
<
Base
>::
value
,
int
>
=
0
>
static
void
add_base
(
detail
::
type_record
&
rec
)
{
rec
.
add_base
(
typeid
(
Base
),
[](
void
*
src
)
->
void
*
{
return
static_cast
<
Base
*>
(
reinterpret_cast
<
type
*>
(
src
));
});
}
template
<
typename
Base
,
detail
::
enable_if_t
<!
is_base
<
Base
>::
value
,
int
>
=
0
>
static
void
add_base
(
detail
::
type_record
&
)
{
}
template
<
typename
Func
,
typename
...
Extra
>
yclass
&
def
(
const
char
*
name_
,
Func
&&
f
,
const
Extra
&
...
extra
)
{
cpp_function
cf
(
method_adaptor
<
type
>
(
std
::
forward
<
Func
>
(
f
)),
name
(
name_
),
is_method
(
*
this
),
sibling
(
getattr
(
*
this
,
name_
,
none
())),
extra
...);
add_class_method
(
*
this
,
name_
,
cf
);
return
*
this
;
}
template
<
typename
Func
,
typename
...
Extra
>
yclass
&
def_static
(
const
char
*
name_
,
Func
&&
f
,
const
Extra
&
...
extra
)
{
static_assert
(
!
std
::
is_member_function_pointer
<
Func
>::
value
,
"def_static(...) called with a non-static member function pointer"
);
cpp_function
cf
(
std
::
forward
<
Func
>
(
f
),
name
(
name_
),
scope
(
*
this
),
sibling
(
getattr
(
*
this
,
name_
,
none
())),
extra
...);
attr
(
cf
.
name
())
=
staticmethod
(
cf
);
return
*
this
;
}
template
<
detail
::
op_id
id
,
detail
::
op_type
ot
,
typename
L
,
typename
R
,
typename
...
Extra
>
yclass
&
def
(
const
detail
::
op_
<
id
,
ot
,
L
,
R
>
&
op
,
const
Extra
&
...
extra
)
{
op
.
execute
(
*
this
,
extra
...);
return
*
this
;
}
template
<
detail
::
op_id
id
,
detail
::
op_type
ot
,
typename
L
,
typename
R
,
typename
...
Extra
>
yclass
&
def_cast
(
const
detail
::
op_
<
id
,
ot
,
L
,
R
>
&
op
,
const
Extra
&
...
extra
)
{
op
.
execute_cast
(
*
this
,
extra
...);
return
*
this
;
}
template
<
typename
...
Args
,
typename
...
Extra
>
yclass
&
def
(
const
detail
::
initimpl
::
constructor
<
Args
...
>
&
init
,
const
Extra
&
...
extra
)
{
init
.
execute
(
*
this
,
extra
...);
return
*
this
;
}
template
<
typename
...
Args
,
typename
...
Extra
>
yclass
&
def
(
const
detail
::
initimpl
::
alias_constructor
<
Args
...
>
&
init
,
const
Extra
&
...
extra
)
{
init
.
execute
(
*
this
,
extra
...);
return
*
this
;
}
template
<
typename
...
Args
,
typename
...
Extra
>
yclass
&
def
(
detail
::
initimpl
::
factory
<
Args
...
>
&&
init
,
const
Extra
&
...
extra
)
{
std
::
move
(
init
).
execute
(
*
this
,
extra
...);
return
*
this
;
}
template
<
typename
...
Args
,
typename
...
Extra
>
yclass
&
def
(
detail
::
initimpl
::
pickle_factory
<
Args
...
>
&&
pf
,
const
Extra
&
...
extra
)
{
std
::
move
(
pf
).
execute
(
*
this
,
extra
...);
return
*
this
;
}
template
<
typename
Func
>
yclass
&
def_buffer
(
Func
&&
func
)
{
struct
capture
{
Func
func
;
};
auto
*
ptr
=
new
capture
{
std
::
forward
<
Func
>
(
func
)
};
install_buffer_funcs
([](
PyObject
*
obj
,
void
*
ptr
)
->
buffer_info
*
{
detail
::
make_caster
<
type
>
caster
;
if
(
!
caster
.
load
(
obj
,
false
))
return
nullptr
;
return
new
buffer_info
(((
capture
*
)
ptr
)
->
func
(
caster
));
},
ptr
);
weakref
(
m_ptr
,
cpp_function
([
ptr
](
handle
wr
)
{
delete
ptr
;
wr
.
dec_ref
();
})).
release
();
return
*
this
;
}
template
<
typename
Return
,
typename
Class
,
typename
...
Args
>
yclass
&
def_buffer
(
Return
(
Class
::*
func
)(
Args
...))
{
return
def_buffer
([
func
]
(
type
&
obj
)
{
return
(
obj
.
*
func
)();
});
}
template
<
typename
Return
,
typename
Class
,
typename
...
Args
>
yclass
&
def_buffer
(
Return
(
Class
::*
func
)(
Args
...)
const
)
{
return
def_buffer
([
func
]
(
const
type
&
obj
)
{
return
(
obj
.
*
func
)();
});
}
template
<
typename
C
,
typename
D
,
typename
...
Extra
>
yclass
&
def_readwrite
(
const
char
*
name
,
D
C
::*
pm
,
const
Extra
&
...
extra
)
{
static_assert
(
std
::
is_same
<
C
,
type
>::
value
||
std
::
is_base_of
<
C
,
type
>::
value
,
"def_readwrite() requires a class member (or base class member)"
);
cpp_function
fget
([
pm
](
const
type
&
c
)
->
const
D
&
{
return
c
.
*
pm
;
},
is_method
(
*
this
)),
fset
([
pm
](
type
&
c
,
const
D
&
value
)
{
c
.
*
pm
=
value
;
},
is_method
(
*
this
));
def_property
(
name
,
fget
,
fset
,
return_value_policy
::
reference_internal
,
extra
...);
return
*
this
;
}
template
<
typename
C
,
typename
D
,
typename
...
Extra
>
yclass
&
def_readonly
(
const
char
*
name
,
const
D
C
::*
pm
,
const
Extra
&
...
extra
)
{
static_assert
(
std
::
is_same
<
C
,
type
>::
value
||
std
::
is_base_of
<
C
,
type
>::
value
,
"def_readonly() requires a class member (or base class member)"
);
cpp_function
fget
([
pm
](
const
type
&
c
)
->
const
D
&
{
return
c
.
*
pm
;
},
is_method
(
*
this
));
def_property_readonly
(
name
,
fget
,
return_value_policy
::
reference_internal
,
extra
...);
return
*
this
;
}
template
<
typename
D
,
typename
...
Extra
>
yclass
&
def_readwrite_static
(
const
char
*
name
,
D
*
pm
,
const
Extra
&
...
extra
)
{
cpp_function
fget
([
pm
](
object
)
->
const
D
&
{
return
*
pm
;
},
scope
(
*
this
)),
fset
([
pm
](
object
,
const
D
&
value
)
{
*
pm
=
value
;
},
scope
(
*
this
));
def_property_static
(
name
,
fget
,
fset
,
return_value_policy
::
reference
,
extra
...);
return
*
this
;
}
template
<
typename
D
,
typename
...
Extra
>
yclass
&
def_readonly_static
(
const
char
*
name
,
const
D
*
pm
,
const
Extra
&
...
extra
)
{
cpp_function
fget
([
pm
](
object
)
->
const
D
&
{
return
*
pm
;
},
scope
(
*
this
));
def_property_readonly_static
(
name
,
fget
,
return_value_policy
::
reference
,
extra
...);
return
*
this
;
}
/// Uses return_value_policy::reference_internal by default
template
<
typename
Getter
,
typename
...
Extra
>
yclass
&
def_property_readonly
(
const
char
*
name
,
const
Getter
&
fget
,
const
Extra
&
...
extra
)
{
return
def_property_readonly
(
name
,
cpp_function
(
method_adaptor
<
type
>
(
fget
)),
return_value_policy
::
reference_internal
,
extra
...);
}
/// Uses cpp_function's return_value_policy by default
template
<
typename
...
Extra
>
yclass
&
def_property_readonly
(
const
char
*
name
,
const
cpp_function
&
fget
,
const
Extra
&
...
extra
)
{
return
def_property
(
name
,
fget
,
nullptr
,
extra
...);
}
/// Uses return_value_policy::reference by default
template
<
typename
Getter
,
typename
...
Extra
>
yclass
&
def_property_readonly_static
(
const
char
*
name
,
const
Getter
&
fget
,
const
Extra
&
...
extra
)
{
return
def_property_readonly_static
(
name
,
cpp_function
(
fget
),
return_value_policy
::
reference
,
extra
...);
}
/// Uses cpp_function's return_value_policy by default
template
<
typename
...
Extra
>
yclass
&
def_property_readonly_static
(
const
char
*
name
,
const
cpp_function
&
fget
,
const
Extra
&
...
extra
)
{
return
def_property_static
(
name
,
fget
,
nullptr
,
extra
...);
}
/// Uses return_value_policy::reference_internal by default
template
<
typename
Getter
,
typename
Setter
,
typename
...
Extra
>
yclass
&
def_property
(
const
char
*
name
,
const
Getter
&
fget
,
const
Setter
&
fset
,
const
Extra
&
...
extra
)
{
return
def_property
(
name
,
fget
,
cpp_function
(
method_adaptor
<
type
>
(
fset
)),
extra
...);
}
template
<
typename
Getter
,
typename
...
Extra
>
yclass
&
def_property
(
const
char
*
name
,
const
Getter
&
fget
,
const
cpp_function
&
fset
,
const
Extra
&
...
extra
)
{
return
def_property
(
name
,
cpp_function
(
method_adaptor
<
type
>
(
fget
)),
fset
,
return_value_policy
::
reference_internal
,
extra
...);
}
/// Uses cpp_function's return_value_policy by default
template
<
typename
...
Extra
>
yclass
&
def_property
(
const
char
*
name
,
const
cpp_function
&
fget
,
const
cpp_function
&
fset
,
const
Extra
&
...
extra
)
{
return
def_property_static
(
name
,
fget
,
fset
,
is_method
(
*
this
),
extra
...);
}
/// Uses return_value_policy::reference by default
template
<
typename
Getter
,
typename
...
Extra
>
yclass
&
def_property_static
(
const
char
*
name
,
const
Getter
&
fget
,
const
cpp_function
&
fset
,
const
Extra
&
...
extra
)
{
return
def_property_static
(
name
,
cpp_function
(
fget
),
fset
,
return_value_policy
::
reference
,
extra
...);
}
/// Uses cpp_function's return_value_policy by default
template
<
typename
...
Extra
>
yclass
&
def_property_static
(
const
char
*
name
,
const
cpp_function
&
fget
,
const
cpp_function
&
fset
,
const
Extra
&
...
extra
)
{
static_assert
(
0
==
detail
::
constexpr_sum
(
std
::
is_base_of
<
arg
,
Extra
>::
value
...),
"Argument annotations are not allowed for properties"
);
auto
rec_fget
=
get_function_record
(
fget
),
rec_fset
=
get_function_record
(
fset
);
auto
*
rec_active
=
rec_fget
;
if
(
rec_fget
)
{
char
*
doc_prev
=
rec_fget
->
doc
;
/* 'extra' field may include a property-specific documentation string */
detail
::
process_attributes
<
Extra
...
>::
init
(
extra
...,
rec_fget
);
if
(
rec_fget
->
doc
&&
rec_fget
->
doc
!=
doc_prev
)
{
free
(
doc_prev
);
rec_fget
->
doc
=
strdup
(
rec_fget
->
doc
);
}
}
if
(
rec_fset
)
{
char
*
doc_prev
=
rec_fset
->
doc
;
detail
::
process_attributes
<
Extra
...
>::
init
(
extra
...,
rec_fset
);
if
(
rec_fset
->
doc
&&
rec_fset
->
doc
!=
doc_prev
)
{
free
(
doc_prev
);
rec_fset
->
doc
=
strdup
(
rec_fset
->
doc
);
}
if
(
!
rec_active
)
rec_active
=
rec_fset
;
}
def_property_static_impl
(
name
,
fget
,
fset
,
rec_active
);
return
*
this
;
}
private
:
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
template
<
typename
T
>
static
void
init_holder
(
detail
::
instance
*
inst
,
detail
::
value_and_holder
&
v_h
,
const
holder_type
*
/* unused */
,
const
std
::
enable_shared_from_this
<
T
>
*
/* dummy */
)
{
try
{
auto
sh
=
std
::
dynamic_pointer_cast
<
typename
holder_type
::
element_type
>
(
v_h
.
value_ptr
<
type
>
()
->
shared_from_this
());
if
(
sh
)
{
new
(
std
::
addressof
(
v_h
.
holder
<
holder_type
>
()))
holder_type
(
std
::
move
(
sh
));
v_h
.
set_holder_constructed
();
}
}
catch
(
const
std
::
bad_weak_ptr
&
)
{}
if
(
!
v_h
.
holder_constructed
()
&&
inst
->
owned
)
{
new
(
std
::
addressof
(
v_h
.
holder
<
holder_type
>
()))
holder_type
(
v_h
.
value_ptr
<
type
>
());
v_h
.
set_holder_constructed
();
}
}
static
void
init_holder_from_existing
(
const
detail
::
value_and_holder
&
v_h
,
const
holder_type
*
holder_ptr
,
std
::
true_type
/*is_copy_constructible*/
)
{
new
(
std
::
addressof
(
v_h
.
holder
<
holder_type
>
()))
holder_type
(
*
reinterpret_cast
<
const
holder_type
*>
(
holder_ptr
));
}
static
void
init_holder_from_existing
(
const
detail
::
value_and_holder
&
v_h
,
const
holder_type
*
holder_ptr
,
std
::
false_type
/*is_copy_constructible*/
)
{
new
(
std
::
addressof
(
v_h
.
holder
<
holder_type
>
()))
holder_type
(
std
::
move
(
*
const_cast
<
holder_type
*>
(
holder_ptr
)));
}
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
static
void
init_holder
(
detail
::
instance
*
inst
,
detail
::
value_and_holder
&
v_h
,
const
holder_type
*
holder_ptr
,
const
void
*
/* dummy -- not enable_shared_from_this<T>) */
)
{
if
(
holder_ptr
)
{
init_holder_from_existing
(
v_h
,
holder_ptr
,
std
::
is_copy_constructible
<
holder_type
>
());
v_h
.
set_holder_constructed
();
}
else
if
(
inst
->
owned
||
detail
::
always_construct_holder
<
holder_type
>::
value
)
{
new
(
std
::
addressof
(
v_h
.
holder
<
holder_type
>
()))
holder_type
(
v_h
.
value_ptr
<
type
>
());
v_h
.
set_holder_constructed
();
}
}
/// Performs instance initialization including constructing a holder and registering the known
/// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an
/// optional pointer to an existing holder to use; if not specified and the instance is
/// `.owned`, a new holder will be constructed to manage the value pointer.
static
void
init_instance
(
detail
::
instance
*
inst
,
const
void
*
holder_ptr
)
{
auto
v_h
=
inst
->
get_value_and_holder
(
detail
::
get_type_info
(
typeid
(
type
)));
if
(
!
v_h
.
instance_registered
())
{
register_instance
(
inst
,
v_h
.
value_ptr
(),
v_h
.
type
);
v_h
.
set_instance_registered
();
}
init_holder
(
inst
,
v_h
,
(
const
holder_type
*
)
holder_ptr
,
v_h
.
value_ptr
<
type
>
());
}
/// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
static
void
dealloc
(
detail
::
value_and_holder
&
v_h
)
{
// We could be deallocating because we are cleaning up after a Python exception.
// If so, the Python error indicator will be set. We need to clear that before
// running the destructor, in case the destructor code calls more Python.
// If we don't, the Python API will exit with an exception, and pybind11 will
// throw error_already_set from the C++ destructor which is forbidden and triggers
// std::terminate().
error_scope
scope
;
if
(
v_h
.
holder_constructed
())
{
v_h
.
holder
<
holder_type
>
().
~
holder_type
();
v_h
.
set_holder_constructed
(
false
);
}
else
{
detail
::
call_operator_delete
(
v_h
.
value_ptr
<
type
>
(),
v_h
.
type
->
type_size
,
v_h
.
type
->
type_align
);
}
v_h
.
value_ptr
()
=
nullptr
;
}
static
detail
::
function_record
*
get_function_record
(
handle
h
)
{
h
=
detail
::
get_function
(
h
);
return
h
?
(
detail
::
function_record
*
)
reinterpret_borrow
<
capsule
>
(
PyCFunction_GET_SELF
(
h
.
ptr
()))
:
nullptr
;
}
};
PYBIND11_NAMESPACE_END
(
PYBIND11_NAMESPACE
)
tests/test_unique_ptr_member.cpp
View file @
410e364a
#include "pybind11_tests.h"
#include <pybind11/yclass.h>
#include <memory>
...
...
@@ -38,9 +39,12 @@ inline int cpp_pattern() {
TEST_SUBMODULE
(
unique_ptr_member
,
m
)
{
m
.
def
(
"cpp_pattern"
,
cpp_pattern
);
py
::
class_
<
pointee
>
(
m
,
"pointee"
)
py
::
yclass
<
pointee
>
(
m
,
"pointee"
)
#ifdef JUNK
.
def
(
py
::
init
<>
())
.
def
(
"get_int"
,
&
pointee
::
get_int
);
.
def
(
"get_int"
,
&
pointee
::
get_int
)
#endif
;
py
::
class_
<
ptr_owner
>
(
m
,
"ptr_owner"
)
#ifdef FEAT_UNIQUE_PTR_ARG
...
...
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