cast.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #pragma once
  2. #include "instance.h"
  3. namespace pkbind {
  4. template <typename T>
  5. constexpr inline bool is_string_v =
  6. std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
  7. /// The `type_caster` class is responsible for converting between Python objects and C++ objects.
  8. ///
  9. /// The static method `type_caster<T>::cast(...)` is used to convert a C++ object to a Python object.
  10. /// If the conversion fails, an exception is thrown.
  11. ///
  12. /// The method `type_caster<T>::load(...)` is used to try to convert a Python object to a C++ object.
  13. /// If the conversion is successful, it returns true, and you can then call `type_caster<T>::value()`
  14. /// to access the resulting C++ object. If the conversion fails, it returns false.
  15. ///
  16. /// NOTE: The type T could be a reference type or a pointer type. What is the lifetime of the reference or pointer?
  17. /// It depends on the referenced type. For some types, such as bool, int, float, etc., the loaded value is stored
  18. /// in the type_caster itself, so the lifetime of the reference is no longer than the lifetime of the type_caster
  19. /// object. For other user-registered types, the lifetime of the reference is the same as the corresponding Python
  20. /// object. A static variable `is_temporary_v` is used to indicate whether the loaded value is temporary or not.
  21. template <typename T, typename SFINAE>
  22. struct type_caster {
  23. T* data;
  24. static_assert(!std::is_pointer_v<T>, "type caster for pointer type must be specialized.");
  25. static_assert(!std::is_reference_v<T>, "type caster for reference type must be specialized.");
  26. template <typename U>
  27. static object cast(U&& value, return_value_policy policy, handle parent) {
  28. // TODO: support implicit cast
  29. return instance::create(type::of<T>(), std::forward<U>(value), parent, policy);
  30. }
  31. bool load(handle src, bool convert) {
  32. if(isinstance<T>(src)) {
  33. auto& i = *static_cast<instance*>(py_touserdata(src.ptr()));
  34. data = &i.as<T>();
  35. return true;
  36. }
  37. return false;
  38. }
  39. T& value() { return *data; }
  40. constexpr inline static bool is_temporary_v = false;
  41. };
  42. template <>
  43. struct type_caster<bool> {
  44. bool data;
  45. static object cast(bool src, return_value_policy, handle) { return bool_(src); }
  46. bool load(handle src, bool) {
  47. if(isinstance<pkbind::bool_>(src)) {
  48. data = py_tobool(src.ptr());
  49. return true;
  50. }
  51. return false;
  52. }
  53. bool& value() { return data; }
  54. constexpr inline static bool is_temporary_v = true;
  55. };
  56. template <typename T>
  57. struct type_caster<T, std::enable_if_t<is_integer_v<T>>> {
  58. T data;
  59. static object cast(T src, return_value_policy, handle) { return int_(src); }
  60. bool load(handle src, bool) {
  61. if(isinstance<int_>(src)) {
  62. data = static_cast<T>(py_toint(src.ptr()));
  63. return true;
  64. }
  65. return false;
  66. }
  67. T& value() { return data; }
  68. constexpr inline static bool is_temporary_v = true;
  69. };
  70. template <typename T>
  71. struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
  72. T data;
  73. static object cast(T src, return_value_policy, handle) { return float_(src); }
  74. bool load(handle src, bool convert) {
  75. if(isinstance<pkbind::float_>(src)) {
  76. data = static_cast<T>(py_tofloat(src.ptr()));
  77. return true;
  78. }
  79. if(convert && isinstance<pkbind::int_>(src)) {
  80. data = static_cast<T>(py_toint(src.ptr()));
  81. return true;
  82. }
  83. return false;
  84. }
  85. T& value() { return data; }
  86. constexpr inline static bool is_temporary_v = true;
  87. };
  88. template <typename T>
  89. struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
  90. T data;
  91. template <typename U>
  92. static object cast(U&& src, return_value_policy, handle) {
  93. return str(std::forward<U>(src));
  94. }
  95. bool load(handle src, bool) {
  96. if(isinstance<pkbind::str>(src)) {
  97. data = py_tostr(src.ptr());
  98. return true;
  99. }
  100. return false;
  101. }
  102. T& value() { return data; }
  103. constexpr inline static bool is_temporary_v = true;
  104. };
  105. template <typename T>
  106. struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
  107. T data;
  108. template <typename U>
  109. static object cast(U&& src, return_value_policy, handle) {
  110. return object(std::forward<U>(src));
  111. }
  112. bool load(handle src, bool) {
  113. if(isinstance<T>(src)) {
  114. data = T(src.ptr(), object::realloc_t{});
  115. return true;
  116. }
  117. return false;
  118. }
  119. T& value() { return data; }
  120. constexpr inline static bool is_temporary_v = true;
  121. };
  122. template <typename T>
  123. struct type_caster<T, std::enable_if_t<is_pointer_v<T> || std::is_reference_v<T>>> {
  124. using underlying =
  125. std::remove_cv_t<std::conditional_t<is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>>;
  126. type_caster<underlying> caster;
  127. template <typename U>
  128. static object cast(U&& value, return_value_policy policy, handle parent) {
  129. return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
  130. }
  131. bool load(handle src, bool convert) { return caster.load(src, convert); }
  132. T value() {
  133. if constexpr(std::is_pointer_v<T>) {
  134. return &caster.value();
  135. } else {
  136. return caster.value();
  137. }
  138. }
  139. constexpr inline static bool is_temporary_v = type_caster<underlying>::is_temporary_v;
  140. };
  141. } // namespace pkbind