constructor_stats.h
Go to the documentation of this file.
1 #pragma once
2 /*
3  tests/constructor_stats.h -- framework for printing and tracking object
4  instance lifetimes in example/test code.
5 
6  Copyright (c) 2016 Jason Rhinelander <jason@imaginary.ca>
7 
8  All rights reserved. Use of this source code is governed by a
9  BSD-style license that can be found in the LICENSE file.
10 
11 This header provides a few useful tools for writing examples or tests that want to check and/or
12 display object instance lifetimes. It requires that you include this header and add the following
13 function calls to constructors:
14 
15  class MyClass {
16  MyClass() { ...; print_default_created(this); }
17  ~MyClass() { ...; print_destroyed(this); }
18  MyClass(const MyClass &c) { ...; print_copy_created(this); }
19  MyClass(MyClass &&c) { ...; print_move_created(this); }
20  MyClass(int a, int b) { ...; print_created(this, a, b); }
21  MyClass &operator=(const MyClass &c) { ...; print_copy_assigned(this); }
22  MyClass &operator=(MyClass &&c) { ...; print_move_assigned(this); }
23 
24  ...
25  }
26 
27 You can find various examples of these in several of the existing testing .cpp files. (Of course
28 you don't need to add any of the above constructors/operators that you don't actually have, except
29 for the destructor).
30 
31 Each of these will print an appropriate message such as:
32 
33  ### MyClass @ 0x2801910 created via default constructor
34  ### MyClass @ 0x27fa780 created 100 200
35  ### MyClass @ 0x2801910 destroyed
36  ### MyClass @ 0x27fa780 destroyed
37 
38 You can also include extra arguments (such as the 100, 200 in the output above, coming from the
39 value constructor) for all of the above methods which will be included in the output.
40 
41 For testing, each of these also keeps track the created instances and allows you to check how many
42 of the various constructors have been invoked from the Python side via code such as:
43 
44  from pybind11_tests import ConstructorStats
45  cstats = ConstructorStats.get(MyClass)
46  print(cstats.alive())
47  print(cstats.default_constructions)
48 
49 Note that `.alive()` should usually be the first thing you call as it invokes Python's garbage
50 collector to actually destroy objects that aren't yet referenced.
51 
52 For everything except copy and move constructors and destructors, any extra values given to the
53 print_...() function is stored in a class-specific values list which you can retrieve and inspect
54 from the ConstructorStats instance `.values()` method.
55 
56 In some cases, when you need to track instances of a C++ class not registered with pybind11, you
57 need to add a function returning the ConstructorStats for the C++ class; this can be done with:
58 
59  m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>,
60 py::return_value_policy::reference)
61 
62 Finally, you can suppress the output messages, but keep the constructor tracking (for
63 inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
64 `track_copy_created(this)`).
65 
66 */
67 
68 #include "pybind11_tests.h"
69 
70 #include <list>
71 #include <sstream>
72 #include <typeindex>
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:
87 
88  void copy_created(void *inst) {
89  created(inst);
91  }
92 
93  void move_created(void *inst) {
94  created(inst);
96  }
97 
98  void default_created(void *inst) {
99  created(inst);
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();
133  int total = 0;
134  for (const auto &p : _instances) {
135  if (p.second > 0) {
136  total += p.second;
137  }
138  }
139  return total;
140  }
141 
142  void value() {} // Recursion terminator
143  // Takes one or more values, converts them to strings, then stores them.
144  template <typename T, typename... Tmore>
145  void value(const T &v, Tmore &&...args) {
146  std::ostringstream oss;
147  oss << v;
148  _values.push_back(oss.str());
149  value(std::forward<Tmore>(args)...);
150  }
151 
152  // Move out stored values
153  py::list values() {
154  py::list l;
155  for (const auto &v : _values) {
156  l.append(py::cast(v));
157  }
158  _values.clear();
159  return l;
160  }
161 
162  // Gets constructor stats from a C++ type index
163  static ConstructorStats &get(std::type_index type) {
164  static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
165  return all_cstats[type];
166  }
167 
168  // Gets constructor stats from a C++ type
169  template <typename T>
170  static ConstructorStats &get() {
171 #if defined(PYPY_VERSION)
172  gc();
173 #endif
174  return get(typeid(T));
175  }
176 
177  // Gets constructor stats from a Python class
178  static ConstructorStats &get(py::object class_) {
180  const std::type_index *t1 = nullptr, *t2 = nullptr;
181  try {
182  auto *type_info
183  = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
184  for (auto &p : internals.registered_types_cpp) {
185  if (p.second == type_info) {
186  if (t1) {
187  t2 = &p.first;
188  break;
189  }
190  t1 = &p.first;
191  }
192  }
193  } catch (const std::out_of_range &) {
194  }
195  if (!t1) {
196  throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
197  }
198  auto &cs1 = get(*t1);
199  // If we have both a t1 and t2 match, one is probably the trampoline class; return
200  // whichever has more constructions (typically one or the other will be 0)
201  if (t2) {
202  auto &cs2 = get(*t2);
203  int cs1_total = cs1.default_constructions + cs1.copy_constructions
204  + cs1.move_constructions + (int) cs1._values.size();
205  int cs2_total = cs2.default_constructions + cs2.copy_constructions
206  + cs2.move_constructions + (int) cs2._values.size();
207  if (cs2_total > cs1_total) {
208  return cs2;
209  }
210  }
211  return cs1;
212  }
213 };
214 
215 // To track construction/destruction, you need to call these methods from the various
216 // constructors/operators. The ones that take extra values record the given values in the
217 // constructor stats values for later inspection.
218 template <class T>
220  ConstructorStats::get<T>().copy_created(inst);
221 }
222 template <class T>
224  ConstructorStats::get<T>().move_created(inst);
225 }
226 template <class T, typename... Values>
228  auto &cst = ConstructorStats::get<T>();
229  cst.copy_assignments++;
230  cst.value(std::forward<Values>(values)...);
231 }
232 template <class T, typename... Values>
234  auto &cst = ConstructorStats::get<T>();
235  cst.move_assignments++;
236  cst.value(std::forward<Values>(values)...);
237 }
238 template <class T, typename... Values>
240  auto &cst = ConstructorStats::get<T>();
241  cst.default_created(inst);
242  cst.value(std::forward<Values>(values)...);
243 }
244 template <class T, typename... Values>
246  auto &cst = ConstructorStats::get<T>();
247  cst.created(inst);
248  cst.value(std::forward<Values>(values)...);
249 }
250 template <class T, typename... Values>
252  ConstructorStats::get<T>().destroyed(inst);
253 }
254 template <class T, typename... Values>
255 void track_values(T *, Values &&...values) {
256  ConstructorStats::get<T>().value(std::forward<Values>(values)...);
257 }
258 
260 inline const char *format_ptrs(const char *p) { return p; }
261 template <typename T>
263  return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p));
264 }
265 template <typename T>
266 auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) {
267  return std::forward<T>(x);
268 }
269 
270 template <class T, typename... Output>
271 void print_constr_details(T *inst, const std::string &action, Output &&...output) {
272  py::print("###",
273  py::type_id<T>(),
274  "@",
275  format_ptrs(inst),
276  action,
277  format_ptrs(std::forward<Output>(output))...);
278 }
279 
280 // Verbose versions of the above:
281 template <class T, typename... Values>
283  Values &&...values) { // NB: this prints, but doesn't store, given values
284  print_constr_details(inst, "created via copy constructor", values...);
286 }
287 template <class T, typename... Values>
289  Values &&...values) { // NB: this prints, but doesn't store, given values
290  print_constr_details(inst, "created via move constructor", values...);
292 }
293 template <class T, typename... Values>
295  print_constr_details(inst, "assigned via copy assignment", values...);
297 }
298 template <class T, typename... Values>
300  print_constr_details(inst, "assigned via move assignment", values...);
302 }
303 template <class T, typename... Values>
305  print_constr_details(inst, "created via default constructor", values...);
307 }
308 template <class T, typename... Values>
310  print_constr_details(inst, "created", values...);
312 }
313 template <class T, typename... Values>
314 void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
315  print_constr_details(inst, "destroyed", values...);
317 }
318 template <class T, typename... Values>
320  print_constr_details(inst, ":", values...);
321  track_values(inst, values...);
322 }
gtsam.examples.DogLegOptimizerExample.int
int
Definition: DogLegOptimizerExample.py:111
track_destroyed
void track_destroyed(T *inst)
Definition: constructor_stats.h:251
ConstructorStats::get
static ConstructorStats & get(py::object class_)
Definition: constructor_stats.h:178
Eigen::internal::print
EIGEN_STRONG_INLINE Packet4f print(const Packet4f &a)
Definition: NEON/PacketMath.h:3115
internals
Definition: internals.h:162
gtsam.examples.DogLegOptimizerExample.type
type
Definition: DogLegOptimizerExample.py:111
track_copy_assigned
void track_copy_assigned(T *, Values &&...values)
Definition: constructor_stats.h:227
type_info
Definition: internals.h:226
ConstructorStats::_values
std::list< std::string > _values
Definition: constructor_stats.h:79
ConstructorStats::destroyed
void destroyed(void *inst)
Definition: constructor_stats.h:105
internals::registered_types_cpp
type_map< type_info * > registered_types_cpp
Definition: internals.h:167
x
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
Definition: gnuplot_common_settings.hh:12
Values
track_values
void track_values(T *, Values &&...values)
Definition: constructor_stats.h:255
ConstructorStats::alive
int alive()
Definition: constructor_stats.h:131
T
Eigen::Triplet< double > T
Definition: Tutorial_sparse_example.cpp:6
different_sigmas::values
HybridValues values
Definition: testHybridBayesNet.cpp:245
ConstructorStats::values
py::list values()
Definition: constructor_stats.h:153
get_internals
PYBIND11_NOINLINE internals & get_internals()
Return a reference to the current internals data.
Definition: internals.h:483
ConstructorStats::default_constructions
int default_constructions
Definition: constructor_stats.h:82
result
Values result
Definition: OdometryOptimize.cpp:8
ConstructorStats::created
void created(void *inst)
Definition: constructor_stats.h:103
print_copy_created
void print_copy_created(T *inst, Values &&...values)
Definition: constructor_stats.h:282
ConstructorStats::get
static ConstructorStats & get()
Definition: constructor_stats.h:170
ConstructorStats::value
void value(const T &v, Tmore &&...args)
Definition: constructor_stats.h:145
track_move_created
void track_move_created(T *inst)
Definition: constructor_stats.h:223
print_default_created
void print_default_created(T *inst, Values &&...values)
Definition: constructor_stats.h:304
ConstructorStats::copy_assignments
int copy_assignments
Definition: constructor_stats.h:85
ConstructorStats::value
void value()
Definition: constructor_stats.h:142
print_copy_assigned
void print_copy_assigned(T *inst, Values &&...values)
Definition: constructor_stats.h:294
format_ptrs
const char * format_ptrs(const char *p)
Don't cast pointers to Python, print them as strings.
Definition: constructor_stats.h:260
l
static const Line3 l(Rot3(), 1, 1)
Eigen::Triplet< double >
track_copy_created
void track_copy_created(T *inst)
Definition: constructor_stats.h:219
gtsam.examples.DogLegOptimizerExample.action
action
Definition: DogLegOptimizerExample.py:115
ConstructorStats::copy_constructions
int copy_constructions
Definition: constructor_stats.h:83
ConstructorStats::move_assignments
int move_assignments
Definition: constructor_stats.h:86
track_default_created
void track_default_created(T *inst, Values &&...values)
Definition: constructor_stats.h:239
ConstructorStats::copy_created
void copy_created(void *inst)
Definition: constructor_stats.h:88
ConstructorStats::move_created
void move_created(void *inst)
Definition: constructor_stats.h:93
print_move_created
void print_move_created(T *inst, Values &&...values)
Definition: constructor_stats.h:288
ConstructorStats::default_created
void default_created(void *inst)
Definition: constructor_stats.h:98
pybind11_tests.h
print_constr_details
void print_constr_details(T *inst, const std::string &action, Output &&...output)
Definition: constructor_stats.h:271
ConstructorStats::get
static ConstructorStats & get(std::type_index type)
Definition: constructor_stats.h:163
track_created
void track_created(T *inst, Values &&...values)
Definition: constructor_stats.h:245
args
Definition: pytypes.h:2210
print_destroyed
void print_destroyed(T *inst, Values &&...values)
Definition: constructor_stats.h:314
p
float * p
Definition: Tutorial_Map_using.cpp:9
v
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
internals::registered_types_py
std::unordered_map< PyTypeObject *, std::vector< type_info * > > registered_types_py
Definition: internals.h:169
print_move_assigned
void print_move_assigned(T *inst, Values &&...values)
Definition: constructor_stats.h:299
ConstructorStats::_instances
std::unordered_map< void *, int > _instances
Definition: constructor_stats.h:77
globals
dict globals()
Definition: pybind11.h:1372
ConstructorStats::move_constructions
int move_constructions
Definition: constructor_stats.h:84
track_move_assigned
void track_move_assigned(T *, Values &&...values)
Definition: constructor_stats.h:233
ConstructorStats::gc
static void gc()
Definition: constructor_stats.h:113
gtsam.examples.ShonanAveragingCLI.str
str
Definition: ShonanAveragingCLI.py:115
uintptr_t
_W64 unsigned int uintptr_t
Definition: ms_stdint.h:124
Eigen::internal::cast
EIGEN_DEVICE_FUNC NewType cast(const OldType &x)
Definition: Eigen/src/Core/MathFunctions.h:460
print_created
void print_created(T *inst, Values &&...values)
Definition: constructor_stats.h:309
pybind_wrapper_test_script.inst
inst
Definition: pybind_wrapper_test_script.py:49
print_values
void print_values(T *inst, Values &&...values)
Definition: constructor_stats.h:319


gtsam
Author(s):
autogenerated on Fri Nov 1 2024 03:32:11