File indexing completed on 2025-01-18 10:17:51
0001 #pragma once
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068 #include "pybind11_tests.h"
0069
0070 #include <list>
0071 #include <sstream>
0072 #include <typeindex>
0073 #include <unordered_map>
0074
0075 class ConstructorStats {
0076 protected:
0077 std::unordered_map<void *, int> _instances;
0078
0079 std::list<std::string> _values;
0080
0081 public:
0082 int default_constructions = 0;
0083 int copy_constructions = 0;
0084 int move_constructions = 0;
0085 int copy_assignments = 0;
0086 int move_assignments = 0;
0087
0088 void copy_created(void *inst) {
0089 created(inst);
0090 copy_constructions++;
0091 }
0092
0093 void move_created(void *inst) {
0094 created(inst);
0095 move_constructions++;
0096 }
0097
0098 void default_created(void *inst) {
0099 created(inst);
0100 default_constructions++;
0101 }
0102
0103 void created(void *inst) { ++_instances[inst]; }
0104
0105 void destroyed(void *inst) {
0106 if (--_instances[inst] < 0) {
0107 throw std::runtime_error("cstats.destroyed() called with unknown "
0108 "instance; potential double-destruction "
0109 "or a missing cstats.created()");
0110 }
0111 }
0112
0113 static void gc() {
0114
0115 #if defined(PYPY_VERSION)
0116 PyObject *globals = PyEval_GetGlobals();
0117 PyObject *result = PyRun_String("import gc\n"
0118 "for i in range(2):\n"
0119 " gc.collect()\n",
0120 Py_file_input,
0121 globals,
0122 globals);
0123 if (result == nullptr)
0124 throw py::error_already_set();
0125 Py_DECREF(result);
0126 #else
0127 py::module_::import("gc").attr("collect")();
0128 #endif
0129 }
0130
0131 int alive() {
0132 gc();
0133 int total = 0;
0134 for (const auto &p : _instances) {
0135 if (p.second > 0) {
0136 total += p.second;
0137 }
0138 }
0139 return total;
0140 }
0141
0142 void value() {}
0143
0144 template <typename T, typename... Tmore>
0145 void value(const T &v, Tmore &&...args) {
0146 std::ostringstream oss;
0147 oss << v;
0148 _values.push_back(oss.str());
0149 value(std::forward<Tmore>(args)...);
0150 }
0151
0152
0153 py::list values() {
0154 py::list l;
0155 for (const auto &v : _values) {
0156 l.append(py::cast(v));
0157 }
0158 _values.clear();
0159 return l;
0160 }
0161
0162
0163 static ConstructorStats &get(std::type_index type) {
0164 static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
0165 return all_cstats[type];
0166 }
0167
0168
0169 template <typename T>
0170 static ConstructorStats &get() {
0171 #if defined(PYPY_VERSION)
0172 gc();
0173 #endif
0174 return get(typeid(T));
0175 }
0176
0177
0178 static ConstructorStats &get(py::object class_) {
0179 auto &internals = py::detail::get_internals();
0180 const std::type_index *t1 = nullptr, *t2 = nullptr;
0181 try {
0182 auto *type_info
0183 = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
0184 for (auto &p : internals.registered_types_cpp) {
0185 if (p.second == type_info) {
0186 if (t1) {
0187 t2 = &p.first;
0188 break;
0189 }
0190 t1 = &p.first;
0191 }
0192 }
0193 } catch (const std::out_of_range &) {
0194 }
0195 if (!t1) {
0196 throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
0197 }
0198 auto &cs1 = get(*t1);
0199
0200
0201 if (t2) {
0202 auto &cs2 = get(*t2);
0203 int cs1_total = cs1.default_constructions + cs1.copy_constructions
0204 + cs1.move_constructions + (int) cs1._values.size();
0205 int cs2_total = cs2.default_constructions + cs2.copy_constructions
0206 + cs2.move_constructions + (int) cs2._values.size();
0207 if (cs2_total > cs1_total) {
0208 return cs2;
0209 }
0210 }
0211 return cs1;
0212 }
0213 };
0214
0215
0216
0217
0218 template <class T>
0219 void track_copy_created(T *inst) {
0220 ConstructorStats::get<T>().copy_created(inst);
0221 }
0222 template <class T>
0223 void track_move_created(T *inst) {
0224 ConstructorStats::get<T>().move_created(inst);
0225 }
0226 template <class T, typename... Values>
0227 void track_copy_assigned(T *, Values &&...values) {
0228 auto &cst = ConstructorStats::get<T>();
0229 cst.copy_assignments++;
0230 cst.value(std::forward<Values>(values)...);
0231 }
0232 template <class T, typename... Values>
0233 void track_move_assigned(T *, Values &&...values) {
0234 auto &cst = ConstructorStats::get<T>();
0235 cst.move_assignments++;
0236 cst.value(std::forward<Values>(values)...);
0237 }
0238 template <class T, typename... Values>
0239 void track_default_created(T *inst, Values &&...values) {
0240 auto &cst = ConstructorStats::get<T>();
0241 cst.default_created(inst);
0242 cst.value(std::forward<Values>(values)...);
0243 }
0244 template <class T, typename... Values>
0245 void track_created(T *inst, Values &&...values) {
0246 auto &cst = ConstructorStats::get<T>();
0247 cst.created(inst);
0248 cst.value(std::forward<Values>(values)...);
0249 }
0250 template <class T, typename... Values>
0251 void track_destroyed(T *inst) {
0252 ConstructorStats::get<T>().destroyed(inst);
0253 }
0254 template <class T, typename... Values>
0255 void track_values(T *, Values &&...values) {
0256 ConstructorStats::get<T>().value(std::forward<Values>(values)...);
0257 }
0258
0259
0260 inline const char *format_ptrs(const char *p) { return p; }
0261 template <typename T>
0262 py::str format_ptrs(T *p) {
0263 return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p));
0264 }
0265 template <typename T>
0266 auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) {
0267 return std::forward<T>(x);
0268 }
0269
0270 template <class T, typename... Output>
0271 void print_constr_details(T *inst, const std::string &action, Output &&...output) {
0272 py::print("###",
0273 py::type_id<T>(),
0274 "@",
0275 format_ptrs(inst),
0276 action,
0277 format_ptrs(std::forward<Output>(output))...);
0278 }
0279
0280
0281 template <class T, typename... Values>
0282 void print_copy_created(T *inst,
0283 Values &&...values) {
0284 print_constr_details(inst, "created via copy constructor", values...);
0285 track_copy_created(inst);
0286 }
0287 template <class T, typename... Values>
0288 void print_move_created(T *inst,
0289 Values &&...values) {
0290 print_constr_details(inst, "created via move constructor", values...);
0291 track_move_created(inst);
0292 }
0293 template <class T, typename... Values>
0294 void print_copy_assigned(T *inst, Values &&...values) {
0295 print_constr_details(inst, "assigned via copy assignment", values...);
0296 track_copy_assigned(inst, values...);
0297 }
0298 template <class T, typename... Values>
0299 void print_move_assigned(T *inst, Values &&...values) {
0300 print_constr_details(inst, "assigned via move assignment", values...);
0301 track_move_assigned(inst, values...);
0302 }
0303 template <class T, typename... Values>
0304 void print_default_created(T *inst, Values &&...values) {
0305 print_constr_details(inst, "created via default constructor", values...);
0306 track_default_created(inst, values...);
0307 }
0308 template <class T, typename... Values>
0309 void print_created(T *inst, Values &&...values) {
0310 print_constr_details(inst, "created", values...);
0311 track_created(inst, values...);
0312 }
0313 template <class T, typename... Values>
0314 void print_destroyed(T *inst, Values &&...values) {
0315 print_constr_details(inst, "destroyed", values...);
0316 track_destroyed(inst);
0317 }
0318 template <class T, typename... Values>
0319 void print_values(T *inst, Values &&...values) {
0320 print_constr_details(inst, ":", values...);
0321 track_values(inst, values...);
0322 }