| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /* 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 CleanUserType =
- std::remove_pointer_t<std::remove_reference_t<std::remove_cv_t<T>>>;
- /**
- * User type identifier
- */
- template <typename T>
- UserTypeID user_type_id = (void*) INTPTR_MAX;
- /**
- * Registry name for a meta table which is associated with a user type
- */
- template <typename T>
- std::string user_type_reg_name =
- "UD#" + std::to_string(uintptr_t(&user_type_id<CleanUserType<T>>));
- /**
- * Register a new meta table for a user type T.
- */
- template <typename T> static inline
- void new_user_type_id(State* state) {
- luaL_newmetatable(state, user_type_reg_name<CleanUserType<T>>.c_str());
- // Use the address as user type identifier
- UserTypeID ut_id = lua_topointer(state, -1);
- user_type_id<CleanUserType<T>> = ut_id;
- }
- /**
- * Get the identifier for a user type at the given index.
- */
- static inline
- UserTypeID get_user_type_id(State* state, int index) {
- if (!lua_isuserdata(state, index))
- return nullptr;
- if (lua_getmetatable(state, index)) {
- UserTypeID type_id = lua_topointer(state, -1);
- lua_pop(state, 1);
- return type_id;
- } else {
- return nullptr;
- }
- }
- /**
- * Check if the value at the given index if a user type T.
- */
- template <typename T> static inline
- T* check_user_type(State* state, int index) {
- UserTypeID uid = get_user_type_id(state, index);
- if (uid == user_type_id<CleanUserType<T>>) {
- return static_cast<T*>(lua_touserdata(state, index));
- } else {
- std::string error_msg =
- "Expected user type " + std::to_string(uintptr_t(user_type_id<CleanUserType<T>>));
- luaL_argerror(state, index, error_msg.c_str());
- return nullptr;
- }
- }
- template <typename T> static inline
- void apply_user_type_meta_table(State* state) {
- luaL_getmetatable(state, user_type_reg_name<CleanUserType<T>>.c_str());
- lua_setmetatable(state, -2);
- }
- /**
- * Lua C function to construct a user type T with parameters A
- */
- template <typename T, typename... A> static inline
- int construct_user_type(State* state) {
- return internal::Layout<int(A...)>::direct(
- state,
- 1,
- &Value<T&>::template push<A...>,
- state
- );
- }
- /**
- * Lua C function to destruct a user type T
- */
- template <typename T> static inline
- int destruct_user_type(State* state) {
- if (!lua_islightuserdata(state, 1))
- Value<T&>::read(state, 1).~T();
- return 0;
- }
- /**
- * Create a string representation for user type T.
- */
- template <typename T> static
- std::string stringify_user_type(T& val) {
- return
- internal::user_type_reg_name<CleanUserType<T>>
- + "@"
- + std::to_string(uintptr_t(&val));
- }
- /**
- * Lua C function for a property accessor.
- */
- template <typename T, typename R, R T::* property_pointer> static inline
- int access_user_type_property(State* state) {
- if (lua_gettop(state) > 1) {
- // Setter
- (Value<T&>::read(state, 1).*property_pointer) = Value<R>::read(state, 2);
- return 0;
- } else {
- // Getter
- return push(state, Value<T&>::read(state, 1).*property_pointer);
- }
- }
- template <typename T, typename S>
- struct MethodWrapper {
- static_assert(
- sizeof(T) == -1,
- "The MethodWrapper template expects a type name and a function signature as parameter"
- );
- };
- template <typename T, typename R, typename... A>
- struct MethodWrapper<T, R(A...)> {
- using MethodPointerType = R (T::*)(A...);
- using FunctionSignature = R (T&, A...);
- /**
- * This function is a wrapped around the invocation of a given method.
- */
- 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 T>
- struct Value<T&> {
- static inline
- T& read(State* state, int n) {
- 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;
- }
- // Call constructor on instance
- new (mem) T(std::forward<A>(args)...);
- // Set metatable for this type
- 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 T>
- struct Value<T*> {
- static inline
- T* read(State* state, int n) {
- return internal::check_user_type<T>(state, n);
- }
- static inline
- int push(State* state, T* instance) {
- // push instance as light user data
- lua_pushlightuserdata(state, instance);
- // Set metatable for this type
- 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<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_property<T, R, &T::my_property>;
- */
- template <
- typename T,
- typename R,
- R T::* property_pointer
- >
- constexpr CFunction wrap_property =
- &internal::access_user_type_property<T, R, property_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 T> 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>()
- ) {
- // Setup an appropriate meta table name
- internal::new_user_type_id<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, wrap_function<std::string(T&), &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 meta table off the stack
- lua_pop(state, -1);
- }
- LUWRA_NS_END
- #endif
|