Skip to content

Commit cb89d13

Browse files
committed
Add function to safely cast to Python function
1 parent e65258f commit cb89d13

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

src/classes.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,14 @@ namespace scorepy
6363
PyTypeObject& getCInstrumenterType()
6464
{
6565
static PyMethodDef methods[] = {
66-
{ "_enable_instrumenter", reinterpret_cast<PyCFunction>(CInstrumenter_enable_instrumenter),
66+
{ "_enable_instrumenter", scorepy::castToPyFunc(CInstrumenter_enable_instrumenter),
6767
METH_NOARGS, "Enable the instrumenter" },
68-
{ "_disable_instrumenter",
69-
reinterpret_cast<PyCFunction>(CInstrumenter_disable_instrumenter), METH_NOARGS,
70-
"Disable the instrumenter" },
68+
{ "_disable_instrumenter", scorepy::castToPyFunc(CInstrumenter_disable_instrumenter),
69+
METH_NOARGS, "Disable the instrumenter" },
7170
{ nullptr } /* Sentinel */
7271
};
7372
static PyGetSetDef getseters[] = {
74-
{ "tracingOrProfiling", reinterpret_cast<getter>(CInstrumenter_get_tracingOrProfiling),
73+
{ "tracingOrProfiling", scorepy::castToPyFunc(CInstrumenter_get_tracingOrProfiling),
7574
nullptr, "Return whether the trace (True) or profile (False) instrumentation is used",
7675
nullptr },
7776
{ nullptr } /* Sentinel */
@@ -83,7 +82,7 @@ PyTypeObject& getCInstrumenterType()
8382
sizeof(CInstrumenter), /* tp_basicsize */
8483
};
8584
type.tp_new = call_object_new;
86-
type.tp_init = reinterpret_cast<initproc>(CInstrumenter_init);
85+
type.tp_init = scorepy::castToPyFunc(CInstrumenter_init);
8786
type.tp_methods = methods;
8887
type.tp_getset = getseters;
8988
type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;

src/scorepy/pythonHelpers.hpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <Python.h>
44
#include <frameobject.h>
55
#include <string>
6+
#include <type_traits>
67

78
namespace scorepy
89
{
@@ -53,10 +54,61 @@ class PyRefObject
5354
}
5455
};
5556

57+
namespace detail
58+
{
59+
template <typename TFunc>
60+
struct ReplaceArgsToPyObject;
61+
62+
template <typename TFunc>
63+
using ReplaceArgsToPyObject_t = typename ReplaceArgsToPyObject<TFunc>::type;
64+
} // namespace detail
65+
66+
/// Cast a function pointer to a python-bindings compatible function pointer
67+
/// Replaces all Foo* by PyObject* for all types Foo that are PyObject compatible
68+
template <typename TFunc>
69+
auto castToPyFunc(TFunc* func) -> detail::ReplaceArgsToPyObject_t<TFunc>*
70+
{
71+
return reinterpret_cast<detail::ReplaceArgsToPyObject_t<TFunc>*>(func);
72+
}
73+
5674
/// Return the module name the frame belongs to.
5775
/// The pointer is valid for the lifetime of the frame
5876
const char* get_module_name(const PyFrameObject& frame);
5977
/// Return the file name the frame belongs to
6078
std::string get_file_name(const PyFrameObject& frame);
6179

80+
// Implementation details
81+
namespace detail
82+
{
83+
84+
template <typename>
85+
struct make_void
86+
{
87+
typedef void type;
88+
};
89+
template <typename T>
90+
using void_t = typename make_void<T>::type;
91+
92+
template <class T, class = void>
93+
struct IsPyObject : std::false_type
94+
{
95+
};
96+
template <class T>
97+
struct IsPyObject<T*> : IsPyObject<T>
98+
{
99+
};
100+
101+
template <class T>
102+
struct IsPyObject<T, void_t<decltype(std::declval<T>().toPyObject())>> : std::true_type
103+
{
104+
};
105+
106+
template <typename TResult, typename... TArgs>
107+
struct ReplaceArgsToPyObject<TResult(TArgs...)>
108+
{
109+
template <typename T>
110+
using replace = typename std::conditional<IsPyObject<T>::value, PyObject*, T>::type;
111+
using type = TResult(replace<TArgs>...);
112+
};
113+
} // namespace detail
62114
} // namespace scorepy

0 commit comments

Comments
 (0)