usertypes.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /* Luwra
  2. * Minimal-overhead Lua wrapper for C++
  3. *
  4. * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
  5. */
  6. #ifndef LUWRA_USERTYPES_H_
  7. #define LUWRA_USERTYPES_H_
  8. #include "common.hpp"
  9. #include "types.hpp"
  10. #include "stack.hpp"
  11. #include <map>
  12. LUWRA_NS_BEGIN
  13. namespace internal {
  14. using UserTypeID = const void*;
  15. template <typename T>
  16. using StripUserType = std::remove_cv_t<T>;
  17. /**
  18. * User type identifier
  19. */
  20. template <typename T> extern
  21. const UserTypeID user_type_id = (void*) INTPTR_MAX;
  22. /**
  23. * Registry name for a metatable which is associated with a user type
  24. */
  25. template <typename T> extern
  26. const std::string user_type_reg_name =
  27. "UD#" + std::to_string(uintptr_t(&user_type_id<StripUserType<T>>));
  28. /**
  29. * Register a new metatable for a user type T.
  30. */
  31. template <typename U> static inline
  32. void new_user_type_metatable(State* state) {
  33. using T = StripUserType<U>;
  34. luaL_newmetatable(state, user_type_reg_name<T>.c_str());
  35. }
  36. /**
  37. * Check if the value at the given index if a user type T.
  38. */
  39. template <typename U> static inline
  40. StripUserType<U>* check_user_type(State* state, int index) {
  41. using T = StripUserType<U>;
  42. return static_cast<T*>(luaL_checkudata(state, index, user_type_reg_name<T>.c_str()));
  43. }
  44. /**
  45. * Apply U's metatable for the value at the top of the stack.
  46. */
  47. template <typename U> static inline
  48. void apply_user_type_meta_table(State* state) {
  49. using T = StripUserType<U>;
  50. luaL_getmetatable(state, user_type_reg_name<T>.c_str());
  51. lua_setmetatable(state, -2);
  52. }
  53. /**
  54. * Lua C function to construct a user type T with parameters A
  55. */
  56. template <typename U, typename... A> static inline
  57. int construct_user_type(State* state) {
  58. return internal::Layout<int(A...)>::direct(
  59. state,
  60. 1,
  61. &Value<StripUserType<U>&>::template push<A...>,
  62. state
  63. );
  64. }
  65. /**
  66. * Lua C function to destruct a user type T
  67. */
  68. template <typename U> static inline
  69. int destruct_user_type(State* state) {
  70. using T = StripUserType<U>;
  71. if (!lua_islightuserdata(state, 1))
  72. Value<T&>::read(state, 1).~T();
  73. return 0;
  74. }
  75. /**
  76. * Create a string representation for user type T.
  77. */
  78. template <typename U> static
  79. int stringify_user_type(State* state) {
  80. using T = StripUserType<U>;
  81. return Value<std::string>::push(
  82. state,
  83. internal::user_type_reg_name<T>
  84. + "@"
  85. + std::to_string(uintptr_t(Value<T*>::read(state, 1)))
  86. );
  87. }
  88. }
  89. /**
  90. * User type T.
  91. * Instances created using this specialization are allocated and constructed as full user data
  92. * types in Lua. The default garbage-collecting hook will destruct the user type, once it has
  93. * been marked.
  94. */
  95. template <typename U>
  96. struct Value<U&> {
  97. using T = internal::StripUserType<U>;
  98. static inline
  99. U& read(State* state, int n) {
  100. // T is unqualified, therefore conversion from T& to U& is allowed
  101. return *internal::check_user_type<T>(state, n);
  102. }
  103. template <typename... A> static inline
  104. int push(State* state, A&&... args) {
  105. void* mem = lua_newuserdata(state, sizeof(T));
  106. if (!mem) {
  107. luaL_error(state, "Failed to allocate user type");
  108. return -1;
  109. }
  110. // Construct
  111. new (mem) T {std::forward<A>(args)...};
  112. // Apply metatable for unqualified type T
  113. internal::apply_user_type_meta_table<T>(state);
  114. return 1;
  115. }
  116. };
  117. /**
  118. * User type T.
  119. * Instances created using this specialization are allocated as light user data in Lua.
  120. * The default garbage-collector does not destruct light user data types.
  121. */
  122. template <typename U>
  123. struct Value<U*> {
  124. using T = internal::StripUserType<U>;
  125. static inline
  126. U* read(State* state, int n) {
  127. // T is unqualified, therefore conversion from T* to U* is allowed
  128. return internal::check_user_type<T>(state, n);
  129. }
  130. static inline
  131. int push(State* state, T* instance) {
  132. if (instance == nullptr)
  133. return 0;
  134. // Push instance as light user data
  135. lua_pushlightuserdata(state, instance);
  136. // Apply metatable for unqualified type T
  137. internal::apply_user_type_meta_table<T>(state);
  138. return 1;
  139. }
  140. };
  141. /**
  142. * Register the metatable for user type `T`. This function allows you to register methods
  143. * which are shared across all instances of this type. A garbage-collector hook is also inserted.
  144. * Meta-methods can be added and/or overwritten aswell.
  145. */
  146. template <typename U> static inline
  147. void registerUserType(
  148. State* state,
  149. const std::map<const char*, CFunction>& methods = std::map<const char*, CFunction>(),
  150. const std::map<const char*, CFunction>& meta_methods = std::map<const char*, CFunction>()
  151. ) {
  152. using T = internal::StripUserType<U>;
  153. // Setup an appropriate metatable name
  154. internal::new_user_type_metatable<T>(state);
  155. // Register methods
  156. if (methods.size() > 0 && meta_methods.count("__index") == 0) {
  157. push(state, "__index");
  158. lua_newtable(state);
  159. for (auto& method: methods) {
  160. setFields(state, -1, method.first, method.second);
  161. }
  162. lua_rawset(state, -3);
  163. }
  164. // Register garbage-collection hook
  165. if (meta_methods.count("__gc") == 0) {
  166. setFields(state, -1, "__gc", &internal::destruct_user_type<T>);
  167. }
  168. // Register string representation function
  169. if (meta_methods.count("__tostring") == 0) {
  170. setFields(state, -1, "__tostring", &internal::stringify_user_type<T>);
  171. }
  172. // Insert meta methods
  173. for (const auto& metamethod: meta_methods) {
  174. setFields(state, -1, metamethod.first, metamethod.second);
  175. }
  176. // Pop metatable off the stack
  177. lua_pop(state, -1);
  178. }
  179. LUWRA_NS_END
  180. /**
  181. * Generate a `lua_CFunction` wrapper for a constructor.
  182. * \param type Type to instantiate
  183. * \param ... Constructor parameter types
  184. * \return Wrapped function as `lua_CFunction`
  185. */
  186. #define LUWRA_WRAP_CONSTRUCTOR(type, ...) \
  187. (&luwra::internal::construct_user_type<luwra::internal::StripUserType<type>, __VA_ARGS__>)
  188. #define LUWRA_FIELD(type, name) {__STRING(name), LUWRA_WRAP_FIELD(type::name)}
  189. #define LUWRA_METHOD(type, name) {__STRING(name), LUWRA_WRAP_METHOD(type::name)}
  190. #define LUWRA_FUNCTION(type, name) {__STRING(name), LUWRA_WRAP_FUNCTION(type::name)}
  191. #endif