usertypes.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. #include <string>
  13. LUWRA_NS_BEGIN
  14. namespace internal {
  15. using UserTypeID = const void*;
  16. template <typename T>
  17. using StripUserType = std::remove_cv_t<T>;
  18. /**
  19. * User type identifier
  20. */
  21. template <typename T> extern
  22. const UserTypeID user_type_id = (void*) INTPTR_MAX;
  23. /**
  24. * Registry name for a metatable which is associated with a user type
  25. */
  26. template <typename T> extern
  27. const std::string user_type_reg_name =
  28. "UD#" + std::to_string(uintptr_t(&user_type_id<StripUserType<T>>));
  29. /**
  30. * Register a new metatable for a user type T.
  31. */
  32. template <typename U> static inline
  33. void new_user_type_metatable(State* state) {
  34. using T = StripUserType<U>;
  35. luaL_newmetatable(state, user_type_reg_name<T>.c_str());
  36. }
  37. /**
  38. * Check if the value at the given index if a user type T.
  39. */
  40. template <typename U> static inline
  41. StripUserType<U>* check_user_type(State* state, int index) {
  42. using T = StripUserType<U>;
  43. return static_cast<T*>(luaL_checkudata(state, index, user_type_reg_name<T>.c_str()));
  44. }
  45. /**
  46. * Apply U's metatable for the value at the top of the stack.
  47. */
  48. template <typename U> static inline
  49. void apply_user_type_meta_table(State* state) {
  50. using T = StripUserType<U>;
  51. luaL_getmetatable(state, user_type_reg_name<T>.c_str());
  52. lua_setmetatable(state, -2);
  53. }
  54. /**
  55. * Lua C function to construct a user type T with parameters A
  56. */
  57. template <typename U, typename... A> static inline
  58. int construct_user_type(State* state) {
  59. return direct<int(A...)>(
  60. state,
  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. read<T&>(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 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. namespace internal {
  180. template <typename T>
  181. struct UserTypeSignature {
  182. static_assert(
  183. sizeof(T) == -1,
  184. "Parameter to UserTypeSignature is not a valid signature"
  185. );
  186. };
  187. template <typename T, typename... A>
  188. struct UserTypeSignature<T(A...)> {
  189. using UserType = T;
  190. static inline
  191. void registerConstructor(State* state, const std::string& name) {
  192. setGlobal(state, name, &construct_user_type<UserType, A...>);
  193. }
  194. };
  195. }
  196. /**
  197. * Same as other `registerUserType` but registers the construtor as well. Also template parameter
  198. * is a signature `U(A...)` where `U` is the user type and `A...` its constructor parameters.
  199. */
  200. template <typename T> static inline
  201. void registerUserType(
  202. State* state,
  203. const std::string& ctor_name,
  204. const std::map<const char*, CFunction>& methods = std::map<const char*, CFunction>(),
  205. const std::map<const char*, CFunction>& meta_methods = std::map<const char*, CFunction>()
  206. ) {
  207. using U = typename internal::UserTypeSignature<T>::UserType;
  208. registerUserType<U>(state, methods, meta_methods);
  209. internal::UserTypeSignature<T>::registerConstructor(state, ctor_name);
  210. }
  211. LUWRA_NS_END
  212. /**
  213. * Generate a `lua_CFunction` wrapper for a constructor.
  214. * \param type Type to instantiate
  215. * \param ... Constructor parameter types
  216. * \return Wrapped function as `lua_CFunction`
  217. */
  218. #define LUWRA_WRAP_CONSTRUCTOR(type, ...) \
  219. (&luwra::internal::construct_user_type<luwra::internal::StripUserType<type>, __VA_ARGS__>)
  220. #define LUWRA_FIELD(type, name) {__STRING(name), LUWRA_WRAP_FIELD(type::name)}
  221. #define LUWRA_METHOD(type, name) {__STRING(name), LUWRA_WRAP_METHOD(type::name)}
  222. #define LUWRA_FUNCTION(type, name) {__STRING(name), LUWRA_WRAP_FUNCTION(type::name)}
  223. #endif