Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
#pragma once
|
|
|
|
/*
|
2016-08-12 11:50:00 +00:00
|
|
|
tests/constructor_stats.h -- framework for printing and tracking object
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
instance lifetimes in example/test code.
|
|
|
|
|
|
|
|
Copyright (c) 2016 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.
|
|
|
|
|
|
|
|
This header provides a few useful tools for writing examples or tests that want to check and/or
|
|
|
|
display object instance lifetimes. It requires that you include this header and add the following
|
|
|
|
function calls to constructors:
|
|
|
|
|
|
|
|
class MyClass {
|
|
|
|
MyClass() { ...; print_default_created(this); }
|
|
|
|
~MyClass() { ...; print_destroyed(this); }
|
|
|
|
MyClass(const MyClass &c) { ...; print_copy_created(this); }
|
|
|
|
MyClass(MyClass &&c) { ...; print_move_created(this); }
|
|
|
|
MyClass(int a, int b) { ...; print_created(this, a, b); }
|
|
|
|
MyClass &operator=(const MyClass &c) { ...; print_copy_assigned(this); }
|
|
|
|
MyClass &operator=(MyClass &&c) { ...; print_move_assigned(this); }
|
|
|
|
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2017-01-31 16:28:29 +00:00
|
|
|
You can find various examples of these in several of the existing testing .cpp files. (Of course
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
you don't need to add any of the above constructors/operators that you don't actually have, except
|
|
|
|
for the destructor).
|
|
|
|
|
|
|
|
Each of these will print an appropriate message such as:
|
|
|
|
|
|
|
|
### MyClass @ 0x2801910 created via default constructor
|
|
|
|
### MyClass @ 0x27fa780 created 100 200
|
|
|
|
### MyClass @ 0x2801910 destroyed
|
|
|
|
### MyClass @ 0x27fa780 destroyed
|
|
|
|
|
|
|
|
You can also include extra arguments (such as the 100, 200 in the output above, coming from the
|
|
|
|
value constructor) for all of the above methods which will be included in the output.
|
|
|
|
|
|
|
|
For testing, each of these also keeps track the created instances and allows you to check how many
|
|
|
|
of the various constructors have been invoked from the Python side via code such as:
|
|
|
|
|
2017-01-31 16:28:29 +00:00
|
|
|
from pybind11_tests import ConstructorStats
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
cstats = ConstructorStats.get(MyClass)
|
|
|
|
print(cstats.alive())
|
|
|
|
print(cstats.default_constructions)
|
|
|
|
|
|
|
|
Note that `.alive()` should usually be the first thing you call as it invokes Python's garbage
|
|
|
|
collector to actually destroy objects that aren't yet referenced.
|
|
|
|
|
|
|
|
For everything except copy and move constructors and destructors, any extra values given to the
|
|
|
|
print_...() function is stored in a class-specific values list which you can retrieve and inspect
|
|
|
|
from the ConstructorStats instance `.values()` method.
|
|
|
|
|
|
|
|
In some cases, when you need to track instances of a C++ class not registered with pybind11, you
|
|
|
|
need to add a function returning the ConstructorStats for the C++ class; this can be done with:
|
|
|
|
|
Don't construct unique_ptr around unowned pointers (#478)
If we need to initialize a holder around an unowned instance, and the
holder type is non-copyable (i.e. a unique_ptr), we currently construct
the holder type around the value pointer, but then never actually
destruct the holder: the holder destructor is called only for the
instance that actually has `inst->owned = true` set.
This seems no pointer, however, in creating such a holder around an
unowned instance: we never actually intend to use anything that the
unique_ptr gives us: and, in fact, do not want the unique_ptr (because
if it ever actually got destroyed, it would cause destruction of the
wrapped pointer, despite the fact that that wrapped pointer isn't
owned).
This commit changes the logic to only create a unique_ptr holder if we
actually own the instance, and to destruct via the constructed holder
whenever we have a constructed holder--which will now only be the case
for owned-unique-holder or shared-holder types.
Other changes include:
* Added test for non-movable holder constructor/destructor counts
The three alive assertions now pass, before #478 they fail with counts
of 2/2/1 respectively, because of the unique_ptr that we don't want and
don't destroy (because we don't *want* its destructor to run).
* Return cstats reference; fix ConstructStats doc
Small cleanup to the #478 test code, and fix to the ConstructStats
documentation (the static method definition should use `reference` not
`reference_internal`).
* Rename inst->constructed to inst->holder_constructed
This makes it clearer exactly what it's referring to.
2016-11-06 18:12:48 +00:00
|
|
|
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference)
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
|
|
|
|
Finally, you can suppress the output messages, but keep the constructor tracking (for
|
|
|
|
inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
|
|
|
|
`track_copy_created(this)`).
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2016-08-12 11:50:00 +00:00
|
|
|
#include "pybind11_tests.h"
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
#include <unordered_map>
|
|
|
|
#include <list>
|
|
|
|
#include <typeindex>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
class ConstructorStats {
|
|
|
|
protected:
|
|
|
|
std::unordered_map<void*, int> _instances; // Need a map rather than set because members can shared address with parents
|
|
|
|
std::list<std::string> _values; // Used to track values (e.g. of value constructors)
|
|
|
|
public:
|
|
|
|
int default_constructions = 0;
|
|
|
|
int copy_constructions = 0;
|
|
|
|
int move_constructions = 0;
|
|
|
|
int copy_assignments = 0;
|
|
|
|
int move_assignments = 0;
|
|
|
|
|
|
|
|
void copy_created(void *inst) {
|
|
|
|
created(inst);
|
|
|
|
copy_constructions++;
|
|
|
|
}
|
2016-12-16 14:00:46 +00:00
|
|
|
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
void move_created(void *inst) {
|
|
|
|
created(inst);
|
|
|
|
move_constructions++;
|
|
|
|
}
|
2016-12-16 14:00:46 +00:00
|
|
|
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
void default_created(void *inst) {
|
|
|
|
created(inst);
|
|
|
|
default_constructions++;
|
|
|
|
}
|
2016-12-16 14:00:46 +00:00
|
|
|
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
void created(void *inst) {
|
|
|
|
++_instances[inst];
|
2016-12-16 14:00:46 +00:00
|
|
|
}
|
|
|
|
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
void destroyed(void *inst) {
|
|
|
|
if (--_instances[inst] < 0)
|
2016-12-16 14:00:46 +00:00
|
|
|
throw std::runtime_error("cstats.destroyed() called with unknown "
|
|
|
|
"instance; potential double-destruction "
|
|
|
|
"or a missing cstats.created()");
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 14:00:46 +00:00
|
|
|
static void gc() {
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
// Force garbage collection to ensure any pending destructors are invoked:
|
2016-12-16 14:00:46 +00:00
|
|
|
#if defined(PYPY_VERSION)
|
|
|
|
PyObject *globals = PyEval_GetGlobals();
|
|
|
|
PyObject *result = PyRun_String(
|
|
|
|
"import gc\n"
|
|
|
|
"for i in range(2):"
|
|
|
|
" gc.collect()\n",
|
|
|
|
Py_file_input, globals, globals);
|
|
|
|
if (result == nullptr)
|
|
|
|
throw py::error_already_set();
|
|
|
|
Py_DECREF(result);
|
|
|
|
#else
|
2020-10-03 17:38:03 +00:00
|
|
|
py::module_::import("gc").attr("collect")();
|
2016-12-16 14:00:46 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int alive() {
|
|
|
|
gc();
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
int total = 0;
|
2016-12-16 14:00:46 +00:00
|
|
|
for (const auto &p : _instances)
|
|
|
|
if (p.second > 0)
|
|
|
|
total += p.second;
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
void value() {} // Recursion terminator
|
|
|
|
// Takes one or more values, converts them to strings, then stores them.
|
|
|
|
template <typename T, typename... Tmore> void value(const T &v, Tmore &&...args) {
|
|
|
|
std::ostringstream oss;
|
|
|
|
oss << v;
|
|
|
|
_values.push_back(oss.str());
|
|
|
|
value(std::forward<Tmore>(args)...);
|
|
|
|
}
|
2016-08-12 11:50:00 +00:00
|
|
|
|
|
|
|
// Move out stored values
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
py::list values() {
|
|
|
|
py::list l;
|
|
|
|
for (const auto &v : _values) l.append(py::cast(v));
|
2016-08-12 11:50:00 +00:00
|
|
|
_values.clear();
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets constructor stats from a C++ type index
|
|
|
|
static ConstructorStats& get(std::type_index type) {
|
|
|
|
static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
|
|
|
|
return all_cstats[type];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets constructor stats from a C++ type
|
|
|
|
template <typename T> static ConstructorStats& get() {
|
2016-12-16 14:00:46 +00:00
|
|
|
#if defined(PYPY_VERSION)
|
|
|
|
gc();
|
|
|
|
#endif
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
return get(typeid(T));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets constructor stats from a Python class
|
|
|
|
static ConstructorStats& get(py::object class_) {
|
|
|
|
auto &internals = py::detail::get_internals();
|
|
|
|
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
|
|
|
try {
|
2017-02-23 02:36:09 +00:00
|
|
|
auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
for (auto &p : internals.registered_types_cpp) {
|
|
|
|
if (p.second == type_info) {
|
|
|
|
if (t1) {
|
|
|
|
t2 = &p.first;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
t1 = &p.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-24 07:33:05 +00:00
|
|
|
catch (const std::out_of_range&) {}
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
|
|
|
auto &cs1 = get(*t1);
|
|
|
|
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
|
|
|
// has more constructions (typically one or the other will be 0)
|
|
|
|
if (t2) {
|
|
|
|
auto &cs2 = get(*t2);
|
|
|
|
int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size();
|
|
|
|
int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size();
|
|
|
|
if (cs2_total > cs1_total) return cs2;
|
|
|
|
}
|
|
|
|
return cs1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// To track construction/destruction, you need to call these methods from the various
|
|
|
|
// constructors/operators. The ones that take extra values record the given values in the
|
|
|
|
// constructor stats values for later inspection.
|
|
|
|
template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); }
|
|
|
|
template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); }
|
|
|
|
template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) {
|
|
|
|
auto &cst = ConstructorStats::get<T>();
|
|
|
|
cst.copy_assignments++;
|
|
|
|
cst.value(std::forward<Values>(values)...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void track_move_assigned(T *, Values &&...values) {
|
|
|
|
auto &cst = ConstructorStats::get<T>();
|
|
|
|
cst.move_assignments++;
|
|
|
|
cst.value(std::forward<Values>(values)...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void track_default_created(T *inst, Values &&...values) {
|
|
|
|
auto &cst = ConstructorStats::get<T>();
|
|
|
|
cst.default_created(inst);
|
|
|
|
cst.value(std::forward<Values>(values)...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void track_created(T *inst, Values &&...values) {
|
|
|
|
auto &cst = ConstructorStats::get<T>();
|
|
|
|
cst.created(inst);
|
|
|
|
cst.value(std::forward<Values>(values)...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void track_destroyed(T *inst) {
|
|
|
|
ConstructorStats::get<T>().destroyed(inst);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void track_values(T *, Values &&...values) {
|
|
|
|
ConstructorStats::get<T>().value(std::forward<Values>(values)...);
|
|
|
|
}
|
|
|
|
|
2016-09-06 22:50:10 +00:00
|
|
|
/// Don't cast pointers to Python, print them as strings
|
|
|
|
inline const char *format_ptrs(const char *p) { return p; }
|
|
|
|
template <typename T>
|
|
|
|
py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); }
|
|
|
|
template <typename T>
|
|
|
|
auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { return std::forward<T>(x); }
|
|
|
|
|
|
|
|
template <class T, typename... Output>
|
|
|
|
void print_constr_details(T *inst, const std::string &action, Output &&...output) {
|
|
|
|
py::print("###", py::type_id<T>(), "@", format_ptrs(inst), action,
|
|
|
|
format_ptrs(std::forward<Output>(output))...);
|
Improve constructor/destructor tracking
This commit rewrites the examples that look for constructor/destructor
calls to do so via static variable tracking rather than output parsing.
The added ConstructorStats class provides methods to keep track of
constructors and destructors, number of default/copy/move constructors,
and number of copy/move assignments. It also provides a mechanism for
storing values (e.g. for value construction), and then allows all of
this to be checked at the end of a test by getting the statistics for a
C++ (or python mapping) class.
By not relying on the precise pattern of constructions/destructions,
but rather simply ensuring that every construction is matched with a
destruction on the same object, we ensure that everything that gets
created also gets destroyed as expected.
This replaces all of the various "std::cout << whatever" code in
constructors/destructors with
`print_created(this)`/`print_destroyed(this)`/etc. functions which
provide similar output, but now has a unified format across the
different examples, including a new ### prefix that makes mixed example
output and lifecycle events easier to distinguish.
With this change, relaxed mode is no longer needed, which enables
testing for proper destruction under MSVC, and under any other compiler
that generates code calling extra constructors, or optimizes away any
constructors. GCC/clang are used as the baseline for move
constructors; the tests are adapted to allow more move constructors to
be evoked (but other types are constructors much have matching counts).
This commit also disables output buffering of tests, as the buffering
sometimes results in C++ output ending up in the middle of python
output (or vice versa), depending on the OS/python version.
2016-08-07 17:05:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verbose versions of the above:
|
|
|
|
template <class T, typename... Values> void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
|
|
|
|
print_constr_details(inst, "created via copy constructor", values...);
|
|
|
|
track_copy_created(inst);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
|
|
|
|
print_constr_details(inst, "created via move constructor", values...);
|
|
|
|
track_move_created(inst);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_copy_assigned(T *inst, Values &&...values) {
|
|
|
|
print_constr_details(inst, "assigned via copy assignment", values...);
|
|
|
|
track_copy_assigned(inst, values...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_move_assigned(T *inst, Values &&...values) {
|
|
|
|
print_constr_details(inst, "assigned via move assignment", values...);
|
|
|
|
track_move_assigned(inst, values...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_default_created(T *inst, Values &&...values) {
|
|
|
|
print_constr_details(inst, "created via default constructor", values...);
|
|
|
|
track_default_created(inst, values...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_created(T *inst, Values &&...values) {
|
|
|
|
print_constr_details(inst, "created", values...);
|
|
|
|
track_created(inst, values...);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
|
|
|
|
print_constr_details(inst, "destroyed", values...);
|
|
|
|
track_destroyed(inst);
|
|
|
|
}
|
|
|
|
template <class T, typename... Values> void print_values(T *inst, Values &&...values) {
|
|
|
|
print_constr_details(inst, ":", values...);
|
|
|
|
track_values(inst, values...);
|
|
|
|
}
|