Commit f4f2afb6 by Wenzel Jakob Committed by GitHub

Merge pull request #324 from jagerman/example-constructor-tracking

Improve constructor/destructor tracking
parents 85557b1d 3f589379
......@@ -60,9 +60,6 @@ foreach(CompilerFlag ${CompilerFlags})
endforeach()
set(RUN_TEST ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_test.py)
if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
set(RUN_TEST ${RUN_TEST} --relaxed)
endif()
foreach(VALUE ${PYBIND11_EXAMPLES})
string(REGEX REPLACE "^(.+).cpp$" "\\1" EXAMPLE_NAME "${VALUE}")
......
......@@ -8,35 +8,36 @@
*/
#include "example.h"
#include "constructor-stats.h"
class Matrix {
public:
Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) {
std::cout << "Value constructor: Creating a " << rows << "x" << cols << " matrix " << std::endl;
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[rows*cols];
memset(m_data, 0, sizeof(float) * rows * cols);
}
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
std::cout << "Copy constructor: Creating a " << m_rows << "x" << m_cols << " matrix " << std::endl;
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[m_rows * m_cols];
memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols);
}
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
std::cout << "Move constructor: Creating a " << m_rows << "x" << m_cols << " matrix " << std::endl;
print_move_created(this);
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
}
~Matrix() {
std::cout << "Freeing a " << m_rows << "x" << m_cols << " matrix " << std::endl;
print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
}
Matrix &operator=(const Matrix &s) {
std::cout << "Assignment operator : Creating a " << s.m_rows << "x" << s.m_cols << " matrix " << std::endl;
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data;
m_rows = s.m_rows;
m_cols = s.m_cols;
......@@ -46,7 +47,7 @@ public:
}
Matrix &operator=(Matrix &&s) {
std::cout << "Move assignment operator : Creating a " << s.m_rows << "x" << s.m_cols << " matrix " << std::endl;
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) {
delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
......@@ -111,5 +112,6 @@ void init_ex_buffers(py::module &m) {
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
sizeof(float) }
);
});
})
;
}
......@@ -30,3 +30,16 @@ for i in range(m4.rows()):
for j in range(m4.cols()):
print(m4[i, j], end = ' ')
print()
from example import ConstructorStats
cstats = ConstructorStats.get(Matrix)
print("Instances not destroyed:", cstats.alive())
m = m4 = None
print("Instances not destroyed:", cstats.alive())
m2 = None # m2 holds an m reference
print("Instances not destroyed:", cstats.alive())
print("Constructor values:", cstats.values())
print("Copy constructions:", cstats.copy_constructions)
#print("Move constructions:", cstats.move_constructions >= 0) # Don't invoke any
print("Copy assignments:", cstats.copy_assignments)
print("Move assignments:", cstats.move_assignments)
Value constructor: Creating a 5x5 matrix
### Matrix @ 0x1df1920 created 5x5 matrix
0.0
4.0
[[ 0. 0. 0. 0. 0.]
......@@ -10,8 +10,15 @@ Value constructor: Creating a 5x5 matrix
5.0
[[ 1. 2. 3.]
[ 4. 5. 6.]]
Value constructor: Creating a 2x3 matrix
### Matrix @ 0x1fa8cf0 created 2x3 matrix
1.0 2.0 3.0
4.0 5.0 6.0
Freeing a 2x3 matrix
Freeing a 5x5 matrix
Instances not destroyed: 2
### Matrix @ 0x1fa8cf0 destroyed 2x3 matrix
Instances not destroyed: 1
### Matrix @ 0x1df1920 destroyed 5x5 matrix
Instances not destroyed: 0
Constructor values: ['5x5 matrix', '2x3 matrix']
Copy constructions: 0
Copy assignments: 0
Move assignments: 0
......@@ -8,6 +8,7 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/functional.h>
......@@ -57,6 +58,21 @@ void test_dummy_function(const std::function<int(int)> &f) {
}
}
struct Payload {
Payload() {
print_default_created(this);
}
~Payload() {
print_destroyed(this);
}
Payload(const Payload &) {
print_copy_created(this);
}
Payload(Payload &&) {
print_move_created(this);
}
};
void init_ex_callbacks(py::module &m) {
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
......@@ -66,21 +82,6 @@ void init_ex_callbacks(py::module &m) {
/* Test cleanup of lambda closure */
struct Payload {
Payload() {
std::cout << "Payload constructor" << std::endl;
}
~Payload() {
std::cout << "Payload destructor" << std::endl;
}
Payload(const Payload &) {
std::cout << "Payload copy constructor" << std::endl;
}
Payload(Payload &&) {
std::cout << "Payload move constructor" << std::endl;
}
};
m.def("test_cleanup", []() -> std::function<void(void)> {
Payload p;
......@@ -94,4 +95,6 @@ void init_ex_callbacks(py::module &m) {
m.def("dummy_function2", &dummy_function2);
m.def("roundtrip", &roundtrip);
m.def("test_dummy_function", &test_dummy_function);
// Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>);
}
......@@ -55,6 +55,12 @@ print("func(number=43) = %i" % f(number=43))
test_cleanup()
from example import payload_cstats
cstats = payload_cstats()
print("Payload instances not destroyed:", cstats.alive())
print("Copy constructions:", cstats.copy_constructions)
print("Move constructions:", cstats.move_constructions >= 1)
from example import dummy_function
from example import dummy_function2
from example import test_dummy_function
......
......@@ -7,7 +7,7 @@ Molly is a dog
Woof!
The following error is expected: Incompatible function arguments. The following argument types are supported:
1. (arg0: example.Dog) -> None
Invoked with: <example.Pet object at 0>
Invoked with: <example.Pet object at 0x7ffaf4b00db0>
Callback function 1 called!
False
Callback function 2 called : Hello, x, True, 5
......@@ -19,12 +19,15 @@ False
func(43) = 44
func(43) = 44
func(number=43) = 44
Payload constructor
Payload copy constructor
Payload move constructor
Payload destructor
Payload destructor
Payload destructor
### Payload @ 0x7ffdcee09c80 created via default constructor
### Payload @ 0x7ffdcee09c88 created via copy constructor
### Payload @ 0xb54500 created via move constructor
### Payload @ 0x7ffdcee09c88 destroyed
### Payload @ 0x7ffdcee09c80 destroyed
### Payload @ 0xb54500 destroyed
Payload instances not destroyed: 0
Copy constructions: 1
Move constructions: True
argument matches dummy_function
eval(1) = 2
roundtrip..
......@@ -36,6 +39,7 @@ could not convert to a function pointer.
All OK!
could not convert to a function pointer.
All OK!
test_callback3(arg0: Callable[[int], int]) -> None
test_callback4() -> Callable[[int], int]
......@@ -8,32 +8,25 @@
BSD-style license that can be found in the LICENSE file.
*/
#include <unordered_map>
#include <list>
#include "example.h"
#include "constructor-stats.h"
class ExampleMandA {
public:
ExampleMandA() {
cout << "Called ExampleMandA default constructor.." << endl;
}
ExampleMandA(int value) : value(value) {
cout << "Called ExampleMandA constructor with value " << value << ".." << endl;
}
ExampleMandA(const ExampleMandA &e) : value(e.value) {
cout << "Called ExampleMandA copy constructor with value " << value << ".." << endl;
}
ExampleMandA(ExampleMandA &&e) : value(e.value) {
cout << "Called ExampleMandA move constructor with value " << value << ".." << endl;
e.value = 0;
}
~ExampleMandA() {
cout << "Called ExampleMandA destructor (" << value << ")" << endl;
}
ExampleMandA() { print_default_created(this); }
ExampleMandA(int value) : value(value) { print_created(this, value); }
ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); }
ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); }
~ExampleMandA() { print_destroyed(this); }
std::string toString() {
return "ExampleMandA[value=" + std::to_string(value) + "]";
}
void operator=(const ExampleMandA &e) { cout << "Assignment operator" << endl; value = e.value; }
void operator=(ExampleMandA &&e) { cout << "Move assignment operator" << endl; value = e.value; e.value = 0;}
void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; }
void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; }
void add1(ExampleMandA other) { value += other.value; } // passing by value
void add2(ExampleMandA &other) { value += other.value; } // passing by reference
......@@ -88,5 +81,6 @@ void init_ex_methods_and_attributes(py::module &m) {
.def("internal4", &ExampleMandA::internal4)
.def("internal5", &ExampleMandA::internal5)
.def("__str__", &ExampleMandA::toString)
.def_readwrite("value", &ExampleMandA::value);
.def_readwrite("value", &ExampleMandA::value)
;
}
......@@ -35,3 +35,16 @@ print(instance1.internal5())
print("Instance 1, direct access = %i" % instance1.value)
instance1.value = 100
print("Instance 1: " + str(instance1))
from example import ConstructorStats
cstats = ConstructorStats.get(ExampleMandA)
print("Instances not destroyed:", cstats.alive())
instance1 = instance2 = None
print("Instances not destroyed:", cstats.alive())
print("Constructor values:", cstats.values())
print("Default constructions:", cstats.default_constructions)
print("Copy constructions:", cstats.copy_constructions)
print("Move constructions:", cstats.move_constructions >= 1)
print("Copy assignments:", cstats.copy_assignments)
print("Move assignments:", cstats.move_assignments)
Called ExampleMandA default constructor..
Called ExampleMandA constructor with value 32..
Called ExampleMandA copy constructor with value 32..
Called ExampleMandA copy constructor with value 32..
Called ExampleMandA destructor (32)
Called ExampleMandA destructor (32)
### ExampleMandA @ 0x2801910 created via default constructor
### ExampleMandA @ 0x27fa780 created 32
### ExampleMandA @ 0x7fff80a98a74 created via copy constructor
### ExampleMandA @ 0x7fff80a98a78 created via copy constructor
### ExampleMandA @ 0x7fff80a98a78 destroyed
### ExampleMandA @ 0x7fff80a98a74 destroyed
Instance 1: ExampleMandA[value=320]
Instance 2: ExampleMandA[value=32]
Called ExampleMandA copy constructor with value 320..
Called ExampleMandA move constructor with value 320..
Called ExampleMandA destructor (0)
### ExampleMandA @ 0x7fff80a98a84 created via copy constructor
### ExampleMandA @ 0x2801fd0 created via move constructor
### ExampleMandA @ 0x7fff80a98a84 destroyed
ExampleMandA[value=320]
Called ExampleMandA destructor (320)
### ExampleMandA @ 0x2801fd0 destroyed
ExampleMandA[value=320]
ExampleMandA[value=320]
ExampleMandA[value=320]
......@@ -22,5 +22,13 @@ ExampleMandA[value=320]
320
Instance 1, direct access = 320
Instance 1: ExampleMandA[value=100]
Called ExampleMandA destructor (32)
Called ExampleMandA destructor (100)
Instances not destroyed: 2
### ExampleMandA @ 0x2801910 destroyed
### ExampleMandA @ 0x27fa780 destroyed
Instances not destroyed: 0
Constructor values: ['32']
Default constructions: 1
Copy constructions: 3
Move constructions: True
Copy assignments: 0
Move assignments: 0
......@@ -9,6 +9,7 @@
*/
#include "example.h"
#include "constructor-stats.h"
void submodule_func() {
std::cout << "submodule_func()" << std::endl;
......@@ -16,9 +17,10 @@ void submodule_func() {
class A {
public:
A(int v) : v(v) { std::cout << "A constructor" << std::endl; }
~A() { std::cout << "A destructor" << std::endl; }
A(const A&) { std::cout << "A copy constructor" << std::endl; }
A(int v) : v(v) { print_created(this, v); }
~A() { print_destroyed(this); }
A(const A&) { print_copy_created(this); }
A& operator=(const A &copy) { print_copy_assigned(this); v = copy.v; return *this; }
std::string toString() { return "A[" + std::to_string(v) + "]"; }
private:
int v;
......@@ -26,9 +28,10 @@ private:
class B {
public:
B() { std::cout << "B constructor" << std::endl; }
~B() { std::cout << "B destructor" << std::endl; }
B(const B&) { std::cout << "B copy constructor" << std::endl; }
B() { print_default_created(this); }
~B() { print_destroyed(this); }
B(const B&) { print_copy_created(this); }
B& operator=(const B &copy) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; }
A &get_a1() { return a1; }
A &get_a2() { return a2; }
......
......@@ -28,3 +28,16 @@ print(b.get_a2())
print(b.a2)
print(OD([(1, 'a'), (2, 'b')]))
from example import ConstructorStats
cstats = [ConstructorStats.get(A), ConstructorStats.get(B)]
print("Instances not destroyed:", [x.alive() for x in cstats])
b = None
print("Instances not destroyed:", [x.alive() for x in cstats])
print("Constructor values:", [x.values() for x in cstats])
print("Default constructions:", [x.default_constructions for x in cstats])
print("Copy constructions:", [x.copy_constructions for x in cstats])
#print("Move constructions:", [x.move_constructions >= 0 for x in cstats]) # Don't invoke any
print("Copy assignments:", [x.copy_assignments for x in cstats])
print("Move assignments:", [x.move_assignments for x in cstats])
example
example.submodule
submodule_func()
A constructor
A constructor
B constructor
### A @ 0x21a5bc0 created 1
### A @ 0x21a5bc4 created 2
### B @ 0x21a5bc0 created via default constructor
A[1]
A[1]
A[2]
A[2]
A constructor
A destructor
A constructor
A destructor
### A @ 0x20f93b0 created 42
### A @ 0x21a5bc0 assigned via copy assignment
### A @ 0x20f93b0 destroyed
### A @ 0x20f93d0 created 43
### A @ 0x21a5bc4 assigned via copy assignment
### A @ 0x20f93d0 destroyed
A[42]
A[42]
A[43]
A[43]
OrderedDict([(1, 'a'), (2, 'b')])
B destructor
A destructor
A destructor
Instances not destroyed: [2, 1]
### B @ 0x21a5bc0 destroyed
### A @ 0x21a5bc4 destroyed
### A @ 0x21a5bc0 destroyed
Instances not destroyed: [0, 0]
Constructor values: [['1', '2', '42', '43'], []]
Default constructions: [0, 1]
Copy constructions: [0, 0]
Copy assignments: [2, 0]
Move assignments: [0, 0]
......@@ -34,6 +34,8 @@ print_opaque_list(cvp.stringList)
print_void_ptr(return_void_ptr())
print_void_ptr(ExampleMandA()) # Should also work for other C++ types
from example import ConstructorStats
print("ExampleMandA still alive:", ConstructorStats.get(ExampleMandA).alive())
try:
print_void_ptr([1, 2, 3]) # This should not work
......
......@@ -6,13 +6,14 @@ Opaque list: [Element 1]
Opaque list: []
Opaque list: [Element 1, Element 3]
Got void ptr : 0x1234
Called ExampleMandA default constructor..
Got void ptr : 0x7f9ba0f3c430
Called ExampleMandA destructor (0)
### ExampleMandA @ 0x2ac5370 created via default constructor
Got void ptr : 0x2ac5370
### ExampleMandA @ 0x2ac5370 destroyed
ExampleMandA still alive: 0
Caught expected exception: Incompatible function arguments. The following argument types are supported:
1. (arg0: capsule) -> None
Invoked with: [1, 2, 3]
None
Got null str : 0x0
<example.StringList object at 0x10d3277a0>
<example.StringList object at 0x7f7ecde6fc00>
Opaque list: [some value]
......@@ -8,27 +8,28 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/operators.h>
class Vector2 {
public:
Vector2(float x, float y) : x(x), y(y) { std::cout << "Value constructor" << std::endl; }
Vector2(const Vector2 &v) : x(v.x), y(v.y) { std::cout << "Copy constructor" << std::endl; }
Vector2(Vector2 &&v) : x(v.x), y(v.y) { std::cout << "Move constructor" << std::endl; v.x = v.y = 0; }
~Vector2() { std::cout << "Destructor." << std::endl; }
Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); }
Vector2(const Vector2 &v) : x(v.x), y(v.y) { print_copy_created(this); }
Vector2(Vector2 &&v) : x(v.x), y(v.y) { print_move_created(this); v.x = v.y = 0; }
~Vector2() { print_destroyed(this); }
std::string toString() const {
return "[" + std::to_string(x) + ", " + std::to_string(y) + "]";
}
void operator=(const Vector2 &v) {
cout << "Assignment operator" << endl;
print_copy_assigned(this);
x = v.x;
y = v.y;
}
void operator=(Vector2 &&v) {
cout << "Move assignment operator" << endl;
print_move_assigned(this);
x = v.x; y = v.y; v.x = v.y = 0;
}
......@@ -51,7 +52,6 @@ private:
float x, y;
};
void init_ex_operator_overloading(py::module &m) {
py::class_<Vector2>(m, "Vector2")
.def(py::init<float, float>())
......@@ -69,7 +69,8 @@ void init_ex_operator_overloading(py::module &m) {
.def(float() - py::self)
.def(float() * py::self)
.def(float() / py::self)
.def("__str__", &Vector2::toString);
.def("__str__", &Vector2::toString)
;
m.attr("Vector") = m.attr("Vector2");
}
......@@ -25,3 +25,17 @@ v1 += v2
v1 *= 2
print("(v1+v2)*2 = " + str(v1))
from example import ConstructorStats
cstats = ConstructorStats.get(Vector2)
print("Instances not destroyed:", cstats.alive())
v1 = None
print("Instances not destroyed:", cstats.alive())
v2 = None
print("Instances not destroyed:", cstats.alive())
print("Constructor values:", cstats.values())
print("Default constructions:", cstats.default_constructions)
print("Copy constructions:", cstats.copy_constructions)
print("Move constructions:", cstats.move_constructions >= 10)
print("Copy assignments:", cstats.copy_assignments)
print("Move assignments:", cstats.move_assignments)
Value constructor
Value constructor
### Vector2 @ 0x11f7830 created [1.000000, 2.000000]
### Vector2 @ 0x11427c0 created [3.000000, -1.000000]
v1 = [1.000000, 2.000000]
v2 = [3.000000, -1.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144b8 created [4.000000, 1.000000]
### Vector2 @ 0x11f7e90 created via move constructor
### Vector2 @ 0x7ffef6b144b8 destroyed
### Vector2 @ 0x11f7e90 destroyed
v1+v2 = [4.000000, 1.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144b8 created [-2.000000, 3.000000]
### Vector2 @ 0x11f7e90 created via move constructor
### Vector2 @ 0x7ffef6b144b8 destroyed
### Vector2 @ 0x11f7e90 destroyed
v1-v2 = [-2.000000, 3.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144c8 created [-7.000000, -6.000000]
### Vector2 @ 0x1115760 created via move constructor
### Vector2 @ 0x7ffef6b144c8 destroyed
### Vector2 @ 0x1115760 destroyed
v1-8 = [-7.000000, -6.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144c8 created [9.000000, 10.000000]
### Vector2 @ 0x1115760 created via move constructor
### Vector2 @ 0x7ffef6b144c8 destroyed
### Vector2 @ 0x1115760 destroyed
v1+8 = [9.000000, 10.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144b8 created [8.000000, 16.000000]
### Vector2 @ 0x1115760 created via move constructor
### Vector2 @ 0x7ffef6b144b8 destroyed
### Vector2 @ 0x1115760 destroyed
v1*8 = [8.000000, 16.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144a8 created [0.125000, 0.250000]
### Vector2 @ 0x112f150 created via move constructor
### Vector2 @ 0x7ffef6b144a8 destroyed
### Vector2 @ 0x112f150 destroyed
v1/8 = [0.125000, 0.250000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144f8 created [7.000000, 6.000000]
### Vector2 @ 0x112f1b0 created via move constructor
### Vector2 @ 0x7ffef6b144f8 destroyed
### Vector2 @ 0x112f1b0 destroyed
8-v1 = [7.000000, 6.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144f8 created [9.000000, 10.000000]
### Vector2 @ 0x112f1b0 created via move constructor
### Vector2 @ 0x7ffef6b144f8 destroyed
### Vector2 @ 0x112f1b0 destroyed
8+v1 = [9.000000, 10.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144e8 created [8.000000, 16.000000]
### Vector2 @ 0x112f230 created via move constructor
### Vector2 @ 0x7ffef6b144e8 destroyed
### Vector2 @ 0x112f230 destroyed
8*v1 = [8.000000, 16.000000]
Value constructor
Move constructor
Destructor.
Destructor.
### Vector2 @ 0x7ffef6b144d8 created [8.000000, 4.000000]
### Vector2 @ 0x11fb360 created via move constructor
### Vector2 @ 0x7ffef6b144d8 destroyed
### Vector2 @ 0x11fb360 destroyed
8/v1 = [8.000000, 4.000000]
(v1+v2)*2 = [8.000000, 2.000000]
Destructor.
Destructor.
Instances not destroyed: 2
### Vector2 @ 0x11f7830 destroyed
Instances not destroyed: 1
### Vector2 @ 0x11427c0 destroyed
Instances not destroyed: 0
Constructor values: ['[1.000000, 2.000000]', '[3.000000, -1.000000]', '[4.000000, 1.000000]', '[-2.000000, 3.000000]', '[-7.000000, -6.000000]', '[9.000000, 10.000000]', '[8.000000, 16.000000]', '[0.125000, 0.250000]', '[7.000000, 6.000000]', '[9.000000, 10.000000]', '[8.000000, 16.000000]', '[8.000000, 4.000000]']
Default constructions: 0
Copy constructions: 0
Move constructions: True
Copy assignments: 0
Move assignments: 0
......@@ -9,6 +9,7 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/stl.h>
#ifdef _WIN32
......@@ -19,11 +20,11 @@
class ExamplePythonTypes {
public:
static ExamplePythonTypes *new_instance() {
return new ExamplePythonTypes();
}
~ExamplePythonTypes() {
std::cout << "Destructing ExamplePythonTypes" << std::endl;
auto *ptr = new ExamplePythonTypes();
print_created(ptr, "via new_instance");
return ptr;
}
~ExamplePythonTypes() { print_destroyed(this); }
/* Create and return a Python dictionary */
py::dict get_dict() {
......@@ -168,5 +169,6 @@ void init_ex_python_types(py::module &m) {
.def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception")
.def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance")
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)");
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)")
;
}
......@@ -65,3 +65,10 @@ print("__name__(example.ExamplePythonTypes) = %s" % ExamplePythonTypes.__name__)
print("__module__(example.ExamplePythonTypes) = %s" % ExamplePythonTypes.__module__)
print("__name__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__name__)
print("__module__(example.ExamplePythonTypes.get_set) = %s" % ExamplePythonTypes.get_set.__module__)
from example import ConstructorStats
cstats = ConstructorStats.get(ExamplePythonTypes)
print("Instances not destroyed:", cstats.alive())
instance = None
print("Instances not destroyed:", cstats.alive())
......@@ -2,6 +2,7 @@
5
example.ExamplePythonTypes: No constructor defined!
can't set attribute
### ExamplePythonTypes @ 0x1045b80 created via new_instance
key: key2, value=value2
key: key, value=value
key: key, value=value
......@@ -134,4 +135,6 @@ __name__(example.ExamplePythonTypes) = ExamplePythonTypes
__module__(example.ExamplePythonTypes) = example
__name__(example.ExamplePythonTypes.get_set) = get_set
__module__(example.ExamplePythonTypes.get_set) = example
Destructing ExamplePythonTypes
Instances not destroyed: 1
### ExamplePythonTypes @ 0x1045b80 destroyed
Instances not destroyed: 0
......@@ -9,51 +9,55 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/operators.h>
#include <pybind11/stl.h>
class Sequence {
public:
Sequence(size_t size) : m_size(size) {
std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
print_created(this, "of size", m_size);
m_data = new float[size];
memset(m_data, 0, sizeof(float) * size);
}
Sequence(const std::vector<float> &value) : m_size(value.size()) {
std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
print_created(this, "of size", m_size, "from std::vector");
m_data = new float[m_size];
memcpy(m_data, &value[0], sizeof(float) * m_size);
}
Sequence(const Sequence &s) : m_size(s.m_size) {
std::cout << "Copy constructor: Creating a sequence with " << m_size << " entries" << std::endl;
print_copy_created(this);
m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size);
}
Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) {
std::cout << "Move constructor: Creating a sequence with " << m_size << " entries" << std::endl;
print_move_created(this);
s.m_size = 0;
s.m_data = nullptr;
}
~Sequence() {
std::cout << "Freeing a sequence with " << m_size << " entries" << std::endl;
print_destroyed(this);
delete[] m_data;
}
Sequence &operator=(const Sequence &s) {
std::cout << "Assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
delete[] m_data;
m_size = s.m_size;
m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size);
if (&s != this) {
delete[] m_data;
m_size = s.m_size;
m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size);
}
print_copy_assigned(this);
return *this;
}
Sequence &operator=(Sequence &&s) {
std::cout << "Move assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
if (&s != this) {
delete[] m_data;
m_size = s.m_size;
......@@ -61,6 +65,9 @@ public:
s.m_size = 0;
s.m_data = nullptr;
}
print_move_assigned(this);
return *this;
}
......
......@@ -28,3 +28,19 @@ rev[0::2] = Sequence([2.0, 2.0, 2.0])
for i in rev:
print(i, end=' ')
print('')
from example import ConstructorStats
cstats = ConstructorStats.get(Sequence)
print("Instances not destroyed:", cstats.alive())
s = None
print("Instances not destroyed:", cstats.alive())
rev = None
print("Instances not destroyed:", cstats.alive())
rev2 = None
print("Instances not destroyed:", cstats.alive())
print("Constructor values:", cstats.values())
print("Default constructions:", cstats.default_constructions)
print("Copy constructions:", cstats.copy_constructions)
print("Move constructions:", cstats.move_constructions >= 1)
print("Copy assignments:", cstats.copy_assignments)
print("Move assignments:", cstats.move_assignments)
Value constructor: Creating a sequence with 5 entries
s = <example.Sequence object at 0x10c786c70>
### Sequence @ 0x1535b00 created of size 5
s = <example.Sequence object at 0x7efc73cfa4e0>
len(s) = 5
s[0], s[3] = 0.000000 0.000000
12.34 in s: False
12.34 in s: True
s[0], s[3] = 12.340000 56.779999
Value constructor: Creating a sequence with 5 entries
Move constructor: Creating a sequence with 5 entries
Freeing a sequence with 0 entries
Value constructor: Creating a sequence with 5 entries
### Sequence @ 0x7fff22a45068 created of size 5
### Sequence @ 0x1538b90 created via move constructor
### Sequence @ 0x7fff22a45068 destroyed
### Sequence @ 0x1538bf0 created of size 5
rev[0], rev[1], rev[2], rev[3], rev[4] = 0.000000 56.779999 0.000000 0.000000 12.340000
0.0 56.7799987793 0.0 0.0 12.3400001526
0.0 56.7799987793 0.0 0.0 12.3400001526
0.0 56.779998779296875 0.0 0.0 12.34000015258789
0.0 56.779998779296875 0.0 0.0 12.34000015258789
True
Value constructor: Creating a sequence with 3 entries
Freeing a sequence with 3 entries
2.0 56.7799987793 2.0 0.0 2.0
Freeing a sequence with 5 entries
Freeing a sequence with 5 entries
Freeing a sequence with 5 entries
### Sequence @ 0x153c4b0 created of size 3 from std::vector
### Sequence @ 0x153c4b0 destroyed
2.0 56.779998779296875 2.0 0.0 2.0
Instances not destroyed: 3
### Sequence @ 0x1535b00 destroyed
Instances not destroyed: 2
### Sequence @ 0x1538b90 destroyed
Instances not destroyed: 1
### Sequence @ 0x1538bf0 destroyed
Instances not destroyed: 0
Constructor values: ['of size', '5', 'of size', '5', 'of size', '5', 'of size', '3', 'from std::vector']
Default constructions: 0
Copy constructions: 0
Move constructions: True
Copy assignments: 0
Move assignments: 0
......@@ -15,7 +15,7 @@
class MyObject1 : public Object {
public:
MyObject1(int value) : value(value) {
std::cout << toString() << " constructor" << std::endl;
print_created(this, toString());
}
std::string toString() const {
......@@ -24,7 +24,7 @@ public:
protected:
virtual ~MyObject1() {
std::cout << toString() << " destructor" << std::endl;
print_destroyed(this);
}
private:
......@@ -35,7 +35,7 @@ private:
class MyObject2 {
public:
MyObject2(int value) : value(value) {
std::cout << toString() << " constructor" << std::endl;
print_created(this, toString());
}
std::string toString() const {
......@@ -43,7 +43,7 @@ public:
}
virtual ~MyObject2() {
std::cout << toString() << " destructor" << std::endl;
print_destroyed(this);
}
private:
......@@ -54,7 +54,7 @@ private:
class MyObject3 : public std::enable_shared_from_this<MyObject3> {
public:
MyObject3(int value) : value(value) {
std::cout << toString() << " constructor" << std::endl;
print_created(this, toString());
}
std::string toString() const {
......@@ -62,7 +62,7 @@ public:
}
virtual ~MyObject3() {
std::cout << toString() << " destructor" << std::endl;
print_destroyed(this);
}
private:
......@@ -144,4 +144,7 @@ void init_ex_smart_ptr(py::module &m) {
m.def("print_myobject3_4", &print_myobject3_4);
py::implicitly_convertible<py::int_, MyObject1>();
// Expose constructor stats for the ref type
m.def("cstats_ref", &ConstructorStats::get<ref_tag>);
}
......@@ -68,3 +68,18 @@ for o in [MyObject3(9), make_myobject3_1(), make_myobject3_2()]:
print_myobject3_2(o)
print_myobject3_3(o)
print_myobject3_4(o)
from example import ConstructorStats, cstats_ref, Object
cstats = [ConstructorStats.get(Object), ConstructorStats.get(MyObject1),
ConstructorStats.get(MyObject2), ConstructorStats.get(MyObject3),
cstats_ref()]
print("Instances not destroyed:", [x.alive() for x in cstats])
o = None
print("Instances not destroyed:", [x.alive() for x in cstats])
print("Object value constructions:", [x.values() for x in cstats])
print("Default constructions:", [x.default_constructions for x in cstats])
print("Copy constructions:", [x.copy_constructions for x in cstats])
#print("Move constructions:", [x.move_constructions >= 0 for x in cstats]) # Doesn't invoke any
print("Copy assignments:", [x.copy_assignments for x in cstats])
print("Move assignments:", [x.move_assignments for x in cstats])
......@@ -8,18 +8,16 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/functional.h>
/* This is an example class that we'll want to be able to extend from Python */
class ExampleVirt {
public:
ExampleVirt(int state) : state(state) {
cout << "Constructing ExampleVirt.." << endl;
}
~ExampleVirt() {
cout << "Destructing ExampleVirt.." << endl;
}
ExampleVirt(int state) : state(state) { print_created(this, state); }
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
ExampleVirt(ExampleVirt &&e) : state(e.state) { print_move_created(this); e.state = 0; }
~ExampleVirt() { print_destroyed(this); }
virtual int run(int value) {
std::cout << "Original implementation of ExampleVirt::run(state=" << state
......@@ -71,8 +69,8 @@ public:
class NonCopyable {
public:
NonCopyable(int a, int b) : value{new int(a*b)} {}
NonCopyable(NonCopyable &&) = default;
NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); }
NonCopyable(NonCopyable &&o) { value = std::move(o.value); print_move_created(this); }
NonCopyable(const NonCopyable &) = delete;
NonCopyable() = delete;
void operator=(const NonCopyable &) = delete;
......@@ -80,7 +78,7 @@ public:
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; }
~NonCopyable() { print_destroyed(this); }
private:
std::unique_ptr<int> value;
......@@ -90,11 +88,11 @@ private:
// 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; }
Movable(int a, int b) : value{a+b} { print_created(this, a, b); }
Movable(const Movable &m) { value = m.value; print_copy_created(this); }
Movable(Movable &&m) { value = std::move(m.value); print_move_created(this); }
int get_value() const { return value; }
~Movable() { std::cout << "Movable destructor @ " << this << "; value = " << get_value() << std::endl; }
~Movable() { print_destroyed(this); }
private:
int value;
};
......@@ -305,5 +303,6 @@ void init_ex_virtual_functions(py::module &m) {
m.def("runExampleVirtBool", &runExampleVirtBool);
m.def("runExampleVirtVirtual", &runExampleVirtVirtual);
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
initialize_inherited_virtuals(m);
}
......@@ -37,8 +37,6 @@ print(runExampleVirt(ex12p, 20))
print(runExampleVirtBool(ex12p))
runExampleVirtVirtual(ex12p)
sys.stdout.flush()
class VI_AR(A_Repeat):
def unlucky_number(self):
return 99
......@@ -122,3 +120,16 @@ try:
except RuntimeError as e:
# Don't print the exception message here because it differs under debug/non-debug mode
print("Caught expected exception")
from example import ConstructorStats
del ex12
del ex12p
del obj
del ncv1
del ncv2
cstats = [ConstructorStats.get(ExampleVirt), ConstructorStats.get(NonCopyable), ConstructorStats.get(Movable)]
print("Instances not destroyed:", [x.alive() for x in cstats])
print("Constructor values:", [x.values() for x in cstats])
print("Copy constructions:", [x.copy_constructions for x in cstats])
print("Move constructions:", [cstats[i].move_constructions >= 1 for i in range(1, len(cstats))])
Constructing ExampleVirt..
### ExampleVirt @ 0x2073a90 created 10
Original implementation of ExampleVirt::run(state=10, value=20)
30
Caught expected exception: Tried to call pure virtual function "ExampleVirt::pure_virtual"
Constructing ExampleVirt..
### ExampleVirt @ 0x2076a00 created 11
ExtendedExampleVirt::run(20), calling parent..
Original implementation of ExampleVirt::run(state=11, value=21)
32
......@@ -78,20 +78,29 @@ VI_DT says: quack quack quack
Unlucky = 1234
Lucky = -4.25
2^2 * 3^2 =
NonCopyable destructor @ 0x1a6c3f0; value = (null)
### NonCopyable @ 0x207df10 created 4 9
### NonCopyable @ 0x7ffcfe866228 created via move constructor
### NonCopyable @ 0x207df10 destroyed
36
NonCopyable destructor @ 0x7ffc6d1fbaa8; value = 36
### NonCopyable @ 0x7ffcfe866228 destroyed
4 + 5 =
Movable @ 0x7ffc6d1fbacc copy constructor
### Movable @ 0x207e230 created 4 5
### Movable @ 0x7ffcfe86624c created via copy constructor
9
Movable destructor @ 0x7ffc6d1fbacc; value = 9
### Movable @ 0x7ffcfe86624c destroyed
7 + 7 =
Movable @ 0x7ffc6d1fbacc move constructor
Movable destructor @ 0x1a6c4d0; value = 14
### Movable @ 0x20259e0 created 7 7
### Movable @ 0x7ffcfe86624c created via move constructor
### Movable @ 0x20259e0 destroyed
14
Movable destructor @ 0x7ffc6d1fbacc; value = 14
### Movable @ 0x7ffcfe86624c destroyed
### NonCopyable @ 0x2025a00 created 9 9
Caught expected exception
NonCopyable destructor @ 0x29a64b0; value = 81
Movable destructor @ 0x1a6c410; value = 9
Destructing ExampleVirt..
Destructing ExampleVirt..
### ExampleVirt @ 0x2073a90 destroyed
### ExampleVirt @ 0x2076a00 destroyed
### Movable @ 0x207e230 destroyed
### NonCopyable @ 0x2025a00 destroyed
Instances not destroyed: [0, 0, 0]
Constructor values: [['10', '11'], ['4', '9', '9', '9'], ['4', '5', '7', '7']]
Copy constructions: [0, 0, 1]
Move constructions: [True, True]
......@@ -8,6 +8,7 @@
*/
#include "example.h"
#include "constructor-stats.h"
void init_ex_methods_and_attributes(py::module &);
void init_ex_python_types(py::module &);
......@@ -34,9 +35,24 @@ void init_issues(py::module &);
void init_eigen(py::module &);
#endif
void bind_ConstructorStats(py::module &m) {
py::class_<ConstructorStats>(m, "ConstructorStats")
.def("alive", &ConstructorStats::alive)
.def("values", &ConstructorStats::values)
.def_readwrite("default_constructions", &ConstructorStats::default_constructions)
.def_readwrite("copy_assignments", &ConstructorStats::copy_assignments)
.def_readwrite("move_assignments", &ConstructorStats::move_assignments)
.def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
.def_readwrite("move_constructions", &ConstructorStats::move_constructions)
.def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal)
;
}
PYBIND11_PLUGIN(example) {
py::module m("example", "pybind example plugin");
bind_ConstructorStats(m);
init_ex_methods_and_attributes(m);
init_ex_python_types(m);
init_ex_operator_overloading(m);
......
......@@ -8,11 +8,18 @@
*/
#include "example.h"
#include "constructor-stats.h"
#include <pybind11/stl.h>
#include <pybind11/operators.h>
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
#define TRACKERS(CLASS) CLASS() { print_default_created(this); } ~CLASS() { print_destroyed(this); }
struct NestABase { int value = -2; TRACKERS(NestABase) };
struct NestA : NestABase { int value = 3; NestA& operator+=(int i) { value += i; return *this; } TRACKERS(NestA) };
struct NestB { NestA a; int value = 4; NestB& operator-=(int i) { value -= i; return *this; } TRACKERS(NestB) };
struct NestC { NestB b; int value = 5; NestC& operator*=(int i) { value *= i; return *this; } TRACKERS(NestC) };
void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues");
......@@ -159,12 +166,6 @@ void init_issues(py::module &m) {
;
// Issue #328: first member in a class can't be used in operators
#define TRACKERS(CLASS) CLASS() { std::cout << #CLASS "@" << this << " constructor\n"; } \
~CLASS() { std::cout << #CLASS "@" << this << " destructor\n"; }
struct NestABase { int value = -2; TRACKERS(NestABase) };
struct NestA : NestABase { int value = 3; NestA& operator+=(int i) { value += i; return *this; } TRACKERS(NestA) };
struct NestB { NestA a; int value = 4; NestB& operator-=(int i) { value -= i; return *this; } TRACKERS(NestB) };
struct NestC { NestB b; int value = 5; NestC& operator*=(int i) { value *= i; return *this; } TRACKERS(NestC) };
py::class_<NestABase>(m2, "NestABase").def(py::init<>()).def_readwrite("value", &NestABase::value);
py::class_<NestA>(m2, "NestA").def(py::init<>()).def(py::self += int())
.def("as_base", [](NestA &a) -> NestABase& { return (NestABase&) a; }, py::return_value_policy::reference_internal);
......
......@@ -24,15 +24,15 @@ Failed as expected: Incompatible constructor arguments. The following argument t
1. example.issues.StrIssue(arg0: int)
2. example.issues.StrIssue()
Invoked with: no, such, constructor
NestABase@0x1152940 constructor
NestA@0x1152940 constructor
NestABase@0x11f9350 constructor
NestA@0x11f9350 constructor
NestB@0x11f9350 constructor
NestABase@0x112d0d0 constructor
NestA@0x112d0d0 constructor
NestB@0x112d0d0 constructor
NestC@0x112d0d0 constructor
### NestABase @ 0x15eb630 created via default constructor
### NestA @ 0x15eb630 created via default constructor
### NestABase @ 0x1704000 created via default constructor
### NestA @ 0x1704000 created via default constructor
### NestB @ 0x1704000 created via default constructor
### NestABase @ 0x1633110 created via default constructor
### NestA @ 0x1633110 created via default constructor
### NestB @ 0x1633110 created via default constructor
### NestC @ 0x1633110 created via default constructor
13
103
1003
......@@ -43,13 +43,13 @@ NestC@0x112d0d0 constructor
42
-2
42
NestC@0x112d0d0 destructor
NestB@0x112d0d0 destructor
NestA@0x112d0d0 destructor
NestABase@0x112d0d0 destructor
### NestC @ 0x1633110 destroyed
### NestB @ 0x1633110 destroyed
### NestA @ 0x1633110 destroyed
### NestABase @ 0x1633110 destroyed
42
NestA@0x1152940 destructor
NestABase@0x1152940 destructor
NestB@0x11f9350 destructor
NestA@0x11f9350 destructor
NestABase@0x11f9350 destructor
### NestA @ 0x15eb630 destroyed
### NestABase @ 0x15eb630 destroyed
### NestB @ 0x1704000 destroyed
### NestA @ 0x1704000 destroyed
### NestABase @ 0x1704000 destroyed
......@@ -2,15 +2,16 @@
#define __OBJECT_H
#include <atomic>
#include "constructor-stats.h"
/// Reference counted object base class
class Object {
public:
/// Default constructor
Object() { }
Object() { print_default_created(this); }
/// Copy constructor
Object(const Object &) : m_refCount(0) {}
Object(const Object &) : m_refCount(0) { print_copy_created(this); }
/// Return the current reference count
int getRefCount() const { return m_refCount; };
......@@ -37,11 +38,17 @@ protected:
/** \brief Virtual protected deconstructor.
* (Will only be called by \ref ref)
*/
virtual ~Object() { }
virtual ~Object() { print_destroyed(this); }
private:
mutable std::atomic<int> m_refCount { 0 };
};
// Tag class used to track constructions of ref objects. When we track constructors, below, we
// track and print out the actual class (e.g. ref<MyObject>), and *also* add a fake tracker for
// ref_tag. This lets us check that the total number of ref<Anything> constructors/destructors is
// correct without having to check each individual ref<Whatever> type individually.
class ref_tag {};
/**
* \brief Reference counting helper
*
......@@ -55,37 +62,43 @@ private:
template <typename T> class ref {
public:
/// Create a nullptr reference
ref() : m_ptr(nullptr) { std::cout << "Created empty ref" << std::endl; }
ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }
/// Construct a reference from a pointer
ref(T *ptr) : m_ptr(ptr) {
std::cout << "Initialized ref from pointer " << ptr<< std::endl;
if (m_ptr) ((Object *) m_ptr)->incRef();
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
}
/// Copy constructor
ref(const ref &r) : m_ptr(r.m_ptr) {
std::cout << "Initialized ref from ref " << r.m_ptr << std::endl;
if (m_ptr)
((Object *) m_ptr)->incRef();
print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this);
}
/// Move constructor
ref(ref &&r) : m_ptr(r.m_ptr) {
std::cout << "Initialized ref with move from ref " << r.m_ptr << std::endl;
r.m_ptr = nullptr;
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
}
/// Destroy this reference
~ref() {
std::cout << "Destructing ref " << m_ptr << std::endl;
if (m_ptr)
((Object *) m_ptr)->decRef();
print_destroyed(this); track_destroyed((ref_tag*) this);
}
/// Move another reference into the current one
ref& operator=(ref&& r) {
std::cout << "Move-assigning ref " << r.m_ptr << std::endl;
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
if (*this == r)
return *this;
if (m_ptr)
......@@ -97,7 +110,8 @@ public:
/// Overwrite this reference with another reference
ref& operator=(const ref& r) {
std::cout << "Assigning ref " << r.m_ptr << std::endl;
print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this);
if (m_ptr == r.m_ptr)
return *this;
if (m_ptr)
......@@ -110,7 +124,8 @@ public:
/// Overwrite this reference with a pointer to another object
ref& operator=(T *ptr) {
std::cout << "Assigning ptr " << ptr << " to ref" << std::endl;
print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer");
if (m_ptr == ptr)
return *this;
if (m_ptr)
......
......@@ -9,14 +9,16 @@ remove_long_marker = re.compile(r'([0-9])L')
remove_hex = re.compile(r'0x[0-9a-fA-F]+')
shorten_floats = re.compile(r'([1-9][0-9]*\.[0-9]{4})[0-9]*')
relaxed = False
def sanitize(lines):
lines = lines.split('\n')
for i in range(len(lines)):
line = lines[i]
if line.startswith(" |"):
line = ""
if line.startswith("### "):
# Constructor/destructor output. Useful for example, but unreliable across compilers;
# testing of proper construction/destruction occurs with ConstructorStats mechanism instead
line = ""
line = remove_unicode_marker.sub(r'\1', line)
line = remove_long_marker.sub(r'\1', line)
line = remove_hex.sub(r'0', line)
......@@ -28,13 +30,6 @@ def sanitize(lines):
line = line.replace('example.EMode', 'EMode')
line = line.replace('method of builtins.PyCapsule instance', '')
line = line.strip()
if relaxed:
lower = line.lower()
# The precise pattern of allocations and deallocations is dependent on the compiler
# and optimization level, so we unfortunately can't reliably check it in this kind of test case
if 'constructor' in lower or 'destructor' in lower \
or 'ref' in lower or 'freeing' in lower:
line = ""
lines[i] = line
return '\n'.join(sorted([l for l in lines if l != ""]))
......@@ -44,16 +39,12 @@ if path != '':
os.chdir(path)
if len(sys.argv) < 2:
print("Syntax: %s [--relaxed] <test name>" % sys.argv[0])
print("Syntax: %s <test name>" % sys.argv[0])
exit(0)
if len(sys.argv) == 3 and sys.argv[1] == '--relaxed':
del sys.argv[1]
relaxed = True
name = sys.argv[1]
try:
output_bytes = subprocess.check_output([sys.executable, name + ".py"],
output_bytes = subprocess.check_output([sys.executable, "-u", name + ".py"],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
if e.returncode == 99:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment