/****************************************************************************** 版权所有 (C), 2018-2099, Radkil ****************************************************************************** 文 件 名 : pb_lua.c 版 本 号 : 初稿 功 能 描 述 : nanopb 消息的 Lua 桥接:table <-> C struct <-> protobuf 字节流 Lua 侧约定: - 二进制一律 Lua string(与 COM:read/write 直通) - 纯 table 表示消息;t.Value = 999 即改成员 - nil = 字段缺省(OPTIONAL 不出现在线上) - 枚举字段为整数值(与 .proto 枚举编号一致,无名字表) ******************************************************************************/ #ifdef BUILD_PROTOBUF #include #include #include #include #include "lua_base.h" #include "pb_lua.h" #include "pb_lua_desc.h" #include "pb.h" #include "pb_encode.h" #include "pb_decode.h" #include "bsp_CV.pb.h" /* 结构体/编码缓冲上限(当前最大消息 ~130B 结构 / 118B 编码,留足余量) */ #define PB_LUA_MAX_STRUCT 256 #define PB_LUA_MAX_ENCODED 256 /*====================================================================== * 消息注册表: 由 tools/gen_proto_ui.py --pb_lua_registry 生成 * pb_lua_registry.c 提供 g_pb_msgs / g_pb_msgs_count 强符号。 * 字段表内容仍走 Type_FIELDLIST X-macro(见 pb_lua_desc.h)。 *====================================================================*/ extern CV_struct_define g_stCV; /*====================================================================== * 命名全局绑定注册表(pb_ref 用) *====================================================================*/ #define PB_LUA_MAX_BINDINGS 8 typedef struct { const char *name; void *ptr; const pb_lua_msg_t *msg; } pb_lua_binding_t; static pb_lua_binding_t g_pb_bindings[PB_LUA_MAX_BINDINGS]; static size_t g_pb_nbindings; /*====================================================================== * 查找辅助 *====================================================================*/ static size_t pb_lua_strnlen(const char *s, size_t max) { size_t n = 0; while (n < max && s[n] != '\0') n++; return n; } static const pb_lua_msg_t *pb_lua_find_msg_or_null(const char *name) { for (int i = 0; i < g_pb_msgs_count; i++) { if (strcmp(g_pb_msgs[i]->name, name) == 0) return g_pb_msgs[i]; } return NULL; } static const pb_lua_msg_t *pb_lua_find_msg(lua_State *L, const char *name) { const pb_lua_msg_t *m = pb_lua_find_msg_or_null(name); if (m != NULL) return m; /* known 列表从注册表动态拼接(生成 registry 后零手改) */ char known[256]; size_t off = 0; known[0] = '\0'; for (int i = 0; i < g_pb_msgs_count && off + 32 < sizeof(known); i++) { int w = snprintf(known + off, sizeof(known) - off, "%s%s", (i ? ", " : ""), g_pb_msgs[i]->name); if (w <= 0) break; off += (size_t)w; } luaL_error(L, "unknown message type '%s' (known: %s)", name, known); return NULL; } int pb_lua_bind(const char *name, void *ptr, const char *msgtype) { const pb_lua_msg_t *m; if (name == NULL || ptr == NULL) return -1; m = pb_lua_find_msg_or_null(msgtype); if (m == NULL) return -1; for (size_t i = 0; i < g_pb_nbindings; i++) { if (strcmp(g_pb_bindings[i].name, name) == 0) { g_pb_bindings[i].ptr = ptr; g_pb_bindings[i].msg = m; return 0; } } if (g_pb_nbindings >= PB_LUA_MAX_BINDINGS) return -2; g_pb_bindings[g_pb_nbindings].name = name; g_pb_bindings[g_pb_nbindings].ptr = ptr; g_pb_bindings[g_pb_nbindings].msg = m; g_pb_nbindings++; return 0; } static const pb_lua_field_t *pb_lua_find_field(const pb_lua_msg_t *m, const char *name) { for (size_t i = 0; i < m->nfields; i++) { if (strcmp(m->fields[i].name, name) == 0) return &m->fields[i]; } return NULL; } /*====================================================================== * table → C struct(encode 方向) *====================================================================*/ static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg); static void pb_lua_set_one(lua_State *L, int vidx, const pb_lua_field_t *f, void *msg) { void *p = (char *)msg + f->offset; switch (f->ltype) { case PB_LUA_T_BOOL: { bool v; if (!lua_isboolean(L, vidx)) luaL_error(L, "field '%s': expected boolean", f->name); v = lua_toboolean(L, vidx) ? true : false; memcpy(p, &v, sizeof(v)); break; } case PB_LUA_T_INT32: { int32_t v; if (!lua_isinteger(L, vidx)) luaL_error(L, "field '%s': expected integer", f->name); lua_Integer li = lua_tointeger(L, vidx); if (li < (lua_Integer)INT32_MIN || li > (lua_Integer)INT32_MAX) luaL_error(L, "field '%s': integer out of int32 range", f->name); v = (int32_t)li; memcpy(p, &v, sizeof(v)); break; } case PB_LUA_T_INT64: { int64_t v; if (!lua_isinteger(L, vidx)) luaL_error(L, "field '%s': expected integer", f->name); v = (int64_t)lua_tointeger(L, vidx); memcpy(p, &v, sizeof(v)); break; } case PB_LUA_T_DOUBLE: { double v; if (!lua_isnumber(L, vidx)) luaL_error(L, "field '%s': expected number", f->name); v = (double)lua_tonumber(L, vidx); memcpy(p, &v, sizeof(v)); break; } case PB_LUA_T_STRING: { size_t len; const char *s; if (lua_type(L, vidx) != LUA_TSTRING) luaL_error(L, "field '%s': expected string", f->name); s = lua_tolstring(L, vidx, &len); if (memchr(s, 0, len) != NULL) luaL_error(L, "field '%s': string contains embedded NUL", f->name); if (len >= f->size) luaL_error(L, "field '%s': string too long (%u >= %u)", f->name, (unsigned)len, (unsigned)f->size); memset(p, 0, f->size); memcpy(p, s, len); break; } case PB_LUA_T_MESSAGE: { if (lua_type(L, vidx) != LUA_TTABLE) luaL_error(L, "field '%s': expected table", f->name); memset(p, 0, f->size); /* 赋值 = 整体替换子消息(live 路径无预清零) */ pb_lua_table_to_msg(L, vidx, (const pb_lua_msg_t *)f->aux, p); break; } default: luaL_error(L, "field '%s': unsupported protobuf type", f->name); break; } if (f->has_offset >= 0) { bool has = true; memcpy((char *)msg + f->has_offset, &has, sizeof(has)); } } static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg) { tidx = lua_absindex(L, tidx); lua_pushnil(L); while (lua_next(L, tidx) != 0) { if (lua_type(L, -2) != LUA_TSTRING) { luaL_error(L, "%s: field name must be a string", m->name); return; } const char *key = lua_tostring(L, -2); const pb_lua_field_t *f = pb_lua_find_field(m, key); if (f == NULL) { luaL_error(L, "unknown field '%s' in %s", key, m->name); return; } if (lua_isnil(L, -1)) { /* 理论上 lua_next 不返回 nil 值,防御处理:视为缺省 */ lua_pop(L, 1); continue; } pb_lua_set_one(L, -1, f, msg); lua_pop(L, 1); } } /*====================================================================== * C struct → table(decode 方向) *====================================================================*/ static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m); static void pb_lua_get_one(lua_State *L, const void *msg, const pb_lua_field_t *f) { const void *p = (const char *)msg + f->offset; switch (f->ltype) { case PB_LUA_T_BOOL: { bool v; memcpy(&v, p, sizeof(v)); lua_pushboolean(L, v); break; } case PB_LUA_T_INT32: { int32_t v; memcpy(&v, p, sizeof(v)); lua_pushinteger(L, (lua_Integer)v); break; } case PB_LUA_T_INT64: { int64_t v; memcpy(&v, p, sizeof(v)); lua_pushinteger(L, (lua_Integer)v); break; } case PB_LUA_T_DOUBLE: { double v; memcpy(&v, p, sizeof(v)); lua_pushnumber(L, (lua_Number)v); break; } case PB_LUA_T_STRING: { const char *s = (const char *)p; lua_pushlstring(L, s, pb_lua_strnlen(s, f->size > 0 ? f->size - 1 : 0)); break; } case PB_LUA_T_MESSAGE: { pb_lua_msg_to_table(L, p, (const pb_lua_msg_t *)f->aux); break; } default: lua_pushnil(L); break; } } /* OPTIONAL 且 has==false 的字段不进 table(nil = 缺省);SINGULAR 恒进 */ static int pb_lua_field_present(const void *msg, const pb_lua_field_t *f) { if (f->has_offset < 0) return 1; bool has; memcpy(&has, (const char *)msg + f->has_offset, sizeof(has)); return has ? 1 : 0; } static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m) { lua_createtable(L, (int)m->nfields, 0); for (size_t i = 0; i < m->nfields; i++) { const pb_lua_field_t *f = &m->fields[i]; if (!pb_lua_field_present(msg, f)) continue; pb_lua_get_one(L, msg, f); lua_setfield(L, -2, f->name); } } /*====================================================================== * dump:解析显示 *====================================================================*/ static void pb_lua_dump_indent(int depth) { for (int i = 0; i < depth; i++) lua_print(" "); } static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth); /* 递归打印 C 结构字段(不含外层花括号头) */ static void pb_lua_dump_msg_fields(const void *msg, const pb_lua_msg_t *m, int depth) { for (size_t i = 0; i < m->nfields; i++) { const pb_lua_field_t *f = &m->fields[i]; if (!pb_lua_field_present(msg, f)) continue; pb_lua_dump_indent(depth); if (f->ltype == PB_LUA_T_MESSAGE) { lua_print("%s {\n", f->name); pb_lua_dump_msg_fields((const char *)msg + f->offset, (const pb_lua_msg_t *)f->aux, depth + 1); pb_lua_dump_indent(depth); lua_print("}\n"); } else { lua_print("%s = ", f->name); const void *p = (const char *)msg + f->offset; switch (f->ltype) { case PB_LUA_T_BOOL: { bool v; memcpy(&v, p, sizeof(v)); lua_print("%s", v ? "true" : "false"); break; } case PB_LUA_T_INT32: { int32_t v; memcpy(&v, p, sizeof(v)); lua_print("%d", v); break; } case PB_LUA_T_INT64: { int64_t v; memcpy(&v, p, sizeof(v)); lua_print("%lld", (long long)v); break; } case PB_LUA_T_DOUBLE: { double v; memcpy(&v, p, sizeof(v)); print_number_fixed3(v); break; } case PB_LUA_T_STRING: { const char *s = (const char *)p; lua_print("\"%s\"", s); break; } default: lua_print("?"); break; } lua_print("\n"); } } } static void pb_lua_dump_msg(const void *msg, const pb_lua_msg_t *m, int depth) { pb_lua_dump_indent(depth); lua_print("%s {\n", m->name); pb_lua_dump_msg_fields(msg, m, depth + 1); pb_lua_dump_indent(depth); lua_print("}\n"); } /* 按 table 打印字段(不含外层花括号头,多余键标 [unknown] 置后) */ static void pb_lua_dump_table_fields(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth) { tidx = lua_absindex(L, tidx); for (size_t i = 0; i < m->nfields; i++) { const pb_lua_field_t *f = &m->fields[i]; lua_getfield(L, tidx, f->name); if (lua_isnil(L, -1)) { lua_pop(L, 1); continue; } pb_lua_dump_indent(depth); if (f->ltype == PB_LUA_T_MESSAGE) { lua_print("%s {\n", f->name); pb_lua_dump_table_fields(L, -1, (const pb_lua_msg_t *)f->aux, depth + 1); pb_lua_dump_indent(depth); lua_print("}\n"); lua_pop(L, 1); } else { lua_print("%s = ", f->name); pb_lua_dump_value(L, -1, f, depth + 1); lua_print("\n"); lua_pop(L, 1); } } /* 多余键 */ lua_pushnil(L); while (lua_next(L, tidx) != 0) { if (lua_type(L, -2) == LUA_TSTRING) { const char *key = lua_tostring(L, -2); if (pb_lua_find_field(m, key) == NULL) { pb_lua_dump_indent(depth); lua_print("%s = ", key); if (lua_type(L, -1) == LUA_TSTRING) lua_print("\"%s\"", lua_tostring(L, -1)); else if (lua_isinteger(L, -1)) lua_print("%d", lua_tointeger(L, -1)); else if (lua_isnumber(L, -1)) print_number_fixed3((double)lua_tonumber(L, -1)); else if (lua_isboolean(L, -1)) lua_print("%s", lua_toboolean(L, -1) ? "true" : "false"); else lua_print("%s", lua_typename(L, lua_type(L, -1))); lua_print(" [unknown]\n"); } } lua_pop(L, 1); } } static void pb_lua_dump_table(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth) { tidx = lua_absindex(L, tidx); pb_lua_dump_indent(depth); lua_print("%s {\n", m->name); pb_lua_dump_table_fields(L, tidx, m, depth + 1); pb_lua_dump_indent(depth); lua_print("}\n"); } static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth) { (void)depth; switch (f->ltype) { case PB_LUA_T_BOOL: lua_print("%s", lua_toboolean(L, vidx) ? "true" : "false"); break; case PB_LUA_T_INT32: case PB_LUA_T_INT64: if (lua_isinteger(L, vidx)) lua_print("%d", lua_tointeger(L, vidx)); else print_number_fixed3((double)lua_tonumber(L, vidx)); break; case PB_LUA_T_DOUBLE: print_number_fixed3((double)lua_tonumber(L, vidx)); break; case PB_LUA_T_STRING: lua_print("\"%s\"", lua_tostring(L, vidx)); break; default: lua_print("%s", lua_typename(L, lua_type(L, vidx))); break; } } static void pb_lua_dump_hex(const pb_byte_t *buf, size_t len) { size_t n = len > 64 ? 64 : len; lua_print("hex:"); for (size_t i = 0; i < n; i++) lua_print(" %02X", (unsigned)buf[i]); if (len > n) lua_print(" ..."); lua_print("\n"); } /*====================================================================== * live userdata(PB_LIVE):C 全局/结构体指针的可写视图 *====================================================================*/ typedef struct { void *ptr; /* 借用指针,不拥有内存(无 __gc) */ const pb_lua_msg_t *msg; } pb_lua_live_t; #define PB_LIVE_MT "PB_LIVE" static int l_live_index(lua_State *L); static int l_live_newindex(lua_State *L); static int l_live_tostring(lua_State *L); static int l_live_dump(lua_State *L); static int l_live_encode(lua_State *L); static int l_live_totable(lua_State *L); static void pb_lua_live_open(lua_State *L) { if (!luaL_newmetatable(L, PB_LIVE_MT)) { lua_pop(L, 1); return; } lua_pushcfunction(L, l_live_index); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, l_live_newindex); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, l_live_tostring); lua_setfield(L, -2, "__tostring"); lua_pushcfunction(L, l_live_dump); lua_setfield(L, -2, "dump"); lua_pushcfunction(L, l_live_encode); lua_setfield(L, -2, "encode"); lua_pushcfunction(L, l_live_totable); lua_setfield(L, -2, "totable"); lua_pop(L, 1); } static void pb_lua_push_live(lua_State *L, void *ptr, const pb_lua_msg_t *m) { pb_lua_live_t *u = (pb_lua_live_t *)lua_newuserdata(L, sizeof *u); u->ptr = ptr; u->msg = m; pb_lua_live_open(L); /* 确保 metatable 已注册(会 pop 掉) */ luaL_getmetatable(L, PB_LIVE_MT); /* 重新从 registry 取出 */ lua_setmetatable(L, -2); } /* 字段优先,方法 fallback;未知键报错(与 table 路径同文案) * MESSAGE 字段: Mutable 语义 — 始终返回嵌套 live 视图, 首次进入置 has_ * (否则 p.Sub.Field = x 在零初始化上必炸; 与 C++ MutableMessage 对齐)。 */ static int l_live_index(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); const char *key = luaL_checkstring(L, 2); const pb_lua_field_t *f = pb_lua_find_field(u->msg, key); if (f != NULL) { if (f->ltype == PB_LUA_T_MESSAGE) { /* 嵌套消息可导航; 首次进入置 has_, encode 才会带上 */ if (f->has_offset >= 0) { bool has; memcpy(&has, (char *)u->ptr + f->has_offset, sizeof(has)); if (!has) { has = true; memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has)); } } pb_lua_push_live(L, (char *)u->ptr + f->offset, (const pb_lua_msg_t *)f->aux); return 1; } if (!pb_lua_field_present(u->ptr, f)) { lua_pushnil(L); return 1; } pb_lua_get_one(L, u->ptr, f); return 1; } lua_getmetatable(L, 1); lua_getfield(L, -1, key); if (!lua_isnil(L, -1)) return 1; return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name); } /* nil → 清 OPTIONAL 的 has_(SINGULAR 报错);其余走 set_one(自动置 has_=true) */ static int l_live_newindex(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); const char *key = luaL_checkstring(L, 2); const pb_lua_field_t *f = pb_lua_find_field(u->msg, key); if (f == NULL) return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name); if (lua_isnil(L, 3)) { bool has = false; if (f->has_offset < 0) return luaL_error(L, "field '%s' in %s: SINGULAR field cannot be cleared", key, u->msg->name); memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has)); return 0; } pb_lua_set_one(L, 3, f, u->ptr); return 0; } static int l_live_tostring(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); lua_pushfstring(L, "PB_LIVE(%s): %p", u->msg->name, u->ptr); return 1; } static int l_live_dump(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); pb_lua_dump_msg(u->ptr, u->msg, 0); return 0; } /* 直接从 C 内存编码,不经 table 往返 */ static int l_live_encode(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); if (u->msg->c_size > PB_LUA_MAX_STRUCT || u->msg->max_encoded > PB_LUA_MAX_ENCODED) return luaL_error(L, "%s: message too large for static buffer", u->msg->name); pb_byte_t buf[PB_LUA_MAX_ENCODED]; pb_ostream_t os = pb_ostream_from_buffer(buf, u->msg->max_encoded); if (!pb_encode(&os, u->msg->msg, u->ptr)) return luaL_error(L, "%s encode failed: %s", u->msg->name, PB_GET_ERROR(&os)); lua_pushlstring(L, (const char *)buf, os.bytes_written); return 1; } static int l_live_totable(lua_State *L) { pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); pb_lua_msg_to_table(L, u->ptr, u->msg); return 1; } int l_pb_ref(lua_State *L) { const char *name = luaL_checkstring(L, 1); for (size_t i = 0; i < g_pb_nbindings; i++) { if (strcmp(g_pb_bindings[i].name, name) == 0) { pb_lua_push_live(L, g_pb_bindings[i].ptr, g_pb_bindings[i].msg); return 1; } } return luaL_error(L, "unknown pb global '%s'", name); } void pb_lua_open(lua_State *L) { pb_lua_live_open(L); if (pb_lua_bind("CV", &g_stCV, "CV_struct_define") != 0) luaL_error(L, "pb_lua_open: bind failed"); } /*====================================================================== * Lua 全局函数 *====================================================================*/ int l_pb_encode(lua_State *L) { const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); luaL_checktype(L, 2, LUA_TTABLE); if (m->c_size > PB_LUA_MAX_STRUCT || m->max_encoded > PB_LUA_MAX_ENCODED) return luaL_error(L, "%s: message too large for static buffer", m->name); char msg[PB_LUA_MAX_STRUCT]; memset(msg, 0, m->c_size); pb_lua_table_to_msg(L, 2, m, msg); pb_byte_t buf[PB_LUA_MAX_ENCODED]; pb_ostream_t os = pb_ostream_from_buffer(buf, m->max_encoded); if (!pb_encode(&os, m->msg, msg)) return luaL_error(L, "%s encode failed: %s", m->name, PB_GET_ERROR(&os)); lua_pushlstring(L, (const char *)buf, os.bytes_written); return 1; } int l_pb_decode(lua_State *L) { const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); size_t len; const pb_byte_t *data = (const pb_byte_t *)luaL_checklstring(L, 2, &len); if (m->c_size > PB_LUA_MAX_STRUCT) return luaL_error(L, "%s: message too large for static buffer", m->name); char msg[PB_LUA_MAX_STRUCT]; memset(msg, 0, m->c_size); pb_istream_t is = pb_istream_from_buffer(data, len); if (!pb_decode(&is, m->msg, msg)) return luaL_error(L, "%s decode failed: %s", m->name, PB_GET_ERROR(&is)); pb_lua_msg_to_table(L, msg, m); return 1; } int l_pb_dump(lua_State *L) { const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); { pb_lua_live_t *u = (pb_lua_live_t *)luaL_testudata(L, 2, PB_LIVE_MT); if (u != NULL) { if (u->msg != m) return luaL_error(L, "pb_dump: type mismatch: %s vs %s", m->name, u->msg->name); pb_lua_dump_msg(u->ptr, u->msg, 0); return 0; } } if (lua_type(L, 2) == LUA_TSTRING) { size_t len; const pb_byte_t *data = (const pb_byte_t *)lua_tolstring(L, 2, &len); if (m->c_size > PB_LUA_MAX_STRUCT) { lua_print("pb_dump: %s too large\n", m->name); return 0; } char msg[PB_LUA_MAX_STRUCT]; memset(msg, 0, m->c_size); pb_istream_t is = pb_istream_from_buffer(data, len); if (!pb_decode(&is, m->msg, msg)) { lua_print("pb_dump: %s decode failed: %s\n", m->name, PB_GET_ERROR(&is)); pb_lua_dump_hex(data, len); return 0; } pb_lua_dump_msg(msg, m, 0); } else if (lua_type(L, 2) == LUA_TTABLE) { pb_lua_dump_table(L, 2, m, 0); } else { return luaL_error(L, "pb_dump: expected binary string, table, or pb_ref userdata"); } return 0; } /* 全零模板:所有字段都置初值(嵌套消息递归展开),方便直接填值编码发送。 * 字段留在表里即视为 present;不想发到线上的字段设为 nil 即可。 */ static void pb_lua_new_table(lua_State *L, const pb_lua_msg_t *m) { lua_createtable(L, (int)m->nfields, 0); for (size_t i = 0; i < m->nfields; i++) { const pb_lua_field_t *f = &m->fields[i]; switch (f->ltype) { case PB_LUA_T_BOOL: lua_pushboolean(L, 0); break; case PB_LUA_T_INT32: case PB_LUA_T_INT64: lua_pushinteger(L, 0); break; case PB_LUA_T_DOUBLE: lua_pushnumber(L, 0); break; case PB_LUA_T_STRING: lua_pushstring(L, ""); break; case PB_LUA_T_MESSAGE: pb_lua_new_table(L, (const pb_lua_msg_t *)f->aux); break; default: lua_pushnil(L); break; } lua_setfield(L, -2, f->name); } } int l_pb_new(lua_State *L) { const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); pb_lua_new_table(L, m); return 1; } #endif