| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /* Luwra
- * Minimal-overhead Lua wrapper for C++
- *
- * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
- */
- #ifndef LUWRA_USERTYPES_H_
- #define LUWRA_USERTYPES_H_
- #include "common.hpp"
- #include "types.hpp"
- #include "stack.hpp"
- #include "functions.hpp"
- #include <map>
- LUWRA_NS_BEGIN
- namespace internal {
- using UserTypeID = const void*;
- template <typename T>
- using StripUserType = std::remove_cv_t<T>;
- /**
- * User type identifier
- */
- template <typename T> extern
- const UserTypeID user_type_id = (void*) INTPTR_MAX;
- /**
- * Registry name for a metatable which is associated with a user type
- */
- template <typename T> extern
- const std::string user_type_reg_name =
- "UD#" + std::to_string(uintptr_t(&user_type_id<StripUserType<T>>));
- /**
- * Register a new metatable for a user type T.
- */
- template <typename U> static inline
- void new_user_type_metatable(State* state) {
- using T = StripUserType<U>;
- luaL_newmetatable(state, user_type_reg_name<T>.c_str());
- }
- /**
- * Check if the value at the given index if a user type T.
- */
- template <typename U> static inline
- StripUserType<U>* check_user_type(State* state, int index) {
- using T = StripUserType<U>;
- return static_cast<T*>(luaL_checkudata(state, index, user_type_reg_name<T>.c_str()));
- }
- /**
- * Apply U's metatable for the value at the top of the stack.
- */
- template <typename U> static inline
- void apply_user_type_meta_table(State* state) {
- using T = StripUserType<U>;
- luaL_getmetatable(state, user_type_reg_name<T>.c_str());
- lua_setmetatable(state, -2);
- }
- /**
- * Lua C function to construct a user type T with parameters A
- */
- template <typename U, typename... A> static inline
- int construct_user_type(State* state) {
- return internal::Layout<int(A...)>::direct(
- state,
- 1,
- &Value<StripUserType<U>&>::template push<A...>,
- state
- );
- }
- /**
- * Lua C function to destruct a user type T
- */
- template <typename U> static inline
- int destruct_user_type(State* state) {
- using T = StripUserType<U>;
- if (!lua_islightuserdata(state, 1))
- Value<T&>::read(state, 1).~T();
- return 0;
- }
- /**
- * Create a string representation for user type T.
- */
- template <typename U> static
- int stringify_user_type(State* state) {
- using T = StripUserType<U>;
- return Value<std::string>::push(
- state,
- internal::user_type_reg_name<T>
- + "@"
- + std::to_string(uintptr_t(Value<T*>::read(state, 1)))
- );
- }
- /**
- * Helper struct for wrapping user type fields
- */
- template <typename U, typename R>
- struct FieldWrapper {
- using T = StripUserType<U>;
- template <R T::* field_pointer> static inline
- int invoke(State* state) {
- if (lua_gettop(state) > 1) {
- // Setter
- Value<T*>::read(state, 1)->*field_pointer = Value<R>::read(state, 2);
- return 0;
- } else {
- // Getter
- return push(state, Value<T*>::read(state, 1)->*field_pointer);
- }
- }
- };
- // 'const'-qualified fields
- template <typename U, typename R>
- struct FieldWrapper<U, const R> {
- using T = StripUserType<U>;
- template <const R T::* field_pointer> static inline
- int invoke(State* state) {
- return push(state, Value<T*>::read(state, 1)->*field_pointer);
- }
- };
- /**
- * Helper struct for wrapping user type methods
- */
- template <typename T, typename S>
- struct MethodWrapper {
- static_assert(
- sizeof(T) == -1,
- "Undefined template MethodWrapper"
- );
- };
- // 'const volatile'-qualified methods
- template <typename T, typename R, typename... A>
- struct MethodWrapper<const volatile T, R(A...)> {
- using MethodPointerType = R (T::*)(A...) const volatile;
- using FunctionSignature = R (const volatile T*, A...);
- template <MethodPointerType method_pointer> static inline
- R call(const volatile T* parent, A... args) {
- return (parent->*method_pointer)(std::forward<A>(args)...);
- }
- };
- // 'const'-qualified methods
- template <typename T, typename R, typename... A>
- struct MethodWrapper<const T, R(A...)> {
- using MethodPointerType = R (T::*)(A...) const;
- using FunctionSignature = R (const T*, A...);
- template <MethodPointerType method_pointer> static inline
- R call(const T* parent, A... args) {
- return (parent->*method_pointer)(std::forward<A>(args)...);
- }
- };
- // 'volatile'-qualified methods
- template <typename T, typename R, typename... A>
- struct MethodWrapper<volatile T, R(A...)> {
- using MethodPointerType = R (T::*)(A...) volatile;
- using FunctionSignature = R (volatile T*, A...);
- template <MethodPointerType method_pointer> static inline
- R call(volatile T* parent, A... args) {
- return (parent->*method_pointer)(std::forward<A>(args)...);
- }
- };
- // unqualified methods
- template <typename T, typename R, typename... A>
- struct MethodWrapper<T, R(A...)> {
- using MethodPointerType = R (T::*)(A...);
- using FunctionSignature = R (T*, A...);
- template <MethodPointerType method_pointer> static inline
- R call(T* parent, A... args) {
- return (parent->*method_pointer)(std::forward<A>(args)...);
- }
- };
- }
- /**
- * User type T.
- * Instances created using this specialization are allocated and constructed as full user data
- * types in Lua. The default garbage-collecting hook will destruct the user type, once it has
- * been marked.
- */
- template <typename U>
- struct Value<U&> {
- using T = internal::StripUserType<U>;
- static inline
- U& read(State* state, int n) {
- // T is unqualified, therefore conversion from T& to U& is allowed
- return *internal::check_user_type<T>(state, n);
- }
- template <typename... A> static inline
- int push(State* state, A&&... args) {
- void* mem = lua_newuserdata(state, sizeof(T));
- if (!mem) {
- luaL_error(state, "Failed to allocate user type");
- return -1;
- }
- // Construct
- new (mem) T(std::forward<A>(args)...);
- // Apply metatable for unqualified type T
- internal::apply_user_type_meta_table<T>(state);
- return 1;
- }
- };
- /**
- * User type T.
- * Instances created using this specialization are allocated as light user data in Lua.
- * The default garbage-collector does not destruct light user data types.
- */
- template <typename U>
- struct Value<U*> {
- using T = internal::StripUserType<U>;
- static inline
- U* read(State* state, int n) {
- // T is unqualified, therefore conversion from T* to U* is allowed
- return internal::check_user_type<T>(state, n);
- }
- static inline
- int push(State* state, T* instance) {
- if (instance == nullptr)
- return 0;
- // Push instance as light user data
- lua_pushlightuserdata(state, instance);
- // Apply metatable for unqualified type T
- internal::apply_user_type_meta_table<T>(state);
- return 1;
- }
- };
- /**
- * Constructor function for a type `T`. Variadic arguments must be used to specify which parameters
- * to use during construction.
- */
- template <typename T, typename... A>
- constexpr CFunction wrap_constructor =
- &internal::construct_user_type<internal::StripUserType<T>, A...>;
- /**
- * Works similiar to `wrap_function`. Given a class or struct declaration as follows:
- *
- * struct T {
- * R my_method(A0, A1 ... An);
- * };
- *
- * You might wrap this method easily:
- *
- * CFunction wrapped_meth = wrap_method<T, R(A0, A1 ... An), &T::my_method>;
- *
- * In Lua, assuming `instance` is a userdata instance of type `T`, x0, x1 ... xn are instances
- * of A0, A1 ... An, and the method has been bound as `my_method`; it is possible to invoke the
- * method like so:
- *
- * instance:my_method(x0, x1 ... xn)
- */
- template <
- typename T,
- typename S,
- typename internal::MethodWrapper<T, S>::MethodPointerType method_pointer
- >
- constexpr CFunction wrap_method =
- wrap_function<
- typename internal::MethodWrapper<T, S>::FunctionSignature,
- internal::MethodWrapper<T, S>::template call<method_pointer>
- >;
- /**
- * Property accessor method
- *
- * struct T {
- * R my_property;
- * };
- *
- * The wrapped property accessor is also a function:
- *
- * CFunction wrapped_property = wrap_field<T, R, &T::my_property>;
- */
- template <
- typename T,
- typename R,
- R T::* field_pointer
- >
- constexpr CFunction wrap_field =
- &internal::FieldWrapper<T, R>::template invoke<field_pointer>;
- /**
- * Register the metatable for user type `T`. This function allows you to register methods
- * which are shared across all instances of this type. A garbage-collector hook is also inserted.
- * Meta-methods can be added and/or overwritten aswell.
- */
- template <typename U> static inline
- void register_user_type(
- State* state,
- const std::map<const char*, CFunction>& methods,
- const std::map<const char*, CFunction>& meta_methods = std::map<const char*, CFunction>()
- ) {
- using T = internal::StripUserType<U>;
- // Setup an appropriate metatable name
- internal::new_user_type_metatable<T>(state);
- // Register methods
- if (methods.size() > 0 && meta_methods.count("__index") == 0) {
- push(state, "__index");
- lua_newtable(state);
- for (auto& method: methods) {
- push(state, method.first);
- push(state, method.second);
- lua_rawset(state, -3);
- }
- lua_rawset(state, -3);
- }
- // Register garbage-collection hook
- if (meta_methods.count("__gc") == 0) {
- push(state, "__gc");
- push(state, &internal::destruct_user_type<T>);
- lua_rawset(state, -3);
- }
- // Register string representation function
- if (meta_methods.count("__tostring") == 0) {
- push(state, "__tostring");
- push(state, &internal::stringify_user_type<T>);
- lua_rawset(state, -3);
- }
- // Insert meta methods
- for (const auto& metamethod: meta_methods) {
- push(state, metamethod.first);
- push(state, metamethod.second);
- lua_rawset(state, -3);
- }
- // Pop metatable off the stack
- lua_pop(state, -1);
- }
- LUWRA_NS_END
- #endif
|