You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyext17.h 19 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. #pragma once
  2. #include <Python.h>
  3. #include <pybind11/pybind11.h>
  4. #include <exception>
  5. #include <stdexcept>
  6. #include <utility>
  7. #include <vector>
  8. namespace pyext17 {
  9. #ifdef METH_FASTCALL
  10. constexpr bool has_fastcall = true;
  11. #else
  12. constexpr bool has_fastcall = false;
  13. #endif
  14. #ifdef _Py_TPFLAGS_HAVE_VECTORCALL
  15. constexpr bool has_vectorcall = true;
  16. #else
  17. constexpr bool has_vectorcall = false;
  18. #endif
  19. template <typename... Args>
  20. struct invocable_with {
  21. template <typename T>
  22. constexpr bool operator()(T&& lmb) {
  23. return std::is_invocable_v<T, Args...>;
  24. }
  25. };
  26. #define HAS_MEMBER_TYPE(T, U) \
  27. invocable_with<T>{}([](auto&& x) -> typename std::decay_t<decltype(x)>::U {})
  28. #define HAS_MEMBER(T, m) \
  29. invocable_with<T>{}([](auto&& x) -> decltype(&std::decay_t<decltype(x)>::m) {})
  30. inline PyObject* cvt_retval(PyObject* rv) {
  31. return rv;
  32. }
  33. #define CVT_RET_PYOBJ(...) \
  34. if constexpr (std::is_same_v<decltype(__VA_ARGS__), void>) { \
  35. __VA_ARGS__; \
  36. Py_RETURN_NONE; \
  37. } else { \
  38. return cvt_retval(__VA_ARGS__); \
  39. }
  40. inline int cvt_retint(int ret) {
  41. return ret;
  42. }
  43. #define CVT_RET_INT(...) \
  44. if constexpr (std::is_same_v<decltype(__VA_ARGS__), void>) { \
  45. __VA_ARGS__; \
  46. return 0; \
  47. } else { \
  48. return cvt_retint(__VA_ARGS__); \
  49. }
  50. struct py_err_set : std::exception {};
  51. // refer to pybind11 for the following exception handling helper
  52. inline void pybind11_translate_exception(std::exception_ptr last_exception) {
  53. auto& registered_exception_translators =
  54. pybind11::detail::get_internals().registered_exception_translators;
  55. for (auto& translator : registered_exception_translators) {
  56. try {
  57. translator(last_exception);
  58. } catch (...) {
  59. last_exception = std::current_exception();
  60. continue;
  61. }
  62. return;
  63. }
  64. PyErr_SetString(
  65. PyExc_SystemError, "Exception escaped from default exception translator!");
  66. }
  67. inline void pybind11_translate_exception() {
  68. pybind11_translate_exception(std::current_exception());
  69. }
  70. #if defined(__GNUG__) && !defined(__clang__)
  71. #define PYEXT17_TRANSLATE_EXC_CATCH_FORCED_UNWIND \
  72. catch (::abi::__forced_unwind&) { \
  73. throw; \
  74. }
  75. #else
  76. #define PYEXT17_TRANSLATE_EXC_CATCH_FORCED_UNWIND
  77. #endif
  78. #define PYEXT17_TRANSLATE_EXC \
  79. catch (::pyext17::py_err_set&) { \
  80. } \
  81. catch (::pybind11::error_already_set & e) { \
  82. e.restore(); \
  83. } \
  84. PYEXT17_TRANSLATE_EXC_CATCH_FORCED_UNWIND \
  85. catch (...) { \
  86. ::pyext17::pybind11_translate_exception(); \
  87. }
  88. #define PYEXT17_TRANSLATE_EXC_RET(RET) \
  89. catch (::pyext17::py_err_set&) { \
  90. return RET; \
  91. } \
  92. catch (::pybind11::error_already_set & e) { \
  93. e.restore(); \
  94. return RET; \
  95. } \
  96. PYEXT17_TRANSLATE_EXC_CATCH_FORCED_UNWIND \
  97. catch (...) { \
  98. ::pyext17::pybind11_translate_exception(); \
  99. return RET; \
  100. };
  101. template <typename T>
  102. struct wrap {
  103. private:
  104. typedef wrap<T> wrap_t;
  105. public:
  106. PyObject_HEAD std::aligned_storage_t<sizeof(T), alignof(T)> storage;
  107. #ifdef _Py_TPFLAGS_HAVE_VECTORCALL
  108. PyObject* (*vectorcall_slot)(PyObject*, PyObject* const*, size_t, PyObject*);
  109. #endif
  110. inline T* inst() { return reinterpret_cast<T*>(&storage); }
  111. inline static PyObject* pycast(T* ptr) {
  112. return (PyObject*)((char*)ptr - offsetof(wrap_t, storage));
  113. }
  114. private:
  115. // method wrapper
  116. enum struct meth_type { noarg, varkw, fastcall, singarg };
  117. template <auto f>
  118. struct detect_meth_type {
  119. static constexpr meth_type value = []() {
  120. using F = decltype(f);
  121. static_assert(std::is_member_function_pointer_v<F>);
  122. if constexpr (std::is_invocable_v<F, T>) {
  123. return meth_type::noarg;
  124. } else if constexpr (std::is_invocable_v<F, T, PyObject*, PyObject*>) {
  125. return meth_type::varkw;
  126. } else if constexpr (std::is_invocable_v<
  127. F, T, PyObject* const*, Py_ssize_t>) {
  128. return meth_type::fastcall;
  129. } else if constexpr (std::is_invocable_v<F, T, PyObject*>) {
  130. return meth_type::singarg;
  131. } else {
  132. static_assert(!std::is_same_v<F, F>);
  133. }
  134. }();
  135. };
  136. template <meth_type, auto f>
  137. struct meth {};
  138. template <auto f>
  139. struct meth<meth_type::noarg, f> {
  140. static constexpr int flags = METH_NOARGS;
  141. static PyObject* impl(PyObject* self, PyObject*) {
  142. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  143. try {
  144. CVT_RET_PYOBJ((inst->*f)());
  145. }
  146. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  147. }
  148. };
  149. template <auto f>
  150. struct meth<meth_type::varkw, f> {
  151. static constexpr int flags = METH_VARARGS | METH_KEYWORDS;
  152. static PyObject* impl(PyObject* self, PyObject* args, PyObject* kwargs) {
  153. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  154. try {
  155. CVT_RET_PYOBJ((inst->*f)(args, kwargs));
  156. }
  157. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  158. }
  159. };
  160. template <auto f>
  161. struct meth<meth_type::fastcall, f> {
  162. #ifdef METH_FASTCALL
  163. static constexpr int flags = METH_FASTCALL;
  164. static PyObject* impl(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
  165. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  166. try {
  167. CVT_RET_PYOBJ((inst->*f)(args, nargs));
  168. }
  169. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  170. }
  171. #else
  172. static constexpr int flags = METH_VARARGS;
  173. static PyObject* impl(PyObject* self, PyObject* args) {
  174. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  175. auto* arr = &PyTuple_GET_ITEM(args, 0);
  176. auto size = PyTuple_GET_SIZE(args);
  177. try {
  178. CVT_RET_PYOBJ((inst->*f)(arr, size));
  179. }
  180. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  181. }
  182. #endif
  183. };
  184. template <auto f>
  185. struct meth<meth_type::singarg, f> {
  186. static constexpr int flags = METH_O;
  187. static PyObject* impl(PyObject* self, PyObject* obj) {
  188. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  189. try {
  190. CVT_RET_PYOBJ((inst->*f)(obj));
  191. }
  192. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  193. }
  194. };
  195. template <auto f>
  196. static constexpr PyMethodDef make_meth_def(
  197. const char* name, const char* doc = nullptr) {
  198. using M = meth<detect_meth_type<f>::value, f>;
  199. return {name, (PyCFunction)M::impl, M::flags, doc};
  200. }
  201. template <auto f>
  202. struct getter {
  203. using F = decltype(f);
  204. static PyObject* impl(PyObject* self, void* closure) {
  205. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  206. try {
  207. if constexpr (std::is_invocable_v<F, PyObject*, void*>) {
  208. CVT_RET_PYOBJ(f(self, closure));
  209. } else if constexpr (std::is_invocable_v<F, T, void*>) {
  210. CVT_RET_PYOBJ((inst->*f)(closure));
  211. } else if constexpr (std::is_invocable_v<F, T>) {
  212. CVT_RET_PYOBJ((inst->*f)());
  213. } else {
  214. static_assert(!std::is_same_v<F, F>);
  215. }
  216. }
  217. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  218. }
  219. };
  220. template <auto f>
  221. struct setter {
  222. using F = decltype(f);
  223. template <typename = void>
  224. static int impl_(PyObject* self, PyObject* val, void* closure) {
  225. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  226. try {
  227. if constexpr (std::is_invocable_v<F, PyObject*, PyObject*, void*>) {
  228. CVT_RET_INT(f(self, val, closure));
  229. } else if constexpr (std::is_invocable_v<F, T, PyObject*, void*>) {
  230. CVT_RET_INT((inst->*f)(val, closure));
  231. } else if constexpr (std::is_invocable_v<F, T, PyObject*>) {
  232. CVT_RET_INT((inst->*f)(val));
  233. } else {
  234. static_assert(!std::is_same_v<F, F>);
  235. }
  236. }
  237. PYEXT17_TRANSLATE_EXC_RET(-1)
  238. }
  239. static constexpr auto impl = []() {
  240. if constexpr (std::is_same_v<F, std::nullptr_t>)
  241. return nullptr;
  242. else
  243. return impl_<>;
  244. }();
  245. };
  246. template <auto get, auto set = nullptr>
  247. static constexpr PyGetSetDef make_getset_def(
  248. const char* name, const char* doc = nullptr, void* closure = nullptr) {
  249. return {const_cast<char*>(name), getter<get>::impl, setter<set>::impl,
  250. const_cast<char*>(doc), closure};
  251. }
  252. // polyfills
  253. struct tp_vectorcall {
  254. static constexpr bool valid = HAS_MEMBER(T, tp_vectorcall);
  255. static constexpr bool haskw = []() {
  256. if constexpr (valid)
  257. if constexpr (std::is_invocable_v<
  258. decltype(&T::tp_vectorcall), T, PyObject* const*,
  259. size_t, PyObject*>)
  260. return true;
  261. return false;
  262. }();
  263. template <typename = void>
  264. static PyObject* impl(
  265. PyObject* self, PyObject* const* args, size_t nargsf,
  266. PyObject* kwnames) {
  267. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  268. if constexpr (haskw) {
  269. CVT_RET_PYOBJ(inst->tp_vectorcall(args, nargsf, kwnames));
  270. } else {
  271. if (kwnames && PyTuple_GET_SIZE(kwnames)) {
  272. PyErr_SetString(PyExc_TypeError, "expect no keyword argument");
  273. return nullptr;
  274. }
  275. CVT_RET_PYOBJ(inst->tp_vectorcall(args, nargsf));
  276. }
  277. }
  278. static constexpr Py_ssize_t offset = []() {
  279. if constexpr (valid)
  280. return offsetof(wrap_t, vectorcall_slot);
  281. else
  282. return 0;
  283. }();
  284. };
  285. struct tp_call {
  286. static constexpr bool provided = HAS_MEMBER(T, tp_call);
  287. static constexpr bool static_form =
  288. invocable_with<T, PyObject*, PyObject*, PyObject*>{}(
  289. [](auto&& t, auto... args)
  290. -> decltype(std::decay_t<decltype(t)>::tp_call(
  291. args...)) {});
  292. static constexpr bool valid = provided || tp_vectorcall::valid;
  293. template <typename = void>
  294. static PyObject* impl(PyObject* self, PyObject* args, PyObject* kwargs) {
  295. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  296. CVT_RET_PYOBJ(inst->tp_call(args, kwargs));
  297. }
  298. static constexpr ternaryfunc value = []() {
  299. if constexpr (static_form)
  300. return T::tp_call;
  301. else if constexpr (provided)
  302. return impl<>;
  303. #ifdef _Py_TPFLAGS_HAVE_VECTORCALL
  304. else if constexpr (valid)
  305. return PyVectorcall_Call;
  306. #endif
  307. else
  308. return nullptr;
  309. }();
  310. };
  311. struct tp_new {
  312. static constexpr bool provided = HAS_MEMBER(T, tp_new);
  313. static constexpr bool varkw = std::is_constructible_v<T, PyObject*, PyObject*>;
  314. static constexpr bool noarg = std::is_default_constructible_v<T>;
  315. template <typename = void>
  316. static PyObject* impl(PyTypeObject* type, PyObject* args, PyObject* kwargs) {
  317. struct FreeGuard {
  318. PyObject* self;
  319. PyTypeObject* type;
  320. ~FreeGuard() {
  321. if (self)
  322. type->tp_free(self);
  323. }
  324. };
  325. auto* self = type->tp_alloc(type, 0);
  326. FreeGuard free_guard{self, type};
  327. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  328. if constexpr (has_vectorcall && tp_vectorcall::valid) {
  329. reinterpret_cast<wrap_t*>(self)->vectorcall_slot =
  330. &tp_vectorcall::template impl<>;
  331. }
  332. try {
  333. if constexpr (varkw) {
  334. new (inst) T(args, kwargs);
  335. } else {
  336. new (inst) T();
  337. }
  338. }
  339. PYEXT17_TRANSLATE_EXC_RET(nullptr)
  340. free_guard.self = nullptr;
  341. return self;
  342. }
  343. static constexpr newfunc value = []() {
  344. if constexpr (provided)
  345. return T::tp_new;
  346. else if constexpr (varkw || noarg)
  347. return impl<>;
  348. else
  349. return nullptr;
  350. }();
  351. };
  352. struct tp_dealloc {
  353. static constexpr bool provided = HAS_MEMBER(T, tp_dealloc);
  354. template <typename = void>
  355. static void impl(PyObject* self) {
  356. reinterpret_cast<wrap_t*>(self)->inst()->~T();
  357. Py_TYPE(self)->tp_free(self);
  358. }
  359. static constexpr destructor value = []() {
  360. if constexpr (provided)
  361. return T::tp_dealloc;
  362. else
  363. return impl<>;
  364. }();
  365. };
  366. public:
  367. class TypeBuilder {
  368. std::vector<PyMethodDef> m_methods;
  369. std::vector<PyGetSetDef> m_getsets;
  370. PyTypeObject m_type;
  371. bool m_finalized = false;
  372. bool m_ready = false;
  373. void check_finalized() {
  374. if (m_finalized) {
  375. throw std::runtime_error("type is already finalized");
  376. }
  377. }
  378. static const char* to_c_str(const char* s) { return s; }
  379. template <size_t N, typename... Ts>
  380. static const char* to_c_str(const pybind11::detail::descr<N, Ts...>& desc) {
  381. return desc.text;
  382. }
  383. public:
  384. TypeBuilder(const TypeBuilder&) = delete;
  385. TypeBuilder& operator=(const TypeBuilder&) = delete;
  386. TypeBuilder() : m_type{PyVarObject_HEAD_INIT(nullptr, 0)} {
  387. constexpr auto has_tp_name = HAS_MEMBER(T, tp_name);
  388. if constexpr (has_tp_name) {
  389. m_type.tp_name = to_c_str(T::tp_name);
  390. }
  391. m_type.tp_dealloc = tp_dealloc::value;
  392. #ifdef _Py_TPFLAGS_HAVE_VECTORCALL
  393. m_type.tp_vectorcall_offset = tp_vectorcall::offset;
  394. #endif
  395. m_type.tp_call = tp_call::value;
  396. m_type.tp_basicsize = sizeof(wrap_t);
  397. m_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  398. #ifdef _Py_TPFLAGS_HAVE_VECTORCALL
  399. if constexpr (tp_vectorcall::valid) {
  400. m_type.tp_flags |= _Py_TPFLAGS_HAVE_VECTORCALL;
  401. }
  402. #endif
  403. m_type.tp_new = tp_new::value;
  404. }
  405. PyTypeObject* operator->() { return &m_type; }
  406. bool ready() const { return m_ready; }
  407. bool isinstance(PyObject* op) { return PyObject_TypeCheck(op, &m_type); }
  408. bool isexact(PyObject* op) { return Py_TYPE(op) == &m_type; }
  409. bool same_pytype(PyTypeObject* pt) { return pt == &m_type; }
  410. PyObject* finalize() {
  411. if (!m_finalized) {
  412. m_finalized = true;
  413. if (m_methods.size()) {
  414. m_methods.push_back({0});
  415. if (m_type.tp_methods) {
  416. PyErr_SetString(PyExc_SystemError, "tp_method is already set");
  417. return nullptr;
  418. }
  419. m_type.tp_methods = &m_methods[0];
  420. }
  421. if (m_getsets.size()) {
  422. m_getsets.push_back({0});
  423. if (m_type.tp_getset) {
  424. PyErr_SetString(PyExc_SystemError, "tp_getset is already set");
  425. return nullptr;
  426. }
  427. m_type.tp_getset = &m_getsets[0];
  428. }
  429. if (PyType_Ready(&m_type)) {
  430. return nullptr;
  431. }
  432. m_ready = true;
  433. }
  434. return (PyObject*)&m_type;
  435. }
  436. template <auto f>
  437. TypeBuilder& def(const char* name, const char* doc = nullptr) {
  438. check_finalized();
  439. m_methods.push_back(make_meth_def<f>(name, doc));
  440. return *this;
  441. }
  442. template <auto get, auto set = nullptr>
  443. TypeBuilder& def_getset(
  444. const char* name, const char* doc = nullptr, void* closure = nullptr) {
  445. check_finalized();
  446. m_getsets.push_back(make_getset_def<get, set>(name, doc, closure));
  447. return *this;
  448. }
  449. };
  450. static TypeBuilder& type() {
  451. static TypeBuilder type_helper;
  452. return type_helper;
  453. }
  454. template <typename... Args>
  455. static PyObject* cnew(Args&&... args) {
  456. auto* pytype = type().operator->();
  457. return cnew_with_type(pytype, std::forward<Args>(args)...);
  458. }
  459. template <typename... Args>
  460. static PyObject* cnew_with_type(PyTypeObject* pytype, Args&&... args) {
  461. auto* self = pytype->tp_alloc(pytype, 0);
  462. auto* inst = reinterpret_cast<wrap_t*>(self)->inst();
  463. if constexpr (has_vectorcall && tp_vectorcall::valid) {
  464. reinterpret_cast<wrap_t*>(self)->vectorcall_slot =
  465. &tp_vectorcall::template impl<>;
  466. }
  467. new (inst) T(std::forward<Args>(args)...);
  468. return self;
  469. }
  470. struct caster {
  471. static constexpr auto name = T::tp_name;
  472. T* value;
  473. bool load(pybind11::handle src, bool convert) {
  474. if (wrap_t::type().isinstance(src.ptr())) {
  475. value = reinterpret_cast<wrap_t*>(src.ptr())->inst();
  476. return true;
  477. }
  478. return false;
  479. }
  480. template <typename U>
  481. using cast_op_type = pybind11::detail::cast_op_type<U>;
  482. operator T*() { return value; }
  483. operator T&() { return *value; }
  484. };
  485. };
  486. } // namespace pyext17
  487. #undef HAS_MEMBER_TYPE
  488. #undef HAS_MEMBER
  489. #undef CVT_RET_PYOBJ
  490. #undef CVT_RET_INT