feat: C++ 阶段 2 - 虚函数/多态 (vtable)

- virtual 成员: 类结构加 __vtbl 指针字段
- 每类发射 vtable typedef (owner) + 实例, 含实现原型保证声明顺序
- 构造器设置 __vtbl = &Cls_vtbl_inst (含派生类 __base.__vtbl)
- 覆盖检测: virtual_impl 沿继承链找实现
- 指针变量: Cls* p = &d -> (struct Cls*)&d 转换
- p->virt() -> p->__vtbl->virt(p) 虚分发; 非虚 -> Cls_meth(p, ...)
- 函数签名: 类参数 Cls* / Cls 加 struct 前缀并注册为指针/值变量
- parse_class 重构: 先收集字段, 后发射 struct (vtbl 字段依赖解析结果)
这个提交包含在:
Paze AI
2026-08-16 18:19:18 +08:00
父节点 68d5bae1ee
当前提交 b112fa79e4
修改 2 个文件,包含 327 行新增31 行删除
二进制
查看文件
二进制文件未显示。
+307 -11
查看文件
@@ -35,6 +35,7 @@ static FILE *g_in, *g_out;
static Token g_tok;
static int g_line = 1;
static int g_ns_open = 0; /* namespace block depth (flattened) */
static int g_virt_pending = 0; /* next member decl is virtual */
/* ---------------- templates ---------------- */
@@ -55,11 +56,14 @@ static TplInst *g_tplinsts = NULL;
static int g_capture = 0; /* capture emit() into g_cap */
static char g_cap[4096];
static int g_cap_len;
static int g_emit_sect = 0; /* 0 = decl buffer, 1 = function buffer */
static int g_emit_sect = 0; /* 0 = decl buffer, 1 = fn buffer,
2 = class-fields buffer */
static char g_outbuf[8 * 1024 * 1024]; /* declarations (classes, globals) */
static int g_out_len;
static char g_fn_buf[8 * 1024 * 1024]; /* top-level function bodies */
static int g_fn_len;
static char g_fields[16384]; /* class data-member fields (per class) */
static int g_fields_len;
static char g_tpl_defs[16384]; /* instantiated definitions (flushed at end) */
static int g_tpl_defs_len;
@@ -70,6 +74,9 @@ typedef struct ClassInfo {
int has_dtor;
char members[64][64]; /* member names (data + functions) */
int n_members;
char virt[16][64]; /* virtual method names (own) */
char virt_ret[16][64]; /* virtual method return types */
int n_virt;
struct ClassInfo *next;
} ClassInfo;
static ClassInfo *g_classes = NULL;
@@ -167,6 +174,59 @@ static void add_class(const char *name)
g_classes = c;
}
/* is 'm' a virtual method of this class (own or inherited)? */
static int is_virtual(ClassInfo *ci, const char *m)
{
int i;
if (!ci) return 0;
for (i = 0; i < ci->n_virt; i++)
if (!strcmp(ci->virt[i], m)) return 1;
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b) return is_virtual(b, m);
}
return 0;
}
/* does this class need a vtable pointer (own or inherited virtuals)? */
static int class_has_vtbl(ClassInfo *ci)
{
if (!ci) return 0;
if (ci->n_virt > 0) return 1;
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b) return class_has_vtbl(b);
}
return 0;
}
/* topmost class whose vtable type is used (the one introducing virtuals) */
static const char *vtbl_owner(ClassInfo *ci)
{
if (!ci) return "";
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b && class_has_vtbl(b)) return vtbl_owner(b);
}
return ci->name;
}
/* which function implements virtual 'm' for class 'ci' */
static void virtual_impl(ClassInfo *ci, const char *m, char *out, int outsz)
{
int i;
for (i = 0; i < ci->n_members; i++)
if (!strcmp(ci->members[i], m)) {
snprintf(out, outsz, "%s_%s", ci->name, m);
return;
}
if (ci->base[0]) {
ClassInfo *b = find_class(ci->base);
if (b) { virtual_impl(b, m, out, outsz); return; }
}
snprintf(out, outsz, "%s_%s", ci->name, m);
}
/* ---------------- tokenizer ---------------- */
static Token g_peek; /* one-token lookahead buffer */
@@ -388,6 +448,12 @@ static void emit(const char *fmt, ...)
n = vsnprintf(g_cap + g_cap_len, sizeof g_cap - g_cap_len, fmt, ap);
if (n > 0) g_cap_len += n;
if (g_cap_len >= (int)sizeof g_cap - 1) g_cap_len = sizeof g_cap - 1;
} else if (g_emit_sect == 2) {
if (g_fields_len < (int)sizeof g_fields - 1) {
n = vsnprintf(g_fields + g_fields_len,
sizeof g_fields - g_fields_len, fmt, ap);
if (n > 0) g_fields_len += n;
}
} else if (g_emit_sect == 1) {
if (g_fn_len < (int)sizeof g_fn_buf - 1) {
n = vsnprintf(g_fn_buf + g_fn_len,
@@ -457,7 +523,8 @@ static void proto_emit(const char *fmt, ...)
static void gather_decl(char *type, int typesz, char *name, int namesz);
static int tpl_call_check(char varcls[][128], char varname[][64], int nvars);
static void parse_member_decl(const char *cls, const char *ret_type, const char *mname_in)
static void parse_member_decl(const char *cls, const char *ret_type,
const char *mname_in, int is_virtual)
{
char name[256];
char mname[256];
@@ -475,6 +542,20 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
if (is_ctor) ci->has_ctor = 1;
if (is_dtor) ci->has_dtor = 1;
else add_member(ci, name);
/* record virtual methods (name + trimmed return type) */
if (is_virtual && !is_ctor && !is_dtor && ci->n_virt < 16) {
char rtb[64];
int rl;
snprintf(rtb, sizeof rtb, "%s", ret_type);
rl = (int)strlen(rtb);
while (rl > 0 && (rtb[rl-1] == ' ' || rtb[rl-1] == '\t'))
rtb[--rl] = 0;
strncpy(ci->virt[ci->n_virt], name, 63);
ci->virt[ci->n_virt][63] = 0;
strncpy(ci->virt_ret[ci->n_virt], rtb[0] ? rtb : "int", 63);
ci->virt_ret[ci->n_virt][63] = 0;
ci->n_virt++;
}
}
/* only advance if g_tok is the name (not already consumed) */
if (!mname_in || !mname_in[0])
@@ -583,6 +664,12 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
if (bci && bci->has_ctor)
proto_emit(" %s_ctor(&this->__base);", mci->base);
}
/* ctor: set the vtable pointer */
if (is_ctor && class_has_vtbl(mci)) {
if (mci->base[0])
proto_emit(" this->__base.__vtbl = &%s_vtbl_inst;", cls);
proto_emit(" this->__vtbl = &%s_vtbl_inst;", cls);
}
{
int bd = 1;
int prev_dot = 0;
@@ -771,6 +858,21 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
}
}
/* emit a prototype for a mangled member function "Cls_method" */
static void emit_impl_proto(const char *mname, const char *ret)
{
const char *us = strrchr(mname, '_');
if (us) {
int n = (int)(us - mname);
if (n > 0 && n < 95) {
char cls[96];
memcpy(cls, mname, n);
cls[n] = 0;
emit(" %s %s(struct %s *this);\n", ret, mname, cls);
}
}
}
static void parse_class(void)
{
char clsname[128];
@@ -799,15 +901,23 @@ static void parse_class(void)
ci = find_class(clsname);
if (base[0]) strncpy(ci->base, base, sizeof ci->base - 1);
emit("struct %s {\n", clsname);
if (base[0])
emit(" struct %s __base;\n", base);
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) next_tok();
else { emit("};\n\n"); return; }
else { emit("struct %s;\n\n", clsname); return; }
/* phase 1: parse the class body; data members go to the fields
buffer (g_emit_sect=2), member functions go to g_protos. The
struct is emitted afterwards once virtuals are known. */
{
int was_sect = g_emit_sect;
g_emit_sect = 2;
g_fields_len = 0;
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}"))) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_VIRTUAL) {
g_virt_pending = 1;
next_tok();
continue;
}
if (g_tok.type == T_PUBLIC || g_tok.type == T_PRIVATE ||
g_tok.type == T_PROTECTED) {
next_tok();
@@ -822,7 +932,8 @@ static void parse_class(void)
gather_decl(tybuf, sizeof tybuf, tmp, sizeof tmp);
if (tmp[0] && g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
/* member function decl */
parse_member_decl(clsname, tybuf[0] ? tybuf : "int", tmp);
parse_member_decl(clsname, tybuf[0] ? tybuf : "int", tmp,
g_virt_pending);
} else if (tmp[0]) {
/* data member(s): type name [more] ; */
ClassInfo *cci = find_class(clsname);
@@ -861,10 +972,48 @@ static void parse_class(void)
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
}
}
g_virt_pending = 0;
}
g_emit_sect = was_sect;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";")) next_tok();
/* phase 2: emit the struct (vtbl field known once body is parsed) */
emit("struct %s {\n", clsname);
if (base[0])
emit(" struct %s __base;\n", base);
if (class_has_vtbl(ci))
emit(" struct %s_vtbl *__vtbl;\n", vtbl_owner(ci));
emit("%s", g_fields);
emit("};\n");
/* vtable: typedef once for the owner, instance per class */
if (ci->n_virt > 0) {
int i;
emit("typedef struct %s_vtbl {\n", ci->name);
for (i = 0; i < ci->n_virt; i++)
emit(" %s (*%s)(struct %s *this);\n",
ci->virt_ret[i], ci->virt[i], ci->name);
emit("} %s_vtbl;\n", ci->name);
}
if (class_has_vtbl(ci)) {
int i;
const char *owner = vtbl_owner(ci);
ClassInfo *oci = find_class(owner);
/* prototypes for the implementations so the initializer is valid */
for (i = 0; i < oci->n_virt; i++) {
char impl[64];
virtual_impl(ci, oci->virt[i], impl, sizeof impl);
emit_impl_proto(impl, oci->virt_ret[i]);
}
emit("static %s_vtbl %s_vtbl_inst = {\n", owner, clsname);
for (i = 0; i < oci->n_virt; i++) {
char impl[64];
virtual_impl(ci, oci->virt[i], impl, sizeof impl);
emit(" (%s(*)(struct %s*))%s,\n",
oci->virt_ret[i], owner, impl);
}
emit("};\n");
}
/* emit member function prototypes after the struct */
if (g_proto_len) {
emit("%s", g_protos);
@@ -1008,6 +1157,12 @@ static void parse_member_definition(const char *ret_type)
if (bci && bci->has_ctor)
emit(" %s_ctor(&this->__base);\n", ci->base);
}
/* ctor: set the vtable pointer */
if (is_ctor && class_has_vtbl(ci)) {
if (ci->base[0])
emit(" this->__base.__vtbl = &%s_vtbl_inst;\n", cls);
emit(" this->__vtbl = &%s_vtbl_inst;\n", cls);
}
{
int depth = 0;
ClassInfo *mci = find_class(cls);
@@ -1545,6 +1700,13 @@ static int tpl_call_check(char varcls[][128], char varname[][64], int nvars)
static void parse_function(const char *ret_type, const char *name)
{
char varcls[64][128]; /* variable name -> class name */
char varname[64][64];
int nvars = 0;
char ptrcls[64][128]; /* pointer variable name -> class name */
char ptrname[64][64];
int nptrs = 0;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) {
/* member function: class is 'name', method follows */
g_tok.type = T_IDENT;
@@ -1561,17 +1723,63 @@ static void parse_function(const char *ret_type, const char *name)
return;
}
/* class return type needs 'struct' prefix */
{
char rtbuf[160];
int rl;
snprintf(rtbuf, sizeof rtbuf, "%s", ret_type);
rl = (int)strlen(rtbuf);
while (rl > 0 && (rtbuf[rl-1] == ' ' || rtbuf[rl-1] == '\t'))
rtbuf[--rl] = 0;
if (find_class(rtbuf)) {
static char rtbuf2[192];
snprintf(rtbuf2, sizeof rtbuf2, "struct %s", rtbuf);
ret_type = rtbuf2;
}
}
emit("%s %s", ret_type, name);
/* signature: class types need 'struct'; class params are recorded */
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{"))) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
char clsname[128];
Token nxt;
strncpy(clsname, g_tok.text, sizeof clsname - 1);
clsname[sizeof clsname - 1] = 0;
read_tok(&nxt);
if (nxt.type == T_PUNCT && !strcmp(nxt.text, "*")) {
/* Cls* name -> struct Cls* name (pointer param) */
emit(" struct %s*", clsname);
next_tok(); /* g_tok = param name */
if (g_tok.type == T_IDENT && nptrs < 64) {
strncpy(ptrcls[nptrs], clsname, 127);
strncpy(ptrname[nptrs], g_tok.text, 63);
nptrs++;
emit(" %s", g_tok.text);
next_tok();
}
continue;
}
if (nxt.type == T_IDENT) {
/* Cls name -> struct Cls name (value param) */
emit(" struct %s %s", clsname, nxt.text);
if (nvars < 64) {
strncpy(varcls[nvars], clsname, 127);
strncpy(varname[nvars], nxt.text, 63);
nvars++;
}
next_tok();
continue;
}
emit(" struct %s", clsname);
unread_tok(&nxt);
}
emit_tok();
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
int depth = 0;
char varcls[64][128]; /* variable name -> class name */
char varname[64][64];
int nvars = 0;
emit(" {");
next_tok();
while (!(g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}") && depth == 0)) {
@@ -1602,6 +1810,35 @@ static void parse_function(const char *ret_type, const char *name)
clsname[sizeof clsname - 1] = 0;
emit(" struct %s", clsname);
next_tok();
/* pointer declaration: Cls* p = ... */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "*")) {
emit(" *");
next_tok();
if (g_tok.type == T_IDENT && nptrs < 64) {
strncpy(ptrcls[nptrs], clsname, 127);
strncpy(ptrname[nptrs], g_tok.text, 63);
nptrs++;
emit(" %s", g_tok.text);
next_tok();
/* init: = &var -> = (struct Cls *)&var */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "=")) {
Token amp, vv;
read_tok(&amp);
if (amp.type == T_PUNCT && !strcmp(amp.text, "&")) {
read_tok(&vv);
if (vv.type == T_IDENT) {
emit(" = (struct %s *)&%s",
clsname, vv.text);
next_tok();
continue;
}
unread_tok(&vv);
}
unread_tok(&amp);
}
}
continue;
}
/* next token(s): var [= ...] */
if (g_tok.type == T_IDENT && nvars < 64) {
char vname[64];
@@ -1838,6 +2075,65 @@ static void parse_function(const char *ret_type, const char *name)
}
}
}
/* pointer member call / virtual dispatch: p->method(args) */
if (g_tok.type == T_IDENT && nptrs > 0) {
int pi;
for (pi = 0; pi < nptrs; pi++) {
if (!strcmp(g_tok.text, ptrname[pi])) {
Token t1, t2;
read_tok(&t1);
if (t1.type == T_PUNCT && !strcmp(t1.text, "->")) {
read_tok(&t2);
if (t2.type == T_IDENT) {
Token t3;
read_tok(&t3);
if (t3.type == T_PUNCT &&
!strcmp(t3.text, "(")) {
char meth[128];
char mname[256];
ClassInfo *pci = find_class(ptrcls[pi]);
strncpy(meth, t2.text, sizeof meth - 1);
meth[sizeof meth - 1] = 0;
if (is_virtual(pci, meth)) {
/* dispatch through the vtable */
emit(" %s->__vtbl->%s(%s",
g_tok.text, meth, g_tok.text);
} else {
snprintf(mname, sizeof mname, "%s_%s",
ptrcls[pi], meth);
emit(" %s(%s", mname, g_tok.text);
}
next_tok(); /* -> first arg or ')' */
if (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")"))) {
emit(", ");
int adepth = 0;
while (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")") &&
adepth == 0)) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, "(")) adepth++;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) adepth--;
emit_tok();
next_tok();
}
}
emit(")");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
goto next_body_tok;
}
unread_tok(&t3);
}
unread_tok(&t2);
}
unread_tok(&t1);
break;
}
}
}
/* class name used as type -> add 'struct' */
if (g_tok.type == T_IDENT && find_class(g_tok.text)) {
emit(" struct %s", g_tok.text);