00001 #ifndef toast_python_make_callback_h
00002 #define toast_python_make_callback_h
00003
00004 #include <iostream>
00005 #include <stdexcept>
00006
00007 #include <boost/type_traits.hpp>
00008 #include <boost/python.hpp>
00009 #include <boost/preprocessor/arithmetic/inc.hpp>
00010 #include <boost/preprocessor/repetition/enum_params.hpp>
00011 #include <boost/preprocessor/repetition/enum_shifted_params.hpp>
00012 #include <boost/preprocessor/repetition/enum_binary_params.hpp>
00013 #include <boost/preprocessor/repetition/repeat_from_to.hpp>
00014
00021
00022
00023 namespace toast {
00024 namespace python {
00025 namespace detail {
00026
00027 class GIL
00028 {
00029 PyGILState_STATE state_;
00030 public:
00031 GIL() : state_(PyGILState_Ensure()) {}
00032 ~GIL() { PyGILState_Release(state_); }
00033 };
00034
00035
00036
00037 #define WRAPCALLBACK_MAX_ARGS 6
00038
00039 template <int Arity, typename Signature>
00040 struct Connector;
00041
00042
00043
00044 inline void make_callback_deleter(boost::python::object *o)
00045 {
00046 PyGILState_STATE gil = PyGILState_Ensure();
00047 delete o;
00048 PyGILState_Release(gil);
00049 }
00050
00051 #define WRAPCALLBACK_TRAIT(Z, N, _) , typename Traits::arg ## N ## _type
00052
00053
00054 #define WRAPCALLBACK(Z, N, _) \
00055 template <typename ReturnType BOOST_PP_COMMA_IF(N) \
00056 BOOST_PP_ENUM_PARAMS(N, typename T) > \
00057 struct WrapCallback ## N \
00058 { \
00059 static ReturnType exec(boost::shared_ptr<boost::python::object> &callback \
00060 BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_BINARY_PARAMS(N, T, arg) ) \
00061 { \
00062 GIL gil; \
00063 try \
00064 { \
00065 return boost::python::call<ReturnType>(callback->ptr() \
00066 BOOST_PP_COMMA_IF(N) \
00067 BOOST_PP_ENUM_PARAMS(N, arg)); \
00068 } \
00069 catch(boost::python::error_already_set const &e) \
00070 { \
00071 PyErr_Print(); \
00072 } \
00073 } \
00074 };
00075
00076 #define CONNECTOR(Z, N, _) \
00077 template <typename Signature> \
00078 struct Connector<N, Signature> \
00079 { \
00080 static boost::function<Signature> connect( \
00081 boost::python::object const &callback) \
00082 { \
00083 typedef boost::function_traits<Signature> Traits; \
00084 boost::shared_ptr<boost::python::object> cb(new \
00085 boost::python::object(callback), &make_callback_deleter); \
00086 return boost::bind(WrapCallback ## N <typename Traits::result_type \
00087 BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_INC(N), \
00088 WRAPCALLBACK_TRAIT, _)>::exec, cb \
00089 BOOST_PP_COMMA_IF(N) \
00090 BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(N), _)); \
00091 } \
00092 };
00093
00094 BOOST_PP_REPEAT(WRAPCALLBACK_MAX_ARGS, WRAPCALLBACK, _)
00095 BOOST_PP_REPEAT_FROM_TO(0, WRAPCALLBACK_MAX_ARGS, CONNECTOR, _)
00096
00097 #undef WRAPCALLBACK_MAX_ARGS
00098 #undef WRAPCALLBACK_TRAIT
00099 #undef WRAPCALLBACK
00100 #undef CONNECTOR
00101
00102 }
00103
00105
00118 template <typename Signature>
00119 boost::function<Signature> make_callback(boost::python::object const &callback)
00120 {
00121 if(callback.ptr() == Py_None)
00122 return boost::function<Signature>();
00123 if(!PyCallable_Check(callback.ptr()))
00124 throw std::runtime_error("object not callable");
00125 return detail::Connector<boost::function_traits<Signature>::arity,
00126 Signature>::connect(callback);
00127 }
00128
00131 }
00132 }
00133
00134 #endif
00135