| 73 | #include <unordered_map> |
| 74 | |
| 75 | class ConstructorStats { |
| 76 | protected: |
| 77 | std::unordered_map<void *, int> _instances; // Need a map rather than set because members can |
| 78 | // shared address with parents |
| 79 | std::list<std::string> _values; // Used to track values |
| 80 | // (e.g. of value constructors) |
| 81 | public: |
| 82 | int default_constructions = 0; |
| 83 | int copy_constructions = 0; |
| 84 | int move_constructions = 0; |
| 85 | int copy_assignments = 0; |
| 86 | int move_assignments = 0; |
| 87 | |
| 88 | void copy_created(void *inst) { |
| 89 | created(inst); |
| 90 | copy_constructions++; |
| 91 | } |
| 92 | |
| 93 | void move_created(void *inst) { |
| 94 | created(inst); |
| 95 | move_constructions++; |
| 96 | } |
| 97 | |
| 98 | void default_created(void *inst) { |
| 99 | created(inst); |
| 100 | default_constructions++; |
| 101 | } |
| 102 | |
| 103 | void created(void *inst) { ++_instances[inst]; } |
| 104 | |
| 105 | void destroyed(void *inst) { |
| 106 | if (--_instances[inst] < 0) { |
| 107 | throw std::runtime_error("cstats.destroyed() called with unknown " |
| 108 | "instance; potential double-destruction " |
| 109 | "or a missing cstats.created()"); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | static void gc() { |
| 114 | // Force garbage collection to ensure any pending destructors are invoked: |
| 115 | #if defined(PYPY_VERSION) |
| 116 | PyObject *globals = PyEval_GetGlobals(); |
| 117 | PyObject *result = PyRun_String("import gc\n" |
| 118 | "for i in range(2):\n" |
| 119 | " gc.collect()\n", |
| 120 | Py_file_input, |
| 121 | globals, |
| 122 | globals); |
| 123 | if (result == nullptr) |
| 124 | throw py::error_already_set(); |
| 125 | Py_DECREF(result); |
| 126 | #else |
| 127 | py::module_::import("gc").attr("collect")(); |
| 128 | #endif |
| 129 | } |
| 130 | |
| 131 | int alive() { |
| 132 | gc(); |