]> PatchyGit - patchyircd.git/commitdiff
admin: c++fication for Client/Channel bindings
authorEgor <egor@patchysicp.xyz>
Wed, 4 Feb 2026 14:09:12 +0000 (16:09 +0200)
committerEgor <egor@patchysicp.xyz>
Wed, 4 Feb 2026 14:09:12 +0000 (16:09 +0200)
src/admin.cpp

index b415b9f..4b84c90 100644 (file)
@@ -14,6 +14,12 @@ static std::string_view checkstringview(lua_State *L, int idx) {
        return std::string_view(key, size);
 }
 
+template<typename T, int uv=0>
+void pushreftype(lua_State *L, auto&&... args) {
+       new (lua_newuserdatauv(L, sizeof(T), uv)) T(std::forward<decltype(args)>(args)...);
+       luaL_setmetatable(L, T::mtname);
+}
+
 template<typename T>
 struct MptrOwnerType {};
 template<typename T, typename U>
@@ -24,14 +30,23 @@ struct MptrOwnerType<U T::*> {
 template<auto P> // <int(T::* P)(lua_State *L)>
 int LuaForwarder(lua_State *L) {
        using T = MptrOwnerType<decltype(P)>::type;
-       return (static_cast<T*>(luaL_checkudata(L, 1, T::mtname()))->*P)(L);
+       return (static_cast<T*>(luaL_checkudata(L, 1, T::mtname))->*P)(L);
+}
+
+static void push(lua_State *L, std::string_view sv) {
+       lua_pushlstring(L, sv.data(), sv.size());
+}
+static void push(lua_State *L, const std::string &s) {
+       push(L, std::string_view(s));
 }
+static void push(lua_State *L, Client *cl);
+static void push(lua_State *L, Channel *chan);
 
 template<typename T>
 struct SetRef {
        map_base2<T> &s;
 
-       static consteval const char *mtname() {
+       static consteval const char *get_mtname() {
                if constexpr (std::same_as<T, std::string>) {
                        return "Set<std::string>";
                } else if constexpr (std::same_as<T, Client*>) {
@@ -42,23 +57,13 @@ struct SetRef {
                        static_assert(false);
                }
        };
-       static void pushval(lua_State *L, const T& v) requires std::same_as<T, std::string> {
-               lua_pushlstring(L, v.data(), v.size());
-       }
-       static void pushval(lua_State *L, const T& v) requires std::same_as<T, Client*> {
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = v;
-               luaL_setmetatable(L, "Client");
-       }
-       static void pushval(lua_State *L, const T& v) requires std::same_as<T, Channel*> {
-               *(Channel **)lua_newuserdatauv(L, sizeof(Channel *), 0) = v;
-               luaL_setmetatable(L, "Channel");
-       }
+       static constexpr const char *mtname = get_mtname();
 
        int index(lua_State *L) {
                std::string_view key = checkstringview(L, 2);
                if (!s.has(key))
                        return 0;
-               pushval(L, s.get(key));
+               push(L, s.get(key));
                return 1;
        }
        int newindex(lua_State *L) {
@@ -80,7 +85,7 @@ struct SetRef {
                return 0;
        }
        struct Iter {
-               static consteval const char *mtname() {
+               static consteval const char *get_mtname() {
                        if constexpr (std::same_as<T, std::string>) {
                                return "Set<std::string>::Iter";
                        } else if constexpr (std::same_as<T, Client*>) {
@@ -91,36 +96,34 @@ struct SetRef {
                                static_assert(false);
                        }
                };
+               static constexpr const char *mtname = get_mtname();
                size_t index = 0;
                int next(lua_State *L) {
                        lua_getiuservalue(L, 1, 1);
-                       SetRef &ref = *(SetRef *)lua_touserdata(L, -1);
+                       SetRef &ref = *static_cast<SetRef *>(lua_touserdata(L, -1));
                        auto &m = ref.s.m;
                        auto it = m.begin() + index;
                        if (it == m.end())
                                return 0;
                        index++;
-                       auto key = to_key(*it);
-                       lua_pushlstring(L, key.data(), key.size());
-                       pushval(L, *it);
+                       push(L, to_key(*it));
+                       push(L, *it);
                        return 2;
                }
        };
        int pairs(lua_State *L) {
                lua_pushcfunction(L, LuaForwarder<&Iter::next>);
-               new (lua_newuserdatauv(L, sizeof(Iter), 1)) Iter();
-               luaL_setmetatable(L, Iter::mtname());
+               pushreftype<Iter, 1>(L);
                lua_pushvalue(L, 1);
                lua_setiuservalue(L, -2, 1);
                lua_pushnil(L);
                return 3;
        }
-       static void push(lua_State *L, map_base2<T> *s) {
-               new (lua_newuserdatauv(L, sizeof(SetRef), 0)) SetRef(*s);
-               luaL_setmetatable(L, mtname());
+       static void _push(lua_State *L, map_base2<T> &s) {
+               pushreftype<SetRef>(L, s);
        }
        static void regmt(lua_State *L) {
-               if (luaL_newmetatable(L, mtname())) {
+               if (luaL_newmetatable(L, mtname)) {
                        luaL_Reg funcs[] = {
                                {"__index", LuaForwarder<&SetRef::index>},
                                {"__newindex", LuaForwarder<&SetRef::newindex>},
@@ -130,117 +133,145 @@ struct SetRef {
                        luaL_setfuncs(L, funcs, 0);
                }
                lua_pop(L, 1);
-               luaL_newmetatable(L, Iter::mtname());
+               luaL_newmetatable(L, Iter::mtname);
                lua_pop(L, 1);
        }
 };
 template<typename T>
-static void SetRef_push(lua_State *L, map_base2<T> *s) {
-       SetRef<T>::push(L, s);
+static void push(lua_State *L, map_base2<T> &s) {
+       SetRef<T>::_push(L, s);
 }
 
-static int client_index(lua_State *L)
-{
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
-       const char *key = luaL_checkstring(L, 2);
-       if (!cl)
-               return 0;
-#define string_getter(c, s) if (!strcmp(key, #s)) return lua_pushstring(L, c->s), 1
-#define integer_getter(c, s) if (!strcmp(key, #s)) return lua_pushinteger(L, c->s), 1
-#define SetRef_getter(c, s) if (!strcmp(key, #s)) return SetRef_push(L, &c->s), 1
-       integer_getter(cl, fd);
-       /* cctx */
-       if (!strcmp(key, "next")) {
-               if (!cl->next)
+struct ClientRef {
+       Client *cl;
+       static constexpr const char *mtname = "Client";
+       friend void push(lua_State *L, Client *cl) {
+               pushreftype<ClientRef>(L, cl);
+       }
+       static Client *check(lua_State *L, int idx) {
+               return static_cast<ClientRef *>(luaL_checkudata(L, idx, mtname))->cl;
+       }
+       int index(lua_State *L) {
+               auto key = checkstringview(L, 2);
+               if (!cl)
                        return 0;
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl->next;
-               luaL_setmetatable(L, "Client");
-               return 1;
+#define string_getter(c, s) if (key == #s##sv) return lua_pushstring(L, c->s), 1
+#define integer_getter(c, s) if (key == #s##sv) return lua_pushinteger(L, c->s), 1
+#define SetRef_getter(c, s) if (key == #s##sv) return push(L, c->s), 1
+               integer_getter(cl, fd);
+               /* cctx */
+               if (key == "next"sv) {
+                       if (!cl->next)
+                               return 0;
+                       return push(L, cl->next), 1;
+               }
+               string_getter(cl, nick);
+               string_getter(cl, key);
+               string_getter(cl, username);
+               string_getter(cl, realname);
+               string_getter(cl, hostname);
+               string_getter(cl, nuh);
+               string_getter(cl, away);
+               string_getter(cl, nick_reg);
+               /* string_getter(cl, pass_reg); */
+               string_getter(cl, peername);
+               integer_getter(cl, peerport);
+               /* saslbuf */
+               /* saslbufLen */
+               /* saslusername */
+               /* webircpeername */
+               /* webirchostname */
+               /* rbuf/rlen/wbuf/wlen */
+               integer_getter(cl, umode);
+               integer_getter(cl, regstate);
+               SetRef_getter(cl, channels);
+               SetRef_getter(cl, invites);
+               SetRef_getter(cl, monitor);
+               integer_getter(cl, cookie);
+               integer_getter(cl, signon);
+               integer_getter(cl, lastactivity);
+               integer_getter(cl, caps);
+               integer_getter(cl, want_write_for_read);
+               integer_getter(cl, want_read_for_write);
+               integer_getter(cl, want_write_for_write);
+               integer_getter(cl, want_read_for_close);
+               integer_getter(cl, want_write_for_close);
+               integer_getter(cl, dying);
+               integer_getter(cl, write_error);
+               integer_getter(cl, poll_reg);
+               /* saslexternal */
+               /* passwait */
+               /* webircwait */
+               return 0;
        }
-       string_getter(cl, nick);
-       string_getter(cl, key);
-       string_getter(cl, username);
-       string_getter(cl, realname);
-       string_getter(cl, hostname);
-       string_getter(cl, nuh);
-       string_getter(cl, away);
-       string_getter(cl, nick_reg);
-       /* string_getter(cl, pass_reg); */
-       string_getter(cl, peername);
-       integer_getter(cl, peerport);
-       /* saslbuf */
-       /* saslbufLen */
-       /* saslusername */
-       /* webircpeername */
-       /* webirchostname */
-       /* rbuf/rlen/wbuf/wlen */
-       integer_getter(cl, umode);
-       integer_getter(cl, regstate);
-       SetRef_getter(cl, channels);
-       SetRef_getter(cl, invites);
-       SetRef_getter(cl, monitor);
-       integer_getter(cl, cookie);
-       integer_getter(cl, signon);
-       integer_getter(cl, lastactivity);
-       integer_getter(cl, caps);
-       integer_getter(cl, want_write_for_read);
-       integer_getter(cl, want_read_for_write);
-       integer_getter(cl, want_write_for_write);
-       integer_getter(cl, want_read_for_close);
-       integer_getter(cl, want_write_for_close);
-       integer_getter(cl, dying);
-       integer_getter(cl, write_error);
-       integer_getter(cl, poll_reg);
-       /* saslexternal */
-       /* passwait */
-       /* webircwait */
-       return 0;
-}
-static int channel_index(lua_State *L)
-{
-       Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel");
-       const char *key = luaL_checkstring(L, 2);
-       if (!chan)
+       static void regmt(lua_State *L) {
+               if (luaL_newmetatable(L, mtname)) {
+                       luaL_Reg funcs[] = {
+                               {"__index", LuaForwarder<&ClientRef::index>},
+                               {0, 0},
+                       };
+                       luaL_setfuncs(L, funcs, 0);
+               }
+               lua_pop(L, 1);
+       }
+};
+struct ChannelRef {
+       Channel *chan;
+       static constexpr const char *mtname = "Client";
+       friend void push(lua_State *L, Channel *chan) {
+               pushreftype<ChannelRef>(L, chan);
+       }
+       static Channel *check(lua_State *L, int idx) {
+               return static_cast<ChannelRef *>(luaL_checkudata(L, idx, mtname))->chan;
+       }
+       int index(lua_State *L) {
+               auto key = checkstringview(L, 2);
+               if (!chan)
+                       return 0;
+               string_getter(chan, key);
+               string_getter(chan, name);
+               string_getter(chan, topic);
+               string_getter(chan, topic_who);
+               integer_getter(chan, topic_when);
+               integer_getter(chan, creation);
+               SetRef_getter(chan, members);
+               SetRef_getter(chan, o);
+               SetRef_getter(chan, v);
+               integer_getter(chan, member_count);
+               integer_getter(chan, dmode);
+               SetRef_getter(chan, listb);
+               SetRef_getter(chan, liste);
+               SetRef_getter(chan, listI);
+               SetRef_getter(chan, listq);
+               SetRef_getter(chan, listwo);
+               SetRef_getter(chan, listwv);
+               SetRef_getter(chan, invitees);
+               string_getter(chan, chankey);
+               integer_getter(chan, member_limit);
                return 0;
-       string_getter(chan, key);
-       string_getter(chan, name);
-       string_getter(chan, topic);
-       string_getter(chan, topic_who);
-       integer_getter(chan, topic_when);
-       integer_getter(chan, creation);
-       SetRef_getter(chan, members);
-       SetRef_getter(chan, o);
-       SetRef_getter(chan, v);
-       integer_getter(chan, member_count);
-       integer_getter(chan, dmode);
-       SetRef_getter(chan, listb);
-       SetRef_getter(chan, liste);
-       SetRef_getter(chan, listI);
-       SetRef_getter(chan, listq);
-       SetRef_getter(chan, listwo);
-       SetRef_getter(chan, listwv);
-       SetRef_getter(chan, invitees);
-       string_getter(chan, chankey);
-       integer_getter(chan, member_limit);
-       return 0;
-}
-static int globals_index(lua_State *L)
-{
-       const char *key = luaL_checkstring(L, 2);
-       if (!strcmp(key, "client_map"))
-               return SetRef_push(L, &client_map), 1;
-       if (!strcmp(key, "channel_map"))
-               return SetRef_push(L, &channel_map), 1;
-       if (!strcmp(key, "clients")) {
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = clients;
-               luaL_setmetatable(L, "Client");
-               return 1;
        }
-       if (!strcmp(key, "dyingclients")) {
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = dyingclients;
-               luaL_setmetatable(L, "Client");
-               return 1;
+       static void regmt(lua_State *L) {
+               if (luaL_newmetatable(L, mtname)) {
+                       luaL_Reg funcs[] = {
+                               {"__index", LuaForwarder<&ChannelRef::index>},
+                               {0, 0},
+                       };
+                       luaL_setfuncs(L, funcs, 0);
+               }
+               lua_pop(L, 1);
        }
+};
+static int globals_index(lua_State *L)
+{
+       auto key = checkstringview(L, 2);
+       if (key == "client_map"sv)
+               return push(L, client_map), 1;
+       if (key == "channel_map"sv)
+               return push(L, channel_map), 1;
+       if (key == "clients"sv)
+               return push(L, clients), 1;
+       if (key == "dyingclients"sv)
+               return push(L, dyingclients), 1;
        return 0;
 }
 static int command_eval(lua_State *L)
@@ -254,25 +285,25 @@ static int command_eval(lua_State *L)
 }
 static int fn_client_privmsg(lua_State *L)
 {
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
+       Client *cl = ClientRef::check(L, 1);
        const char *msg = luaL_checkstring(L, 2);
        msg_privmsg_server(luaL_optstring(L, 3, hostname), cl->nick, msg), sendbuf(cl);
        return 0;
 }
 static int fn_client_notice(lua_State *L)
 {
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
+       Client *cl = ClientRef::check(L, 1);
        const char *msg = luaL_checkstring(L, 2);
        msg_notice_server(luaL_optstring(L, 3, hostname), cl->nick, msg), sendbuf(cl);
        return 0;
 }
 static int fn_channel_join(lua_State *L)
 {
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
+       Client *cl = ClientRef::check(L, 1);
        const char *channel = 0;
        Channel *chan = 0;
        if (lua_isuserdata(L, 2)) {
-               chan = *(Channel **)luaL_checkudata(L, 2, "Channel");
+               chan = ChannelRef::check(L, 2);
                channel = chan->key;
        } else {
                channel = luaL_checkstring(L, 2);
@@ -283,12 +314,12 @@ static int fn_channel_join(lua_State *L)
 }
 static int fn_channel_part(lua_State *L)
 {
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
-       Channel *chan = *(Channel **)luaL_checkudata(L, 2, "Channel");
+       Client *cl = ClientRef::check(L, 1);
+       Channel *chan = ChannelRef::check(L, 2);
        const char *reason = luaL_optstring(L, 3, 0);
        Client *kicker = 0;
        if (lua_isuserdata(L, 4))
-               kicker = *(Client **)luaL_checkudata(L, 4, "Client");
+               kicker = ClientRef::check(L, 4);
        channel_part(cl, chan, reason, kicker);
        return 0;
 }
@@ -309,7 +340,7 @@ static int fn_channel_new(lua_State *L)
 }
 static int fn_channel_mode(lua_State *L)
 {
-       Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel");
+       Channel *chan = ChannelRef::check(L, 1);
        size_t as, bs;
        const char *a = luaL_checklstring(L, 2, &as);
        const char *b = luaL_optlstring(L, 3, 0, &bs);
@@ -324,7 +355,7 @@ static int fn_channel_mode(lua_State *L)
 }
 static int fn_channel_topic(lua_State *L)
 {
-       Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel");
+       Channel *chan = ChannelRef::check(L, 1);
        const char *topic = luaL_checkstring(L, 2);
        const char *who = luaL_checkstring(L, 3);
        time_t when = luaL_optinteger(L, 4, 0);
@@ -333,15 +364,15 @@ static int fn_channel_topic(lua_State *L)
 }
 static int fn_client_chghost(lua_State *L)
 {
-       Client *cl = *(Client **)luaL_checkudata(L, 1, "Client");
+       Client *cl = ClientRef::check(L, 1);
        const char *newusername = luaL_optstring(L, 2, 0);
        const char *newhostname = luaL_optstring(L, 3, 0);
        client_chghost(cl, newusername, newhostname);
        return 0;
 }
 static int fn_rpl_whoisspecial(lua_State *L) {
-       Client *cl1 = *(Client **)luaL_checkudata(L, 1, "Client");
-       Client *cl2 = *(Client **)luaL_checkudata(L, 2, "Client");
+       Client *cl1 = ClientRef::check(L, 1);
+       Client *cl2 = ClientRef::check(L, 2);
        const char *text = luaL_checkstring(L, 3);
        rpl_whoisspecial(cl1, cl2, "", text), sendbuf(cl1);
        return 0;
@@ -382,10 +413,7 @@ static int query(lua_State *L, db::Database &db) {
                                lua_pushinteger(L, stmt.num(i));
                        } else {
                                auto sv = stmt.text(i);
-                               if (sv.data())
-                                       lua_pushlstring(L, sv.data(), sv.size());
-                               else
-                                       lua_pushnil(L);
+                               sv.data() ? push(L, sv) : lua_pushnil(L);
                        }
                        lua_rawseti(L, -2, i+1);
                }
@@ -408,22 +436,8 @@ void admin_init(void)
        SetRef<std::string>::regmt(L);
        SetRef<Client *>::regmt(L);
        SetRef<Channel *>::regmt(L);
-       if (luaL_newmetatable(L, "Client")) {
-               luaL_Reg funcs[] = {
-                       {"__index", client_index},
-                       {0, 0},
-               };
-               luaL_setfuncs(L, funcs, 0);
-       }
-       lua_pop(L, 1);
-       if (luaL_newmetatable(L, "Channel")) {
-               luaL_Reg funcs[] = {
-                       {"__index", channel_index},
-                       {0, 0},
-               };
-               luaL_setfuncs(L, funcs, 0);
-       }
-       lua_pop(L, 1);
+       ClientRef::regmt(L);
+       ChannelRef::regmt(L);
        if (luaL_newmetatable(L, "globals")) {
                luaL_Reg funcs[] = {
                        {"__index", globals_index},
@@ -497,8 +511,7 @@ bool admin_command(Client *client, char *source, char *command, int argc, char *
                lua_pop(L, 1);
                return false;
        }
-       *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = client;
-       luaL_setmetatable(L, "Client");
+       push(L, client);
        lua_pushstring(L, source);
        lua_pushstring(L, command);
        lua_createtable(L, argc, 1);
@@ -528,28 +541,10 @@ int admin_hook(const char *hook, Client *cl1, Client *cl2, Channel *chan, char *
                return 0;
        }
        lua_pushstring(L, hook);
-       if (cl1) {
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl1;
-               luaL_setmetatable(L, "Client");
-       } else {
-               lua_pushnil(L);
-       }
-       if (cl2) {
-               *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl2;
-               luaL_setmetatable(L, "Client");
-       } else {
-               lua_pushnil(L);
-       }
-       if (chan) {
-               *(Channel **)lua_newuserdatauv(L, sizeof(Client *), 0) = chan;
-               luaL_setmetatable(L, "Channel");
-       } else {
-               lua_pushnil(L);
-       }
-       if (txt)
-               lua_pushstring(L, txt);
-       else
-               lua_pushnil(L);
+       cl1 ? push(L, cl1) : lua_pushnil(L);
+       cl2 ? push(L, cl2) : lua_pushnil(L);
+       chan ? push(L, chan) : lua_pushnil(L);
+       txt ? (void)lua_pushstring(L, txt) : lua_pushnil(L);
        if (LUA_OK != lua_pcall(L, 5, 1, 0)) {
                fprintf(stderr, "%s\n", lua_tostring(L, -1));
                lua_pop(L, 1);