Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
P
Pybind11
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Admin message
为了安全,强烈建议开启2FA双因子认证:User Settings -> Account -> Enable two-factor authentication!!!
Show more breadcrumbs
Yixiang Hu
Pybind11
Commits
410e364a
Commit
410e364a
authored
4 years ago
by
Ralf W. Grosse-Kunstleve
Browse files
Options
Downloads
Patches
Plain Diff
first quick experiment adding yclass
parent
7caf5821
No related branches found
Tags
archive/yclass_v0
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
include/pybind11/yclass.h
+352
-0
352 additions, 0 deletions
include/pybind11/yclass.h
tests/test_unique_ptr_member.cpp
+6
-2
6 additions, 2 deletions
tests/test_unique_ptr_member.cpp
with
358 additions
and
2 deletions
include/pybind11/yclass.h
0 → 100644
+
352
−
0
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
)
This diff is collapsed.
Click to expand it.
tests/test_unique_ptr_member.cpp
+
6
−
2
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
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment