#pragma once

#include <functional>
#include <vector>

#include "lua.hpp"

namespace sunny {
template <typename T>
class luna {
private:
    using member_func_t = std::function<int(lua_State*, T&)>;
    member_func_t func_;

public:
    static std::vector<member_func_t> methods_;

    static const char* class_name_;

    static void register_class(lua_State* L) {
        luaL_newmetatable(L, class_name_);
        int metatable = lua_gettop(L);
        lua_pushstring(L, "__index");
        lua_pushcfunction(L, &luna<T>::find);
        lua_settable(L, metatable);
    }

    static void push(lua_State* L, T* instance) {
        auto a = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
        *a = instance;

        luaL_getmetatable(L, class_name_);
        lua_setmetatable(L, -2);
    }

    static void add_member_func(lua_State* L, const char* name, member_func_t func) {
        methods_.push_back(func);
        int metatable = lua_gettop(L);
        lua_pushstring(L, name);
        lua_pushnumber(L, methods_.size() - 1);
        lua_settable(L, metatable);
    }

private:
    static int find(lua_State* L) {
        lua_getmetatable(L, 1);
        lua_pushvalue(L, 2);
        lua_rawget(L, -2);

        if (lua_isnumber(L, -1)) {
            int _index = lua_tonumber(L, -1);
            auto obj = static_cast<T**>(lua_touserdata(L, 1));
            lua_pushnumber(L, _index);
            lua_pushlightuserdata(L, obj);
            lua_pushcclosure(L, &luna<T>::call, 2);
            return 1;
        }

        return 1;
    }

    static int call(lua_State* L) {
        int i = (int)lua_tonumber(L, lua_upvalueindex(1));
        auto obj = static_cast<T**>(lua_touserdata(L, lua_upvalueindex(2)));
        return methods_[i](L, *(*obj));
    }
};

#define ADD_MEMBER_FUNC(CLASS_NAME, FUNC_NAME, LUA_FUNC_NAME)                                 \
    luna<CLASS_NAME>::add_member_func(state_, #FUNC_NAME, [](lua_State* L, CLASS_NAME& obj) { \
        LUA_FUNC_NAME(L, obj.FUNC_NAME());                                                    \
        return 1;                                                                             \
    })

#define ADD_MEMBER_FUNC_TO_INTEGER(CLASS_NAME, FUNC_NAME) ADD_MEMBER_FUNC(CLASS_NAME, FUNC_NAME, lua_pushinteger)

#define ADD_MEMBER_FUNC_TO_NUMBER(CLASS_NAME, FUNC_NAME) ADD_MEMBER_FUNC(CLASS_NAME, FUNC_NAME, lua_pushnumber)

#define ADD_MEMBER_FUNC_TO_STRING(CLASS_NAME, FUNC_NAME)                                      \
    luna<CLASS_NAME>::add_member_func(state_, #FUNC_NAME, [](lua_State* L, CLASS_NAME& obj) { \
        lua_pushstring(L, obj.FUNC_NAME().c_str());                                           \
        return 1;                                                                             \
    })

#define ADD_MEMBER_FUNC_TO_BOOLEAN(CLASS_NAME, FUNC_NAME) ADD_MEMBER_FUNC(CLASS_NAME, FUNC_NAME, lua_pushboolean)
}  // namespace sunny

Lunar参考链接

Luna参考链接