#pragma once #include #include #include "dispatcher.hpp" namespace autocxxpy { /* example to change the callback method: @startcode cpp // switch async/direct template <> struct callback_type_of(&A::func2)> { const static callback_type value = callback_type::Direct; }; // rewrite the whole function template<> struct callback_wrapper(&A::func2)> { inline static void call(A *instance, float) { constexpr auto method = static_cast(&A::func2); default_callback_wrapper::call(instance, 1); (instance->*method)(1); std::cout << "wrapped!" << std::endl; } }; @endcode example to change the calling method: @startcode pp template <> struct calling_wrapper<&A::func2> { static constexpr value = ()[]{return 1;} } @endcode */ // since std::invoke_result cann't get result type for class member pointer, we wrote those template struct value_invoke_result { template inline static ret_type get_type(ret_type(class_type::* m)(arg_types ...)) { } template inline static ret_type get_type(ret_type(*m)(arg_types ...)) { } using type = decltype(get_type(method)); }; template using value_invoke_result_t = typename value_invoke_result::type; template struct class_of_member_method { template inline static class_type get_type(ret_type(class_type::* m)(arg_types ...)) { } template inline static void get_type(ret_type(* m)(arg_types ...)) { // # todo: try to use class template to make gcc happy //static_assert(false, "Don't pass a static method or a global function here!"); } using type = decltype(get_type(method)); }; template using value_invoke_result_t = typename value_invoke_result::type; template using class_of_member_method_t = typename class_of_member_method::type; enum class callback_type { Direct = 0, Async = 1 }; template struct default_callback_type_of { const static callback_type value = callback_type::Async; }; template struct callback_type_of : default_callback_type_of {}; template constexpr callback_type callback_type_of_v = callback_type_of::value; template struct default_callback_wrapper { using ret_type = value_invoke_result_t; using class_type = class_of_member_method_t; public: template inline static ret_type call(class_type *instance, const char *py_func_name, arg_types ... args) { if constexpr (callback_type_of_v == callback_type::Direct) { return sync(instance, args ...); } else { async(instance, py_func_name, args ...); static_assert(std::is_void_v || std::is_default_constructible_v, "type is not default_constructiblev, you should use sync call instead."); return ret_type(); // if ret_type() is not constructable, this will make compiler unhappy } } template inline static void async(class_type *instance, const char *py_func_name, arg_types ... args) { return async_impl(instance, py_func_name, std::index_sequence_for{}, args ...); } template inline static ret_type sync(class_type *instance, const char * py_func_name, arg_types ... args) { // if this code is under test environment, we don't need pybind11 // since header of pybind11 use #pragma once, no macros is defined, we use a public macro to check if pybind11 is included or not #ifdef PYBIND11_OVERLOAD_NAME pybind11::gil_scoped_acquire gil; pybind11::function overload = pybind11::get_overload(static_cast(instance), py_func_name); if (overload) { auto o = overload(args ...); if (pybind11::detail::cast_is_temporary_value_reference::value) { static pybind11::detail::overload_caster_t caster; return pybind11::detail::cast_ref(std::move(o), caster); } else return pybind11::detail::cast_safe(std::move(o)); } #endif return (instance->*method)(args ...); } private: template inline static T &deref(T *val) { // match pointer return *val; } template inline static T &deref(const T *val) { // match const pointer return const_cast(*val); } template inline static T &deref(const T &val) { // match everything else : just use original type return const_cast(val); } template struct resolve { // match default(everyting besides pointer) template inline to_type operator ()(src_type &val) { return val; } }; template struct resolve { // match pointer template inline to_type *operator ()(src_type &val) { // val to poiner return const_cast(&val); } template inline to_type *operator ()(src_type *val) { // pointer to pointer return val; } }; template struct get_type {}; template struct get_type<0, first_type, types ...> { using type = first_type; }; template struct get_type { using type = typename get_type::type; }; template using get_type_t = typename get_type::type; template inline static void async_impl(class_type *instance, const char *py_func_name, std::index_sequence, arg_types ... args) { // wrap for ctp like function calls: // all the pointer might be unavailable after this call, so copy its value into a tuple auto arg_tuple = std::make_tuple(deref(args) ...); auto task = [instance, py_func_name, arg_tuple = std::move(arg_tuple)]() { // resolve all value: // if it was originally a pointer, then use pointer type. // if it was originally a value, just keep a reference to that value. sync( instance, py_func_name, resolve>{} (std::get(arg_tuple)) ... ); }; dispatcher::instance().add(std::move(task)); } }; template struct callback_wrapper : default_callback_wrapper {}; template struct default_calling_wrapper { public: using ret_type = value_invoke_result_t; using func_type = decltype(method); public: static constexpr func_type value = method; }; template struct calling_wrapper : default_calling_wrapper {}; }