Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F91560391
constructor_stats.h
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Nov 12, 05:34
Size
10 KB
Mime Type
text/x-c++
Expires
Thu, Nov 14, 05:34 (2 d)
Engine
blob
Format
Raw Data
Handle
22283328
Attached To
R9490 Homework_sp4e_Peruzzo_SáezUribe
constructor_stats.h
View Options
#pragma once
/*
tests/constructor_stats.h -- framework for printing and tracking object
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); }
...
}
You can find various examples of these in several of the existing testing .cpp files. (Of course
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:
from pybind11_tests import ConstructorStats
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:
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference)
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)`).
*/
#include "pybind11_tests.h"
#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
++
;
}
void
move_created
(
void
*
inst
)
{
created
(
inst
);
move_constructions
++
;
}
void
default_created
(
void
*
inst
)
{
created
(
inst
);
default_constructions
++
;
}
void
created
(
void
*
inst
)
{
++
_instances
[
inst
];
}
void
destroyed
(
void
*
inst
)
{
if
(
--
_instances
[
inst
]
<
0
)
throw
std
::
runtime_error
(
"cstats.destroyed() called with unknown "
"instance; potential double-destruction "
"or a missing cstats.created()"
);
}
static
void
gc
()
{
// Force garbage collection to ensure any pending destructors are invoked:
#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
py
::
module
::
import
(
"gc"
).
attr
(
"collect"
)();
#endif
}
int
alive
()
{
gc
();
int
total
=
0
;
for
(
const
auto
&
p
:
_instances
)
if
(
p
.
second
>
0
)
total
+=
p
.
second
;
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
)...);
}
// Move out stored values
py
::
list
values
()
{
py
::
list
l
;
for
(
const
auto
&
v
:
_values
)
l
.
append
(
py
::
cast
(
v
));
_values
.
clear
();
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
()
{
#if defined(PYPY_VERSION)
gc
();
#endif
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
{
auto
*
type_info
=
internals
.
registered_types_py
.
at
((
PyTypeObject
*
)
class_
.
ptr
()).
at
(
0
);
for
(
auto
&
p
:
internals
.
registered_types_cpp
)
{
if
(
p
.
second
==
type_info
)
{
if
(
t1
)
{
t2
=
&
p
.
first
;
break
;
}
t1
=
&
p
.
first
;
}
}
}
catch
(
const
std
::
out_of_range
&
)
{}
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
)...);
}
/// 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
))...);
}
// 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
...);
}
Event Timeline
Log In to Comment