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
0bc272b2
Commit
0bc272b2
authored
Jun 22, 2017
by
Dean Moldovan
Browse files
Move tests from short translation units into their logical parents
parent
83e328f5
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
365 additions
and
426 deletions
+365
-426
docs/advanced/classes.rst
docs/advanced/classes.rst
+1
-1
tests/CMakeLists.txt
tests/CMakeLists.txt
+0
-2
tests/test_alias_initialization.cpp
tests/test_alias_initialization.cpp
+0
-62
tests/test_alias_initialization.py
tests/test_alias_initialization.py
+0
-80
tests/test_class.cpp
tests/test_class.cpp
+127
-0
tests/test_class.py
tests/test_class.py
+77
-0
tests/test_inheritance.cpp
tests/test_inheritance.cpp
+0
-141
tests/test_inheritance.py
tests/test_inheritance.py
+0
-91
tests/test_virtual_functions.cpp
tests/test_virtual_functions.cpp
+48
-2
tests/test_virtual_functions.py
tests/test_virtual_functions.py
+112
-47
No files found.
docs/advanced/classes.rst
View file @
0bc272b2
...
...
@@ -325,7 +325,7 @@ ensuring member initialization and (eventual) destruction.
.. seealso::
See the file :file:`tests/test_
alias_initializa
tion.cpp` for complete examples
See the file :file:`tests/test_
virtual_func
tion
s
.cpp` for complete examples
showing both normal and forced trampoline instantiation.
.. _custom_constructors:
...
...
tests/CMakeLists.txt
View file @
0bc272b2
...
...
@@ -26,7 +26,6 @@ endif()
# Full set of test files (you can override these; see below)
set
(
PYBIND11_TEST_FILES
test_alias_initialization.cpp
test_buffers.cpp
test_builtin_casters.cpp
test_call_policies.cpp
...
...
@@ -40,7 +39,6 @@ set(PYBIND11_TEST_FILES
test_enum.cpp
test_eval.cpp
test_exceptions.cpp
test_inheritance.cpp
test_kwargs_and_defaults.cpp
test_methods_and_attributes.cpp
test_modules.cpp
...
...
tests/test_alias_initialization.cpp
deleted
100644 → 0
View file @
83e328f5
/*
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
deleted
100644 → 0
View file @
83e328f5
import
pytest
def
test_alias_delay_initialization1
(
capture
):
"""
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
pytest
.
gc_collect
()
assert
capture
==
"A.f()"
# Python version
with
capture
:
b
=
B
()
call_f
(
b
)
del
b
pytest
.
gc_collect
()
assert
capture
==
"""
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def
test_alias_delay_initialization2
(
capture
):
"""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.
"""
from
pybind11_tests
import
A2
,
call_f
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
pytest
.
gc_collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with
capture
:
b2
=
B2
()
call_f
(
b2
)
del
b2
pytest
.
gc_collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""
tests/test_class.cpp
View file @
0bc272b2
...
...
@@ -23,6 +23,133 @@ TEST_SUBMODULE(class_, m) {
py
::
class_
<
NoConstructor
>
(
m
,
"NoConstructor"
)
.
def_static
(
"new_instance"
,
&
NoConstructor
::
new_instance
,
"Return an instance"
);
// test_inheritance
class
Pet
{
public:
Pet
(
const
std
::
string
&
name
,
const
std
::
string
&
species
)
:
m_name
(
name
),
m_species
(
species
)
{}
std
::
string
name
()
const
{
return
m_name
;
}
std
::
string
species
()
const
{
return
m_species
;
}
private:
std
::
string
m_name
;
std
::
string
m_species
;
};
class
Dog
:
public
Pet
{
public:
Dog
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"dog"
)
{}
std
::
string
bark
()
const
{
return
"Woof!"
;
}
};
class
Rabbit
:
public
Pet
{
public:
Rabbit
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"parrot"
)
{}
};
class
Hamster
:
public
Pet
{
public:
Hamster
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"rodent"
)
{}
};
class
Chimera
:
public
Pet
{
Chimera
()
:
Pet
(
"Kimmy"
,
"chimera"
)
{}
};
py
::
class_
<
Pet
>
pet_class
(
m
,
"Pet"
);
pet_class
.
def
(
py
::
init
<
std
::
string
,
std
::
string
>
())
.
def
(
"name"
,
&
Pet
::
name
)
.
def
(
"species"
,
&
Pet
::
species
);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py
::
class_
<
Dog
>
(
m
,
"Dog"
,
pet_class
)
.
def
(
py
::
init
<
std
::
string
>
());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py
::
class_
<
Rabbit
,
Pet
>
(
m
,
"Rabbit"
)
.
def
(
py
::
init
<
std
::
string
>
());
/* And another: list parent in class template arguments */
py
::
class_
<
Hamster
,
Pet
>
(
m
,
"Hamster"
)
.
def
(
py
::
init
<
std
::
string
>
());
/* Constructors are not inherited by default */
py
::
class_
<
Chimera
,
Pet
>
(
m
,
"Chimera"
);
m
.
def
(
"pet_name_species"
,
[](
const
Pet
&
pet
)
{
return
pet
.
name
()
+
" is a "
+
pet
.
species
();
});
m
.
def
(
"dog_bark"
,
[](
const
Dog
&
dog
)
{
return
dog
.
bark
();
});
// test_automatic_upcasting
struct
BaseClass
{
virtual
~
BaseClass
()
{}
};
struct
DerivedClass1
:
BaseClass
{
};
struct
DerivedClass2
:
BaseClass
{
};
py
::
class_
<
BaseClass
>
(
m
,
"BaseClass"
).
def
(
py
::
init
<>
());
py
::
class_
<
DerivedClass1
>
(
m
,
"DerivedClass1"
).
def
(
py
::
init
<>
());
py
::
class_
<
DerivedClass2
>
(
m
,
"DerivedClass2"
).
def
(
py
::
init
<>
());
m
.
def
(
"return_class_1"
,
[]()
->
BaseClass
*
{
return
new
DerivedClass1
();
});
m
.
def
(
"return_class_2"
,
[]()
->
BaseClass
*
{
return
new
DerivedClass2
();
});
m
.
def
(
"return_class_n"
,
[](
int
n
)
->
BaseClass
*
{
if
(
n
==
1
)
return
new
DerivedClass1
();
if
(
n
==
2
)
return
new
DerivedClass2
();
return
new
BaseClass
();
});
m
.
def
(
"return_none"
,
[]()
->
BaseClass
*
{
return
nullptr
;
});
// test_isinstance
m
.
def
(
"check_instances"
,
[](
py
::
list
l
)
{
return
py
::
make_tuple
(
py
::
isinstance
<
py
::
tuple
>
(
l
[
0
]),
py
::
isinstance
<
py
::
dict
>
(
l
[
1
]),
py
::
isinstance
<
Pet
>
(
l
[
2
]),
py
::
isinstance
<
Pet
>
(
l
[
3
]),
py
::
isinstance
<
Dog
>
(
l
[
4
]),
py
::
isinstance
<
Rabbit
>
(
l
[
5
]),
py
::
isinstance
<
UnregisteredType
>
(
l
[
6
])
);
});
// test_mismatched_holder
struct
MismatchBase1
{
};
struct
MismatchDerived1
:
MismatchBase1
{
};
struct
MismatchBase2
{
};
struct
MismatchDerived2
:
MismatchBase2
{
};
m
.
def
(
"mismatched_holder_1"
,
[]()
{
auto
mod
=
py
::
module
::
import
(
"__main__"
);
py
::
class_
<
MismatchBase1
,
std
::
shared_ptr
<
MismatchBase1
>>
(
mod
,
"MismatchBase1"
);
py
::
class_
<
MismatchDerived1
,
MismatchBase1
>
(
mod
,
"MismatchDerived1"
);
});
m
.
def
(
"mismatched_holder_2"
,
[]()
{
auto
mod
=
py
::
module
::
import
(
"__main__"
);
py
::
class_
<
MismatchBase2
>
(
mod
,
"MismatchBase2"
);
py
::
class_
<
MismatchDerived2
,
std
::
shared_ptr
<
MismatchDerived2
>
,
MismatchBase2
>
(
mod
,
"MismatchDerived2"
);
});
// test_override_static
// #511: problem with inheritance + overwritten def_static
struct
MyBase
{
static
std
::
unique_ptr
<
MyBase
>
make
()
{
return
std
::
unique_ptr
<
MyBase
>
(
new
MyBase
());
}
};
struct
MyDerived
:
MyBase
{
static
std
::
unique_ptr
<
MyDerived
>
make
()
{
return
std
::
unique_ptr
<
MyDerived
>
(
new
MyDerived
());
}
};
py
::
class_
<
MyBase
>
(
m
,
"MyBase"
)
.
def_static
(
"make"
,
&
MyBase
::
make
);
py
::
class_
<
MyDerived
,
MyBase
>
(
m
,
"MyDerived"
)
.
def_static
(
"make"
,
&
MyDerived
::
make
)
.
def_static
(
"make2"
,
&
MyDerived
::
make
);
}
template
<
int
N
>
class
BreaksBase
{};
...
...
tests/test_class.py
View file @
0bc272b2
...
...
@@ -42,3 +42,80 @@ def test_docstrings(doc):
Return an instance
"""
def
test_inheritance
(
msg
):
roger
=
m
.
Rabbit
(
'Rabbit'
)
assert
roger
.
name
()
+
" is a "
+
roger
.
species
()
==
"Rabbit is a parrot"
assert
m
.
pet_name_species
(
roger
)
==
"Rabbit is a parrot"
polly
=
m
.
Pet
(
'Polly'
,
'parrot'
)
assert
polly
.
name
()
+
" is a "
+
polly
.
species
()
==
"Polly is a parrot"
assert
m
.
pet_name_species
(
polly
)
==
"Polly is a parrot"
molly
=
m
.
Dog
(
'Molly'
)
assert
molly
.
name
()
+
" is a "
+
molly
.
species
()
==
"Molly is a dog"
assert
m
.
pet_name_species
(
molly
)
==
"Molly is a dog"
fred
=
m
.
Hamster
(
'Fred'
)
assert
fred
.
name
()
+
" is a "
+
fred
.
species
()
==
"Fred is a rodent"
assert
m
.
dog_bark
(
molly
)
==
"Woof!"
with
pytest
.
raises
(
TypeError
)
as
excinfo
:
m
.
dog_bark
(
polly
)
assert
msg
(
excinfo
.
value
)
==
"""
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.class_.Dog) -> str
Invoked with: <m.class_.Pet object at 0>
"""
with
pytest
.
raises
(
TypeError
)
as
excinfo
:
m
.
Chimera
(
"lion"
,
"goat"
)
assert
"No constructor defined!"
in
str
(
excinfo
.
value
)
def
test_automatic_upcasting
():
assert
type
(
m
.
return_class_1
()).
__name__
==
"DerivedClass1"
assert
type
(
m
.
return_class_2
()).
__name__
==
"DerivedClass2"
assert
type
(
m
.
return_none
()).
__name__
==
"NoneType"
# Repeat these a few times in a random order to ensure no invalid caching is applied
assert
type
(
m
.
return_class_n
(
1
)).
__name__
==
"DerivedClass1"
assert
type
(
m
.
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
m
.
return_class_n
(
0
)).
__name__
==
"BaseClass"
assert
type
(
m
.
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
m
.
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
m
.
return_class_n
(
0
)).
__name__
==
"BaseClass"
assert
type
(
m
.
return_class_n
(
1
)).
__name__
==
"DerivedClass1"
def
test_isinstance
():
objects
=
[
tuple
(),
dict
(),
m
.
Pet
(
"Polly"
,
"parrot"
)]
+
[
m
.
Dog
(
"Molly"
)]
*
4
expected
=
(
True
,
True
,
True
,
True
,
True
,
False
,
False
)
assert
m
.
check_instances
(
objects
)
==
expected
def
test_mismatched_holder
():
import
re
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
m
.
mismatched_holder_1
()
assert
re
.
match
(
'generic_type: type ".*MismatchDerived1" does not have a non-default '
'holder type while its base ".*MismatchBase1" does'
,
str
(
excinfo
.
value
))
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
m
.
mismatched_holder_2
()
assert
re
.
match
(
'generic_type: type ".*MismatchDerived2" has a non-default holder type '
'while its base ".*MismatchBase2" does not'
,
str
(
excinfo
.
value
))
def
test_override_static
():
"""#511: problem with inheritance + overwritten def_static"""
b
=
m
.
MyBase
.
make
()
d1
=
m
.
MyDerived
.
make2
()
d2
=
m
.
MyDerived
.
make
()
assert
isinstance
(
b
,
m
.
MyBase
)
assert
isinstance
(
d1
,
m
.
MyDerived
)
assert
isinstance
(
d2
,
m
.
MyDerived
)
tests/test_inheritance.cpp
deleted
100644 → 0
View file @
83e328f5
/*
tests/test_inheritance.cpp -- inheritance, automatic upcasting for polymorphic types
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
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"
class
Pet
{
public:
Pet
(
const
std
::
string
&
name
,
const
std
::
string
&
species
)
:
m_name
(
name
),
m_species
(
species
)
{}
std
::
string
name
()
const
{
return
m_name
;
}
std
::
string
species
()
const
{
return
m_species
;
}
private:
std
::
string
m_name
;
std
::
string
m_species
;
};
class
Dog
:
public
Pet
{
public:
Dog
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"dog"
)
{}
std
::
string
bark
()
const
{
return
"Woof!"
;
}
};
class
Rabbit
:
public
Pet
{
public:
Rabbit
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"parrot"
)
{}
};
class
Hamster
:
public
Pet
{
public:
Hamster
(
const
std
::
string
&
name
)
:
Pet
(
name
,
"rodent"
)
{}
};
class
Chimera
:
public
Pet
{
Chimera
()
:
Pet
(
"Kimmy"
,
"chimera"
)
{}
};
std
::
string
pet_name_species
(
const
Pet
&
pet
)
{
return
pet
.
name
()
+
" is a "
+
pet
.
species
();
}
std
::
string
dog_bark
(
const
Dog
&
dog
)
{
return
dog
.
bark
();
}
struct
BaseClass
{
virtual
~
BaseClass
()
{}
};
struct
DerivedClass1
:
BaseClass
{
};
struct
DerivedClass2
:
BaseClass
{
};
struct
MismatchBase1
{
};
struct
MismatchDerived1
:
MismatchBase1
{
};
struct
MismatchBase2
{
};
struct
MismatchDerived2
:
MismatchBase2
{
};
test_initializer
inheritance
([](
py
::
module
&
m
)
{
py
::
class_
<
Pet
>
pet_class
(
m
,
"Pet"
);
pet_class
.
def
(
py
::
init
<
std
::
string
,
std
::
string
>
())
.
def
(
"name"
,
&
Pet
::
name
)
.
def
(
"species"
,
&
Pet
::
species
);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py
::
class_
<
Dog
>
(
m
,
"Dog"
,
pet_class
)
.
def
(
py
::
init
<
std
::
string
>
());
/* Another way of declaring a subclass relationship: reference parent's C++ type */
py
::
class_
<
Rabbit
,
Pet
>
(
m
,
"Rabbit"
)
.
def
(
py
::
init
<
std
::
string
>
());
/* And another: list parent in class template arguments */
py
::
class_
<
Hamster
,
Pet
>
(
m
,
"Hamster"
)
.
def
(
py
::
init
<
std
::
string
>
());
py
::
class_
<
Chimera
,
Pet
>
(
m
,
"Chimera"
);
m
.
def
(
"pet_name_species"
,
pet_name_species
);
m
.
def
(
"dog_bark"
,
dog_bark
);
py
::
class_
<
BaseClass
>
(
m
,
"BaseClass"
).
def
(
py
::
init
<>
());
py
::
class_
<
DerivedClass1
>
(
m
,
"DerivedClass1"
).
def
(
py
::
init
<>
());
py
::
class_
<
DerivedClass2
>
(
m
,
"DerivedClass2"
).
def
(
py
::
init
<>
());
m
.
def
(
"return_class_1"
,
[]()
->
BaseClass
*
{
return
new
DerivedClass1
();
});
m
.
def
(
"return_class_2"
,
[]()
->
BaseClass
*
{
return
new
DerivedClass2
();
});
m
.
def
(
"return_class_n"
,
[](
int
n
)
->
BaseClass
*
{
if
(
n
==
1
)
return
new
DerivedClass1
();
if
(
n
==
2
)
return
new
DerivedClass2
();
return
new
BaseClass
();
});
m
.
def
(
"return_none"
,
[]()
->
BaseClass
*
{
return
nullptr
;
});
m
.
def
(
"test_isinstance"
,
[](
py
::
list
l
)
{
return
py
::
make_tuple
(
py
::
isinstance
<
py
::
tuple
>
(
l
[
0
]),
py
::
isinstance
<
py
::
dict
>
(
l
[
1
]),
py
::
isinstance
<
Pet
>
(
l
[
2
]),
py
::
isinstance
<
Pet
>
(
l
[
3
]),
py
::
isinstance
<
Dog
>
(
l
[
4
]),
py
::
isinstance
<
Rabbit
>
(
l
[
5
]),
py
::
isinstance
<
UnregisteredType
>
(
l
[
6
])
);
});
m
.
def
(
"test_mismatched_holder_type_1"
,
[]()
{
auto
m
=
py
::
module
::
import
(
"__main__"
);
py
::
class_
<
MismatchBase1
,
std
::
shared_ptr
<
MismatchBase1
>>
(
m
,
"MismatchBase1"
);
py
::
class_
<
MismatchDerived1
,
MismatchBase1
>
(
m
,
"MismatchDerived1"
);
});
m
.
def
(
"test_mismatched_holder_type_2"
,
[]()
{
auto
m
=
py
::
module
::
import
(
"__main__"
);
py
::
class_
<
MismatchBase2
>
(
m
,
"MismatchBase2"
);
py
::
class_
<
MismatchDerived2
,
std
::
shared_ptr
<
MismatchDerived2
>
,
MismatchBase2
>
(
m
,
"MismatchDerived2"
);
});
// #511: problem with inheritance + overwritten def_static
struct
MyBase
{
static
std
::
unique_ptr
<
MyBase
>
make
()
{
return
std
::
unique_ptr
<
MyBase
>
(
new
MyBase
());
}
};
struct
MyDerived
:
MyBase
{
static
std
::
unique_ptr
<
MyDerived
>
make
()
{
return
std
::
unique_ptr
<
MyDerived
>
(
new
MyDerived
());
}
};
py
::
class_
<
MyBase
>
(
m
,
"MyBase"
)
.
def_static
(
"make"
,
&
MyBase
::
make
);
py
::
class_
<
MyDerived
,
MyBase
>
(
m
,
"MyDerived"
)
.
def_static
(
"make"
,
&
MyDerived
::
make
)
.
def_static
(
"make2"
,
&
MyDerived
::
make
);
});
tests/test_inheritance.py
deleted
100644 → 0
View file @
83e328f5
import
pytest
def
test_inheritance
(
msg
):
from
pybind11_tests
import
Pet
,
Dog
,
Rabbit
,
Hamster
,
Chimera
,
dog_bark
,
pet_name_species
roger
=
Rabbit
(
'Rabbit'
)
assert
roger
.
name
()
+
" is a "
+
roger
.
species
()
==
"Rabbit is a parrot"
assert
pet_name_species
(
roger
)
==
"Rabbit is a parrot"
polly
=
Pet
(
'Polly'
,
'parrot'
)
assert
polly
.
name
()
+
" is a "
+
polly
.
species
()
==
"Polly is a parrot"
assert
pet_name_species
(
polly
)
==
"Polly is a parrot"
molly
=
Dog
(
'Molly'
)
assert
molly
.
name
()
+
" is a "
+
molly
.
species
()
==
"Molly is a dog"
assert
pet_name_species
(
molly
)
==
"Molly is a dog"
fred
=
Hamster
(
'Fred'
)
assert
fred
.
name
()
+
" is a "
+
fred
.
species
()
==
"Fred is a rodent"
assert
dog_bark
(
molly
)
==
"Woof!"
with
pytest
.
raises
(
TypeError
)
as
excinfo
:
dog_bark
(
polly
)
assert
msg
(
excinfo
.
value
)
==
"""
dog_bark(): incompatible function arguments. The following argument types are supported:
1. (arg0: m.Dog) -> str
Invoked with: <m.Pet object at 0>
"""
with
pytest
.
raises
(
TypeError
)
as
excinfo
:
Chimera
(
"lion"
,
"goat"
)
assert
"No constructor defined!"
in
str
(
excinfo
.
value
)
def
test_automatic_upcasting
():
from
pybind11_tests
import
return_class_1
,
return_class_2
,
return_class_n
,
return_none
assert
type
(
return_class_1
()).
__name__
==
"DerivedClass1"
assert
type
(
return_class_2
()).
__name__
==
"DerivedClass2"
assert
type
(
return_none
()).
__name__
==
"NoneType"
# Repeat these a few times in a random order to ensure no invalid caching
# is applied
assert
type
(
return_class_n
(
1
)).
__name__
==
"DerivedClass1"
assert
type
(
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
return_class_n
(
0
)).
__name__
==
"BaseClass"
assert
type
(
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
return_class_n
(
2
)).
__name__
==
"DerivedClass2"
assert
type
(
return_class_n
(
0
)).
__name__
==
"BaseClass"
assert
type
(
return_class_n
(
1
)).
__name__
==
"DerivedClass1"
def
test_isinstance
():
from
pybind11_tests
import
test_isinstance
,
Pet
,
Dog
objects
=
[
tuple
(),
dict
(),
Pet
(
"Polly"
,
"parrot"
)]
+
[
Dog
(
"Molly"
)]
*
4
expected
=
(
True
,
True
,
True
,
True
,
True
,
False
,
False
)
assert
test_isinstance
(
objects
)
==
expected
def
test_holder
():
from
pybind11_tests
import
test_mismatched_holder_type_1
,
test_mismatched_holder_type_2
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
test_mismatched_holder_type_1
()
assert
str
(
excinfo
.
value
)
==
(
"generic_type: type
\"
MismatchDerived1
\"
does not have "
"a non-default holder type while its base "
"
\"
MismatchBase1
\"
does"
)
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
test_mismatched_holder_type_2
()
assert
str
(
excinfo
.
value
)
==
(
"generic_type: type
\"
MismatchDerived2
\"
has a "
"non-default holder type while its base "
"
\"
MismatchBase2
\"
does not"
)
def
test_inheritance_override_def_static
():
"""#511: problem with inheritance + overwritten def_static"""
from
pybind11_tests
import
MyBase
,
MyDerived
b
=
MyBase
.
make
()
d1
=
MyDerived
.
make2
()
d2
=
MyDerived
.
make
()
assert
isinstance
(
b
,
MyBase
)
assert
isinstance
(
d1
,
MyDerived
)
assert
isinstance
(
d2
,
MyDerived
)
tests/test_virtual_functions.cpp
View file @
0bc272b2
...
...
@@ -322,7 +322,7 @@ struct DispatchIssue : Base {
}
};
test_initializer
virtual_functions
([](
py
::
module
&
m
)
{
TEST_SUBMODULE
(
virtual_functions
,
m
)
{
py
::
class_
<
ExampleVirt
,
PyExampleVirt
>
(
m
,
"ExampleVirt"
)
.
def
(
py
::
init
<
int
>
())
/* Reference original class in function definitions */
...
...
@@ -352,6 +352,52 @@ test_initializer virtual_functions([](py::module &m) {
m
.
def
(
"cstats_debug"
,
&
ConstructorStats
::
get
<
ExampleVirt
>
);
initialize_inherited_virtuals
(
m
);
// test_alias_delay_initialization1
// 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
);
}
};
py
::
class_
<
A
,
PyA
>
(
m
,
"A"
)
.
def
(
py
::
init
<>
())
.
def
(
"f"
,
&
A
::
f
);
m
.
def
(
"call_f"
,
[](
A
*
a
)
{
a
->
f
();
});
// test_alias_delay_initialization2
// ... 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
();
});
// #159: virtual function dispatch has problems with similar-named functions
py
::
class_
<
Base
,
DispatchIssue
>
(
m
,
"DispatchIssue"
)
.
def
(
py
::
init
<>
())
...
...
@@ -398,4 +444,4 @@ test_initializer virtual_functions([](py::module &m) {
// .def("str_ref", &OverrideTest::str_ref)
.
def
(
"A_value"
,
&
OverrideTest
::
A_value
)
.
def
(
"A_ref"
,
&
OverrideTest
::
A_ref
);
}
);
}
tests/test_virtual_functions.py
View file @
0bc272b2
import
pytest
import
pybind11_tests
from
pybind11_tests
import
virtual_functions
as
m
from
pybind11_tests
import
ConstructorStats
def
test_override
(
capture
,
msg
):
from
pybind11_tests
import
(
ExampleVirt
,
runExampleVirt
,
runExampleVirtVirtual
,
runExampleVirtBool
)
class
ExtendedExampleVirt
(
ExampleVirt
):
class
ExtendedExampleVirt
(
m
.
ExampleVirt
):
def
__init__
(
self
,
state
):
super
(
ExtendedExampleVirt
,
self
).
__init__
(
state
+
1
)
self
.
data
=
"Hello world"
...
...
@@ -33,40 +31,40 @@ def test_override(capture, msg):
def
get_string2
(
self
):
return
"override2"
ex12
=
ExampleVirt
(
10
)
ex12
=
m
.
ExampleVirt
(
10
)
with
capture
:
assert
runExampleVirt
(
ex12
,
20
)
==
30
assert
m
.
runExampleVirt
(
ex12
,
20
)
==
30
assert
capture
==
"""
Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
"""
# noqa: E501 line too long
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
runExampleVirtVirtual
(
ex12
)
m
.
runExampleVirtVirtual
(
ex12
)
assert
msg
(
excinfo
.
value
)
==
'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
ex12p
=
ExtendedExampleVirt
(
10
)
with
capture
:
assert
runExampleVirt
(
ex12p
,
20
)
==
32
assert
m
.
runExampleVirt
(
ex12p
,
20
)
==
32
assert
capture
==
"""
ExtendedExampleVirt::run(20), calling parent..
Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
"""
# noqa: E501 line too long
with
capture
:
assert
runExampleVirtBool
(
ex12p
)
is
False
assert
m
.
runExampleVirtBool
(
ex12p
)
is
False
assert
capture
==
"ExtendedExampleVirt::run_bool()"
with
capture
:
runExampleVirtVirtual
(
ex12p
)
m
.
runExampleVirtVirtual
(
ex12p
)
assert
capture
==
"ExtendedExampleVirt::pure_virtual(): Hello world"
ex12p2
=
ExtendedExampleVirt2
(
15
)
with
capture
:
assert
runExampleVirt
(
ex12p2
,
50
)
==
68
assert
m
.
runExampleVirt
(
ex12p2
,
50
)
==
68
assert
capture
==
"""
ExtendedExampleVirt::run(50), calling parent..
Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
"""
# noqa: E501 line too long
cstats
=
ConstructorStats
.
get
(
ExampleVirt
)
cstats
=
ConstructorStats
.
get
(
m
.
ExampleVirt
)
assert
cstats
.
alive
()
==
3
del
ex12
,
ex12p
,
ex12p2
assert
cstats
.
alive
()
==
0
...
...
@@ -75,14 +73,88 @@ def test_override(capture, msg):
assert
cstats
.
move_constructions
>=
0
def
test_inheriting_repeat
():
from
pybind11_tests
import
A_Repeat
,
B_Repeat
,
C_Repeat
,
D_Repeat
,
A_Tpl
,
B_Tpl
,
C_Tpl
,
D_Tpl
def
test_alias_delay_initialization1
(
capture
):
"""`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).
"""
class
B
(
m
.
A
):
def
__init__
(
self
):
super
(
B
,
self
).
__init__
()
def
f
(
self
):
print
(
"In python f()"
)
# C++ version
with
capture
:
a
=
m
.
A
()
m
.
call_f
(
a
)
del
a
pytest
.
gc_collect
()
assert
capture
==
"A.f()"
# Python version
with
capture
:
b
=
B
()
m
.
call_f
(
b
)
del
b
pytest
.
gc_collect
()
assert
capture
==
"""
PyA.PyA()
PyA.f()
In python f()
PyA.~PyA()
"""
def
test_alias_delay_initialization2
(
capture
):
"""`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
(
m
.
A2
):
def
__init__
(
self
):
super
(
B2
,
self
).
__init__
()
class
AR
(
A_Repeat
):
def
f
(
self
):
print
(
"In python B2.f()"
)
# No python subclass version
with
capture
:
a2
=
m
.
A2
()
m
.
call_f
(
a2
)
del
a2
pytest
.
gc_collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
A2.f()
PyA2.~PyA2()
"""
# Python subclass version
with
capture
:
b2
=
B2
()
m
.
call_f
(
b2
)
del
b2
pytest
.
gc_collect
()
assert
capture
==
"""
PyA2.PyA2()
PyA2.f()
In python B2.f()
PyA2.~PyA2()
"""
def
test_inheriting_repeat
():
class
AR
(
m
.
A_Repeat
):
def
unlucky_number
(
self
):
return
99
class
AT
(
A_Tpl
):
class
AT
(
m
.
A_Tpl
):
def
unlucky_number
(
self
):
return
999
...
...
@@ -96,21 +168,21 @@ def test_inheriting_repeat():
assert
obj
.
unlucky_number
()
==
999
assert
obj
.
say_everything
()
==
"hi 999"
for
obj
in
[
B_Repeat
(),
B_Tpl
()]:
for
obj
in
[
m
.
B_Repeat
(),
m
.
B_Tpl
()]:
assert
obj
.
say_something
(
3
)
==
"B says hi 3 times"
assert
obj
.
unlucky_number
()
==
13
assert
obj
.
lucky_number
()
==
7.0
assert
obj
.
say_everything
()
==
"B says hi 1 times 13"
for
obj
in
[
C_Repeat
(),
C_Tpl
()]:
for
obj
in
[
m
.
C_Repeat
(),
m
.
C_Tpl
()]:
assert
obj
.
say_something
(
3
)
==
"B says hi 3 times"
assert
obj
.
unlucky_number
()
==
4444
assert
obj
.
lucky_number
()
==
888.0
assert
obj
.
say_everything
()
==
"B says hi 1 times 4444"
class
CR
(
C_Repeat
):
class
CR
(
m
.
C_Repeat
):
def
lucky_number
(
self
):
return
C_Repeat
.
lucky_number
(
self
)
+
1.25
return
m
.
C_Repeat
.
lucky_number
(
self
)
+
1.25
obj
=
CR
()
assert
obj
.
say_something
(
3
)
==
"B says hi 3 times"
...
...
@@ -118,7 +190,7 @@ def test_inheriting_repeat():
assert
obj
.
lucky_number
()
==
889.25
assert
obj
.
say_everything
()
==
"B says hi 1 times 4444"
class
CT
(
C_Tpl
):
class
CT
(
m
.
C_Tpl
):
pass
obj
=
CT
()
...
...
@@ -147,14 +219,14 @@ def test_inheriting_repeat():
assert
obj
.
lucky_number
()
==
888000.0
assert
obj
.
say_everything
()
==
"B says hi 1 times 4444"
class
DR
(
D_Repeat
):
class
DR
(
m
.
D_Repeat
):
def
unlucky_number
(
self
):
return
123
def
lucky_number
(
self
):
return
42.0
for
obj
in
[
D_Repeat
(),
D_Tpl
()]:
for
obj
in
[
m
.
D_Repeat
(),
m
.
D_Tpl
()]:
assert
obj
.
say_something
(
3
)
==
"B says hi 3 times"
assert
obj
.
unlucky_number
()
==
4444
assert
obj
.
lucky_number
()
==
888.0
...
...
@@ -166,7 +238,7 @@ def test_inheriting_repeat():
assert
obj
.
lucky_number
()
==
42.0
assert
obj
.
say_everything
()
==
"B says hi 1 times 123"
class
DT
(
D_Tpl
):
class
DT
(
m
.
D_Tpl
):
def
say_something
(
self
,
times
):
return
"DT says:"
+
(
' quack'
*
times
)
...
...
@@ -189,7 +261,7 @@ def test_inheriting_repeat():
def
unlucky_number
(
self
):
return
-
3
class
BT
(
B_Tpl
):
class
BT
(
m
.
B_Tpl
):
def
say_something
(
self
,
times
):
return
"BT"
*
times
...
...
@@ -209,31 +281,28 @@ def test_inheriting_repeat():
# PyPy: Reference count > 1 causes call with noncopyable instance
# to fail in ncv1.print_nc()
@
pytest
.
unsupported_on_pypy
@
pytest
.
mark
.
skipif
(
not
hasattr
(
pybind11_tests
,
'NCVirt'
),
reason
=
"NCVirt test broken on ICPC"
)
@
pytest
.
mark
.
skipif
(
not
hasattr
(
m
,
"NCVirt"
),
reason
=
"NCVirt test broken on ICPC"
)
def
test_move_support
():
from
pybind11_tests
import
NCVirt
,
NonCopyable
,
Movable
class
NCVirtExt
(
NCVirt
):
class
NCVirtExt
(
m
.
NCVirt
):
def
get_noncopyable
(
self
,
a
,
b
):
# Constructs and returns a new instance:
nc
=
NonCopyable
(
a
*
a
,
b
*
b
)
nc
=
m
.
NonCopyable
(
a
*
a
,
b
*
b
)
return
nc
def
get_movable
(
self
,
a
,
b
):
# Return a referenced copy
self
.
movable
=
Movable
(
a
,
b
)
self
.
movable
=
m
.
Movable
(
a
,
b
)
return
self
.
movable
class
NCVirtExt2
(
NCVirt
):
class
NCVirtExt2
(
m
.
NCVirt
):
def
get_noncopyable
(
self
,
a
,
b
):
# Keep a reference: this is going to throw an exception
self
.
nc
=
NonCopyable
(
a
,
b
)
self
.
nc
=
m
.
NonCopyable
(
a
,
b
)
return
self
.
nc
def
get_movable
(
self
,
a
,
b
):
# Return a new instance without storing it
return
Movable
(
a
,
b
)
return
m
.
Movable
(
a
,
b
)
ncv1
=
NCVirtExt
()
assert
ncv1
.
print_nc
(
2
,
3
)
==
"36"
...
...
@@ -244,8 +313,8 @@ def test_move_support():
with
pytest
.
raises
(
RuntimeError
):
ncv2
.
print_nc
(
9
,
9
)
nc_stats
=
ConstructorStats
.
get
(
NonCopyable
)
mv_stats
=
ConstructorStats
.
get
(
Movable
)
nc_stats
=
ConstructorStats
.
get
(
m
.
NonCopyable
)
mv_stats
=
ConstructorStats
.
get
(
m
.
Movable
)
assert
nc_stats
.
alive
()
==
1
assert
mv_stats
.
alive
()
==
1
del
ncv1
,
ncv2
...
...
@@ -261,30 +330,26 @@ def test_move_support():
def
test_dispatch_issue
(
msg
):
"""#159: virtual function dispatch has problems with similar-named functions"""
from
pybind11_tests
import
DispatchIssue
,
dispatch_issue_go
class
PyClass1
(
DispatchIssue
):
class
PyClass1
(
m
.
DispatchIssue
):
def
dispatch
(
self
):
return
"Yay.."
class
PyClass2
(
DispatchIssue
):
class
PyClass2
(
m
.
DispatchIssue
):
def
dispatch
(
self
):
with
pytest
.
raises
(
RuntimeError
)
as
excinfo
:
super
(
PyClass2
,
self
).
dispatch
()
assert
msg
(
excinfo
.
value
)
==
'Tried to call pure virtual function "Base::dispatch"'
p
=
PyClass1
()
return
dispatch_issue_go
(
p
)
return
m
.
dispatch_issue_go
(
p
)
b
=
PyClass2
()
assert
dispatch_issue_go
(
b
)
==
"Yay.."
assert
m
.
dispatch_issue_go
(
b
)
==
"Yay.."
def
test_override_ref
():
"""#392/397: overridding reference-returning functions"""
from
pybind11_tests
import
OverrideTest
o
=
OverrideTest
(
"asdf"
)
o
=
m
.
OverrideTest
(
"asdf"
)
# Not allowed (see associated .cpp comment)
# i = o.str_ref()
...
...
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